Spring AOP源码解读(@Aspect注解)

Spring AOP的介绍:

什么是Spring AOP:
面向切面编程,基于动态代理实现的,如果动态代理不了解的,可以翻阅我其它的博客;也就是在执行方法前、方法后和出现异常后做拦截或者做增强处理,我们常用的使用方式就是@AspectJ注解、自己可以使用spring后置处理器自己来实现或者通过实现接口的方式、XML配置的方式;

AOP与AspectJ的关系:
Aspect属于Eclipse基金会,增强的方式是静态织入,它是通过修改代码来实现的,要使用它还要使用它特定的编译器;
SringAO和AspectJ没有什么关系,只是使用了AspectJ的一些概念,用到了其切点解析和匹配。

Spring Aop的发展:
Spring 3.2 以后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这样在使用CGLB代理是不需要显示的引入这两个依赖。

  • Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的,想看源码的同学可以从这里起步。
  • Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
  • Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫 做@AspectJ,但是这个和 AspectJ 其实没啥关系。

先来介绍Spring 1.2中基于接口的Aop ,直接上代码,不做介绍了,因为太简单了。
定义advice和intercepter:

public class MyLogAdvice implements MethodBeforeAdvice {
   
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
   
		//Object invoke = method.invoke(target, args);
		String name = method.getName();
		System.out.println("执行目标方法:"+name+"的前置通知,入参为:"+ Arrays.asList(args));
	}
}
public class MyInterceptor implements MethodInterceptor {
   
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
   
		Method method = invocation.getMethod();
		System.out.println("执行目标方法前置通知:"+method.getName());
		Object proceed = invocation.proceed();
		System.out.println("执行目标方法后置置通知:"+method.getName());
		return proceed;
	}
}
定义一个目标方法:

```java
@Component
public class MyCalculate implements Calculate {
   
	private int num = 0;

	public int getNum() {
   
		return num;
	}

	public void setNum(int num) {
   
		this.num = num;
	}

	@Override
	public int add(int numA, int numB) {
   
		System.out.println("执行目标方法:add");
		//1 / 0
		return numA + numB;
	}

	@Override
	public int sub(int numA, int numB) {
   
		System.out.println("执行目标方法:sub");
		return numA - numB;
	}
}

加入到容器中

@Bean
	public Calculate calculate() {
   
		return new MyCalculate();
	}

	@Bean
	public MethodBeforeAdvice myLogAdvice() {
   
		return new MyLogAdvice();
	}
	
	@Bean
	public MyInterceptor myInterceptor() {
   
		return new MyInterceptor();
	}

然后利用FactoryBean创建代理类;

	@Bean
	public ProxyFactoryBean proxyFactoryBean() {
   
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setInterceptorNames("myLogAdvice","myInterceptor");
		proxyFactoryBean.setTarget(calculate());
		return proxyFactoryBean;
	}

执行一下

	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
		Calculate calculate = context.getBean("proxyFactoryBean",Calculate.class);
		//calculate.sub(7, 8);
		calculate.add(1, 2);

结果为:在这里插入图片描述
从结果看,使用了责任链的方式对advice和Intercept都进行了调用,使用了FactoryBean的getObject方法创建一个代理类实现的。

下面介绍Advisor,它包含了advice增强和porintCut切点。Advisor有好几个实现类,比如NameMatchMethodPointcutAdvisor

	@Bean
	public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
   
		NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
		advisor.setAdvice(myLogAdvice());
		advisor.setMappedName("add");
		return advisor;
	}
@Bean
	public ProxyFactoryBean proxyFactoryBean() {
   
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setInterceptorNames("nameMatchMethodPointcutAdvisor");
		proxyFactoryBean.setTarget(calculate());
		return proxyFactoryBean;
	}

只对add方法进行拦截

自动生成代理类:BeanNameAutoProxyCreator

	@Bean
	public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
   
		BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
		beanNameAutoProxyCreator.setInterceptorNames("myLogAdvice");
		beanNameAutoProxyCreator.setBeanNames("calculate");
		return beanNameAutoProxyCreator;
	}

其实 Advisor 还有一个更加灵活的实现类 RegexpMethodPointcutAdvisor,它能实现正则匹配

DefaultAdvisorAutoProxyCreator,它的配置非常简单,直接使用下面这段配置就可以了,它 就会使得所有的 Advisor 自动生效,无须其他配置。(记得把之前的autoProxyCreator配置去掉,无需创建2次代理)

@Bean
	public DefaultAdvisorAutoProxyCreator autoProxyCreator(){
   
		return new DefaultAdvisorAutoProxyCreator();
	}

下面进入正题:Spring Aop @Aspect注解的源码解析

先上自己画的流程图:
在这里插入图片描述
直接上代码:不做过多解释,因为代码上都有注释:

使用AOP前要在配置类加上@EnableAspectJAutoProxy注解,其有proxyTargetClass属性,当为ture则强制使用CGLB,如果不设置默认为false,自己判断使用JDK还是CGLB,它的作用就是加入AOP代理对象的创建类AnnotationAwareAspectJAutoProxyCreator

因为Aop代理对象实现了后置处理器SmartInstantiationAwareBeanPostProcessor,所以容器启动的过程中,会为符合AOP规则的bean创建动态代理加入到容器中;

容器在启动的时候会在九处调用后置处理器,如下图,也是自己画的:
在这里插入图片描述
那么接下来直接上代码:
容器启动的时候会执行org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation:

@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
   
		//获取当前bean是否可以创建AOP的缓存
		Object cacheKey = getCacheKey(beanClass, beanName);

		//当前是否为是 TargetSource对象,通过自定义targetSource对象可以获取对象,这里就不对此用法做出详解
		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
   
			//判断当前bean是否被解析过
			if (this.advisedBeans.containsKey(cacheKey)) {
   
				return null;
			}
			//1、判断当前Bean是否A为OP的相关基础类Advice.class	Pointcut.class Advisor.class AopInfrastructureBean
			//2、判断当前bean是否为AspectJPointcutAdvisor :如果  <aop:aspect ref="beanName"> 就会生成此类
			//3、如何符合上述条件,则说明不需要进行AOP,加入到缓存中advisedBeans.put(cacheKey, Boolean.FALSE);
			//shouldSkip这个里面的逻辑还是比较多的:
			// a、它会获取xml配置的Advisor和原生接口的AOP的Advisor ,在第一次doCreateBean时会把获取
			//到的beanName缓存到BeanFactoryAdvisorRetrievalHelper.cachedAdvisorBeanNames缓存中,后面会接着使用
			// b、获取@Aspect中配置的Advisor,把得到的Advisor的beanName缓存BeanFactoryAspectJAdvisorsBuilder.aspectBeanNames,
			// 的到的对象缓存到Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>()
			//c、接着判断当前bean是否是AspectJPointcutAdvisor
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
   
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		//如果是自定义的 TargetSource对象,则不进行后面doCreateBean的操作,直接进行AOP的操作
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
   
			if (StringUtils.hasLength(beanName)) {
   
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

接着执行org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization,也就是这一步创建了代理对象。

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   
		if (bean != null) {
   
			//获取当前Bean相关AOP使用的Key;
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			//因为循环依赖创建的AOP动态代理,则不在创建,并且直接移除
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
   
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值