SpringAOP源码分析一

文章介绍了SpringAOP的开发步骤,包括原始对象、切面(Advisor或Aspect)、切入点的概念,并通过代码示例展示了如何定义和使用切面。同时,讨论了@EnableAspectJAutoProxy注解在动态代理创建中的作用,以及JDK和Cglib两种动态代理的区别。在遇到内部方法调用时,通过从ThreadLocal或BeanFactory获取代理对象来确保AOP功能的正确执行。
摘要由CSDN通过智能技术生成

Spring AOP开发步骤

  1. 原始对象(目标对象)
  2. 额外功能:为原始方法的不同运行时机添加额外功能。
  3. 切入点:额外功能增加给哪些原始方法,是由切入点决定的。
  4. 切面:
    1. 在Spring体系,切面称为Advisor
    2. 在AspectJ注解形式开发AOP,切面称为Aspect

示例代码

原始对象:

public interface UserService {
    void printAge();
    void printName();
}

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void printAge() {
        System.out.println("UserServiceImpl.printAge");
    }
    @Override
    public void printName() {
        System.out.println("UserServiceImpl.printName");
    }
}

切面:

切面是由切入点额外功能组装在一起的。

//@Aspect注解表示这是一个切面
@Aspect
public class MyAspect {

    //切入点,表示要给哪些方法加入额外功能
    @Pointcut("execution(* edu.hzb.aop.UserService.*(..))")
    public void pointCut(){};

    /**
    下面四个方法都是额外功能
    */
    //在原始方法执行之前回调该方法
    @Before("pointCut()")
    public void myBefore() {
        System.out.println("MyAspect.MyBefore");
    }
	//在原始方法执行之后回调该方法
    @After("pointCut()")
    public void myAfter() {
        System.out.println("MyAspect.MyAfter");
    }

     //原始方法返回值时回调该方法
//    @AfterReturning(value = "pointCut()")
//    public void myAfterReturning() {
//        System.out.println("MyAspect.myAfterReturning");
//    }
//
      //原始方法抛出异常时回调该方法
//    @AfterThrowing(value = "pointCut()")
//    public void myAfterThrowing() {
//        System.out.println("MyAspect.myAfterThrowing");
//    }
}

配置类:

@Configuration
@ComponentScan("edu.hzb.aop")
//proxyTargetClass=true,表示采用Cglib的方式创建代理
//exposeProxy=true,表示将对象的代理对象存放在ThreadLocal中
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class AppConfig {
}

上述是Spring AOP开发编码的流程,创建代理对象的逻辑就是由@EnableAspectJAutoProxy注解完成。

分析@EnableAspectJAutoProxy属性

首先看@EnableAspectJAutoProxy注解的两个属性

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

   /**
   proxyTargetClass属性默认为false,表示采用JDK动态代理的方式创建代理对象
   proxyTargetClass属性为true,表示采用Cglib动态代理的方式创建代理对象
   */
   boolean proxyTargetClass() default false;

   /**
   exposeProxy直译是暴露代理,exposeProxy=true时会把对象的代理对象存放到ThreadLocal中,
   通过AopContext.currentProxy()拿到代理对象
   */
   boolean exposeProxy() default false;
}
Spring动态代理创建方式有两种,一种是JDK动态代理;一种是Cglib动态代理。动态代理其实就是通过动态字节码技术,在程序运行的过程中创建代理对象,既保留原始功能,又保留额外功能。
JDK动态代理:原始类通过实现接口的方式。
Cglib动态代理:以继承的方式,创建原始类的子类

以一个问题来举例暴露代理的用途,还是上面的代码,将原始对象UserServiceImpl改成如下:

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void printAge() {
        System.out.println("UserServiceImpl.printAge");
        //在printAge()方法中调用printName()方法
        printName();
    }
    @Override
    public void printName() {
        System.out.println("UserServiceImpl.printName");
    }
}

测试代码:

public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    UserService userService = (UserService) context.getBean("userService");
    userService.printAge();
}

运行结果:

MyAspect.MyBefore
UserServiceImpl.printAge
UserServiceImpl.printName
MyAspect.MyAfter

程序运行结果显式先执行了额外功能myBefore()方法,然后执行了printAge()方法,然后内部执行了printName()方法,然后执行了额外功能myAfter()方法。根据切入点表达式pointCut可以知道printName()方法执行前后也是要执行额外功能的,但是并没有执行。原因是因为printAge()方法中是由当前对象(原始对象)this来调用printName()方法的,所以在printAge()方法内部调用printName()方法时,也是需要获取UserServiceImpl的代理对象来执行。

解决办法:

1、从工厂中获取代理对象:

通过实现BeanFactoryAwareApplicationContextAware接口,可以将工厂注入到UserServiceImpl中,通过工厂来获取UserServiceImpl的代理对象。

@Service("userService")
public class UserServiceImpl implements UserService, ApplicationContextAware {

    private BeanFactory beanFactory;
    @Override
    public void printAge() {
        System.out.println("UserServiceImpl.printAge");
		//通过获取UserServiceImpl的代理对象来执行printName()方法
        UserService userService = (UserService) beanFactory.getBean("userService");
        userService.printName();
    }

    @Override
    public void printName() {
        System.out.println("UserServiceImpl.printName");
    }
    //以set注入的方式注入工厂
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanFactory = applicationContext;
    }
}

运行结果:

在执行printAge()和printName()方法前后都执行了额外功能myBefore()和myAfter()。

MyAspect.MyBefore
UserServiceImpl.printAge
MyAspect.MyBefore
UserServiceImpl.printName
MyAspect.MyAfter
MyAspect.MyAfter

2、从ThreadLocal中获取代理对象

exposeProxy=true时会把对象的代理对象存放到ThreadLocal中, 通过AopContext.currentProxy()拿到代理对象,在上面的配置类AppConfig.java中已经将exposeProxy置为true了。

@Service("userService")
public class UserServiceImpl implements UserService{

    @Override
    public void printAge() {
        System.out.println("UserServiceImpl.printAge");
        //从ThreadLocal中获取代理对象
        UserService userService = (UserService) AopContext.currentProxy();
        userService.printName();
    }

    @Override
    public void printName() {
        System.out.println("UserServiceImpl.printName");
    }

}

通过JDK动态代理或Cglib动态代理创建代理对象时,如果exposeProxy=true,会把代理对象存放到ThreadLocal中。

/**
AOPContext类的源代码
*/
public final class AopContext {
    //存放代理对象
   private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");

   private AopContext() {
   }

   public static Object currentProxy() throws IllegalStateException {
      Object proxy = currentProxy.get();
      if (proxy == null) {
         throw new IllegalStateException(
               "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
      }
      return proxy;
   }

 
   @Nullable
   static Object setCurrentProxy(@Nullable Object proxy) {
      Object old = currentProxy.get();
      if (proxy != null) {
         currentProxy.set(proxy);
      }
      else {
         currentProxy.remove();
      }
      return old;
   }

}

JdkDynamicAopProxy.java文件中的invoke方法:

if (this.advised.exposeProxy) {
   // Make invocation available if necessary.
   oldProxy = AopContext.setCurrentProxy(proxy);
   setProxyContext = true;
}

CglibAopProxy.java文件的内部类DynamicAdvisedInterceptorintercept()方法

if (this.advised.exposeProxy) {
   // Make invocation available if necessary.
   oldProxy = AopContext.setCurrentProxy(proxy);
   setProxyContext = true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值