当前位置:   article > 正文

Spring 之 AOP 原理详解_spring aop实现原理

spring aop实现原理

        Spring 是一个流行的 Java 企业应用程序开发框架。其中的 AOP(面向切面编程)是 Spring 框架中的一个核心概念。本文将介绍 Spring AOP 的底层实现原理,并通过源代码解析来详细阐述其实现过程。

什么是AOP?

         AOP是一种编程范式,用于在不修改原始代码的情况下向现有应用程序添加新功能。这种编程方式将应用程序分成许多独立的部分,称为切面。这些切面可以在应用程序的不同位置进行编写和维护,从而提高了应用程序的可重用性和可维护性

         AOP主要用于实现横切关注点(Cross-Cutting Concerns),例如日志记录、性能监测、事务管理等。通过AOP,我们可以将这些关注点与应用程序的其他部分分离开来,从而使应用程序更加模块化和易于维护。

实现原理

Spring AOP 的实现原理是基于动态代理字节码操作的。

        在编译时, Spring 会使用 AspectJ 编译器将切面代码编译成字节码文件。在运行时, Spring 会使用 Java 动态代理或 CGLIB 代理生成代理类,这些代理类会在目标对象方法执行前后插入切面代码,从而实现AOP的功能。

         Spring AOP 可以使用两种代理方式:JDK动态代理和 CGLIB 代理。如果目标对象实现了至少一个接口,则使用JDK动态代理;否则,使用 CGLIB 代理。下面分别介绍这两种代理方式的实现原理。

JDK动态代理

        JDK 动态代理是 Java 自带的动态代理实现方式。使用JDK动态代理时,需要目标对象实现至少一个接口。JDK 动态代理会在运行时生成一个实现了目标对象接口的代理类,该代理类会在目标对象方法执行前后插入切面代码。

下面是JDK动态代理的实现代码:

  1. public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
  2. private final AdvisedSupport advised;
  3. public JdkDynamicAopProxy(AdvisedSupport advised) {
  4. this.advised = advised;
  5. }
  6. @Override
  7. public Object getProxy() {
  8. return Proxy.newProxyInstance(
  9. getClass().getClassLoader(),
  10. advised.getTargetSource().getInterfaces(),
  11. this
  12. );
  13. }
  14. @Override
  15. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  16. MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
  17. MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
  18. advised.getTargetSource().getTarget(),
  19. method,
  20. args,
  21. methodInterceptor,
  22. advised.getTargetSource().getTargetClass()
  23. );
  24. return methodInvocation.proceed();
  25. }
  26. }
  27. 复制代码

        在该代码中,JdkDynamicAopProxy 类实现了 AopProxy 和 InvocationHandler 接口。getProxy 方法返回一个代理对象,该代理对象实现了目标对象实现的所有接口。invoke 方法用于执行代理方法,该方法会在目标对象方法执行前后插入切面代码。

CGLIB 代理

        CGLIB 代理是一个基于字节码操作的代理方式,它可以为没有实现接口的类创建代理对象。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。

下面是 CGLIB 代理的实现代码:

  1. public class CglibAopProxy implements AopProxy {
  2. private final AdvisedSupport advised;
  3. public CglibAopProxy(AdvisedSupport advised) {
  4. this.advised = advised;
  5. }
  6. @Override
  7. public Object getProxy() {
  8. Enhancer enhancer = new Enhancer();
  9. enhancer.setSuperclass(advised.getTargetSource().getTargetClass());
  10. enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
  11. return enhancer.create();
  12. }
  13. private static class DynamicAdvisedInterceptor implements MethodInterceptor {
  14. private final AdvisedSupport advised;
  15. public DynamicAdvisedInterceptor(AdvisedSupport advised) {
  16. this.advised = advised;
  17. }
  18. @Override
  19. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  20. MethodInvocation methodInvocation = new CglibMethodInvocation(
  21. advised.getTargetSource().getTarget(),
  22. method,
  23. args,
  24. proxy,
  25. advised.getMethodInterceptor(),
  26. advised.getTargetSource().getTargetClass()
  27. );
  28. return methodInvocation.proceed();
  29. }
  30. }
  31. }
  32. 复制代码

        在该代码中,CglibAopProxy 类实现了 AopProxy 接口。getProxy 方法返回一个代理对象,该代理对象是目标对象的子类,并覆盖了其中的方法。DynamicAdvisedInterceptor 类实现了 MethodInterceptor 接口,用于在目标对象方法执行前后插入切面代码。

