Spring源码分析之AOP源码

前言

继续学习中,坚持写作,不求快,只求深

源码分析

aop应用代码

配置类

@Configuration
@ComponentScan("com.xxsc.cn.aopstudy")
@EnableAspectJAutoProxy(exposeProxy = true)
public class Myconfig {
   
}

需要加强类的方法

@Component
public class JoinPointDemo {
   
    public void test1(){
   
        System.out.println("test1方法执行了");
    }
}

切面

@Aspect
@Component
public class MyAsepect {
   

    /**
     * 对连接点的描述,个人理解筛选增强的方法
     */
    @Pointcut("execution(* com.xxsc.cn.aopstudy.JoinPointDemo.*(..))")
    public void pointCut(){
   }

    /**
     * 前置通知:在Pointcut筛选的join point连接点执行前执行
     */
    @Before(value = "pointCut()")
    public void methodBefore(JoinPoint joinPoint){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("前置通知执行的目标方法为"+methodName);
    }

    /**
     * 后置通知 在Pointcut筛选的join point连接点执行后执行,无论是否异常
     */
    @After(value = "pointCut()")
    public void methodAfter(JoinPoint joinPoint){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("后置通知执行的目标方法为"+methodName);
    }

    /**
     * 返回通知:在Pointcut筛选的join point连接点执行正常情况下执行,异常不执行
     */
    @AfterReturning(value = "pointCut()",returning = "result")
    public void methodReturning(JoinPoint joinPoint,Object result){
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("返回通知执行的目标方法为"+methodName);
    }

    /**
     * 异常通知 :在Pointcut筛选的join point连接点执行异常情况下执行,正常返回不执行
     */
    @AfterThrowing(value = "pointCut()")
    public void methodAfterThrowing(JoinPoint joinPoint) {
   
        String methodName = joinPoint.getSignature().getName();
        System.out.println("异常通知执行的目标方法为"+methodName);
    }
}

测试类

public class App {
   
    public static void main(String[] args) {
   
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
        JoinPointDemo joinPointDemo = context.getBean("joinPointDemo", JoinPointDemo.class);
        joinPointDemo.test1();
    }
}

执行结果

前置通知执行的目标方法为test1
test1方法执行了
返回通知执行的目标方法为test1
后置通知执行的目标方法为test1

对于以上代码,非常基础,不做过多解释。

aop源码分析入口

对于以上配置类,发现有一个注解,@EnableAspectJAutoProxy(exposeProxy = true) 这个就是SpringAOP源码分析入口。
源码片段一

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   
	boolean proxyTargetClass() default false;
	boolean exposeProxy() default false;
}

发现 @Import(AspectJAutoProxyRegistrar.class) 注解,我们知道@Import注解可以往容器中添加bean,那么@Import添加bean有三种方式,分别如下:
①、直接import类的class
@Import(Student.class),那么Spring就会往容器中添加Student这个bean组件。
②、import实现ImportSelector接口的类
@Import(MyPort.class)

public class MyPort implements ImportSelector {
   
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
   
        return new String[]{
   "com.xxsc.demo.Demo1","com.xxsc.demo.Demo2","com.xxsc.demo.Demo3","com.xxsc.demo.Demo4"};
    }
}

它会把返回的数组的Demo1、Demo2、Demo3、Demo4这四个类都添加到Spring容器中。
③、import实现ImportBeanDefinitionRegistrar接口的类
@Import(MyRegistrar.class)

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
   
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   
        //手动注册
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Student.class);
        registry.registerBeanDefinition("student",rootBeanDefinition);
    }
}

代码清晰可见手动注册一个bean到容器当中。
此时再看AOP的代码@Import(AspectJAutoProxyRegistrar.class),查看AspectJAutoProxyRegistrar类就容易理解了,是属于以上第三种方式,一起看看是如何手动注册的。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
   
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   
	    //关键代码
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
   
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
   
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
   
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
   
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
   
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
   
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
   
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		//这块代码和以上Demo类似,很容易理解,这个class根据源码得知:是这个类AnnotationAwareAspectJAutoProxyCreator
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

以上代码,只看最后几行即可,能找到需要添加Bean的类为:AnnotationAwareAspectJAutoProxyCreator ,这个类就是我们分析源码AOP最为关键的类。
这里补充一点,既然学习源码,就在深一点,如Spring是如何通过@Import注解导入这个组件的

//AbstractApplicationContext类的refresh()中,以下代码是bean扫描把bean定义添加到容器中
invokeBeanFactoryPostProcessors(beanFactory);
//AbstractApplicationContext类的refresh()中,以下代码是getBean初始化Bean
finishBeanFactoryInitialization(beanFactory);

那么从上面IOC的源码分析之后,我们只关注Spring是如何扫描到这个AnnotationAwareAspectJAutoProxyCreator 的bean定义并把它加到容器当中去的。

//ConfigurationClassPostProcessor类的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法的下面几行代码,不关键的在这省略
            ...
            //下面这行代码核心逻辑:解析我们的配置类Myconfig,通过@ComponentScan注解找到所有的候选bean
            //挨个判断是否需要添加到Spring容器,但是它只关注@Service @Compont @Contoller @Register 注解的组件
            //找到有@Import 和 @Bean 注解的类,个人理解,先找了个地方保存起来,并没有注册Bean定义
            parser.parse(candidates);
			.....
			//这行代码,找出通过@Import和@Bean注解的方式导入Spring容器的组件
			this.reader.loadBeanDefinitions(configClasses);

分析this.reader.loadBeanDefinitions(configClasses); 这行代码

   //核心代码
	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
   
		......
		//把保存的候选bean进行遍历,判断是否是通过@Import的方式导入的
		if (configClass.isImported()) {
   
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		//把保存的候选bean进行遍历,判断是否是通过@Bean的方式导入的
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
   
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}
		//加载bean定义是从ImportedResources方式
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        //加载ImportBean资源
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

因为此时,我们的bean就是三个:配置类、切面、需要增强的类 ,并没有通过@Import @Bean方式加入,但是配置类有@Impor 方式要导入的类,所以断定走loadBeanDefinitionsFromRegistrars这行代码
分析loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); 这行代码

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
   
        //去注册,先不看后面怎么注册的源码,走debug再返回来分析接下来的源码
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

DEBUG运行分析:
断点位置:
在这里插入图片描述
此时容器中的bean定义组件有这么多,我们自己的,还有系统的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值