Spring的AOP创建代理的过程

AOP其实就是个代理模式。凡是代理,由于代码不可直接阅读,是bug的重灾区。
案例
某游戏系统,含负责点券充值的类CouponService,它含有一个充值方法deposit():
service

package com.javaedge.spring.aop.serviceimport org.springframework.stereotype.Service@Service
public class CouponService{
	public void deposit() throw Exception{
		System.out.println("Coupon depositing ... ");
		this.pay();
	}
	public void pay() throws Exception{
		System.out.println("Pay with WeChat Pay ...");
		//模拟pay()方法调用耗时
		Thread.sleep(1000;
	}
}

deposit()会使用微信支付充值。因此在Service层类中添加pay()方法,在deposit()方法体中调this.pay();
由于微信支付是第三方接口,需要记录接口调用时间。
引入 @Around 环绕增强 ,分别记录在pay()方法执行前后的时间,并计算pay()执行耗时。
config

package com.javaedge.spring.aop.config@Aspect
@Service
@Slf4j
public class AopConfig{

	@Around"execution(*com.javaedge.spring.aop.service.CouponService.pay())"public Object recordPayPerformance(ProceedingJoinPoint joinPoint) throws Throwable{
	Object result;
	long start =System.currentTimeMillis();
	result = joinPoint.proceed();
	long end = System.currentTimeMillis();
	sout("Pay method time cost (ms):"+(end - start);
	return result;
	}
}

Controller

package com.javaedge.spring.aop.controller@RestController
public class GameController{
	@Autowired
	CouponService couponService;

	@GetMapping(path="deposit")
	public void deposit() throws Exception{
		couponService.deposit();
	}
}

访问接口,会发现这段计算时间的切面并没有执行到,输出日志如下:在这里插入图片描述切面类明明定义了切面对应方法,但却没执行到。说明在类的内部,通过this调用的方法,不会被AOP增强。
解析
this对应的对象就是一个普通CouponService对象:
在这里插入图片描述而在Controller层中自动装配的CouponService对象:
在这里插入图片描述是个被Spring增强过的Bean,所以执行deposit()时,会执行记录接口调用时间的增强操作。而this对应的对象只是一个普通的对象,并无任何额外增强。

为什么this引用的对象只是一个普通对象?被Spring增强过的Bean是增强对象?
要从Spring AOP增强对象的过程来看。
实现
AOP的底层是动态代理,创建代理的方式有两种:
JDK方式
只能对实现了接口的类生成代理,不能针对普通类。
CGLIB方式
可以针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,来实现代理对象。
在这里插入图片描述针对非Spring Boot程序,除了添加相关AOP依赖项外,还会使用 @EnableAspectJAutoProxy 开启AOP功能。
这个注解类引入AspectJAutoProxyRegistrar,它通过实现ImportBeanDefinitionRegistrar接口完成AOP相关Bean准备工作。
现在来看下创建代理对象的过程。先来看下调用栈:
在这里插入图片描述创建代理对象的时机
创建一个Bean时
创建的关键工作由AnnotationAwareAspectJAutoProxyCreator完成
AnnotationAwareAspectJAutoProxyCreator
一种BeanPostProcessor。所以它的执行是在完成原始Bean构建后的初始化Bean(initializeBean)过程中
AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

关键方法wrapIfNecessary:在需要使用AOP时,它会把创建的原始Bean对象wrap成代理对象,作为Bean返回。
AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // 省略非关键代码
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }
   // 省略非关键代码 
}

createProxy
创建代理对象的关键:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
  // ...
  // 1. 创建一个代理工厂
  ProxyFactory proxyFactory = new ProxyFactory();
  if (!proxyFactory.isProxyTargetClass()) {
   if (shouldProxyTargetClass(beanClass, beanName)) {
      proxyFactory.setProxyTargetClass(true);
   }
   else {
      evaluateProxyInterfaces(beanClass, proxyFactory);
   }
  }
  Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  // 2. 将通知器(advisors)、被代理对象等信息加入到代理工厂
  proxyFactory.addAdvisors(advisors);
  proxyFactory.setTargetSource(targetSource);
  customizeProxyFactory(proxyFactory);
   // ...
   // 3. 通过代理工厂获取代理对象
  return proxyFactory.getProxy(getProxyClassLoader());
}

经过这样一个过程,一个代理对象就被创建出来了。我们从Spring中获取到的对象都是这个代理对象,所以具有AOP功能。而之前直接使用this引用到的只是一个普通对象,自然也就没办法实现AOP的功能了。

修正
只有引用的是被动态代理创建出来的对象,才会被Spring增强,具备AOP该有的功能。

什么样的对象具备这样条件?
被@Autowired注解
通过 @Autowired,在类的内部,自己引用自己:
在这里插入图片描述在这里插入图片描述直接从AopContext获取当前Proxy

AopContext,就是通过一个ThreadLocal来将Proxy和线程绑定起来,这样就可以随时拿出当前线程绑定的Proxy。

使用该方案有个前提,需要在 @EnableAspectJAutoProxy 加配置项 exposeProxy = true ,表示将代理对象放入到ThreadLocal,这才可以直接通过

AopContext.currentProxy()

获取到,否则报错:
在这里插入图片描述于是修改代码:
在这里插入图片描述勿忘修改EnableAspectJAutoProxy 的 exposeProxy属性:
在这里插入图片描述总结:
1.this是代替了本类的对象,并不能作为AOP中的增强。
2.@Autowired自动注入对象,具有AOP的增强功能。
3.AopContext.currentProxy()利用AopContext的当前代理创建的对象,可以具有AOP的增强功能,但是需要加入@EnableAspectJAutoProxy (exposeProxy=true) 注解在启动类上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_51297617

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值