AOP使用示例

        在了解了 Spring AOP的实现原理后,我们来看一下 Spring AOP的源码实现。Spring AOP的源码位于org.Springframework.aop包下,其中涉及到的类有:

Advised:一个包含切面信息的接口,用于描述切面的配置信息。

AdvisedSupport:Advised接口的默认实现类,包含了切面的配置信息。

AopProxy:AOP代理的接口,用于获取代理对象。

CglibAopProxy: CGLIB 代理的实现类。

JdkDynamicAopProxy:JDK动态代理的实现类。

MethodInvocation:方法调用的接口,用于封装目标对象方法的调用过程。

ReflectiveMethodInvocation:MethodInvocation接口的默认实现类,用于调用目标对象方法。

MethodInterceptor:方法拦截器的接口,用于实现切面的具体逻辑。

ProxyFactory:代理工厂,用于创建代理对象。

下面是一个使用 Spring AOP的示例代码:

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Override
  4. @LogAnnotation
  5. public void addUser(String username, String password) {
  6. System.out.println("addUser");
  7. }
  8. }
  9. 复制代码

在该代码中,UserServiceImpl 类实现了 UserService 接口,并在 addUser 方法上添加了@LogAnnotation 注解。@LogAnnotation 注解是一个自定义注解,用于标记需要记录日志的方法。

下面是 @LogAnnotation 注解的定义:

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface LogAnnotation {
  4. }
  5. 复制代码

在使用 Spring AOP时,我们需要定义一个切面类,用于实现@LogAnnotation注解的具体逻辑。下面是一个定义了@LogAnnotation注解的切面类:

  1. @Aspect
  2. @Component
  3. public class LogAspect {
  4. @Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
  5. public void logPointcut() {}
  6. @Before("logPointcut()")
  7. public void before(JoinPoint joinPoint) {
  8. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  9. String methodName = signature.getName();
  10. System.out.println("before " + methodName);
  11. }
  12. @After("logPointcut()")
  13. public void after(JoinPoint joinPoint) {
  14. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  15. String methodName = signature.getName();
  16. System.out.println("after " + methodName);
  17. }
  18. }
  19. 复制代码

        在该代码中,LogAspect 类使用 @Aspect 注解标记为切面类,并使用 @Component 注解将其注册为 Spring 的 Bean。

logPointcut 方法使用 @Pointcut 注解定义了切点,该切点匹配所有标记了@LogAnnotation 注解的方法。

before 方法和 after 方法分别使用 @Before 和 @After 注解定义了前置通知和后置通知。在 before 方法和 after 方法中,我们可以编写具体的日志记录逻辑。

下面是创建代理对象的代码:

  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. UserServiceImpl userService = applicationContext.getBean(UserServiceImpl.class);
  3. userService.addUser("test", "test");
  4. 复制代码

在该代码中,我们使用 ApplicationContext 从配置文件中获取了 UserServiceImpl 的 Bean,并调用了 addUser 方法。由于 addUser 方法标记了 @LogAnnotation 注解,因此 Spring 会自动将 LogAspect 类中定义的切面逻辑插入到该方法中。

总结

本文介绍了 Spring AOP 的实现原理,并通过源代码解析详细阐述了其实现过程。在使用 Spring AOP时,我们需要定义切面类,并为需要实现AOP的方法添加注解。 Spring 会自动将切面逻辑插入到这些方法中,从而实现AOP的功能。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/647786
推荐阅读
相关标签
  

闽ICP备14008679号