• Spring 之 AOP 原理


    概念

    面向切面编程

    Aspect Oriented Programming

    底层使用了动态代理,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。

    原理

    阅读源码

    要实现 AOP 必须添加注解 @EnableAspectJAutoProxy,点击该注解,进入接口 EnableAspectJAutoProxy

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    
    	/**
    	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
    	 * to standard Java interface-based proxies. The default is {@code false}.
    	 */
    	boolean proxyTargetClass() default false;
    
    	/**
    	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
    	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
    	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
    	 * @since 4.3.1
    	 */
    	boolean exposeProxy() default false;
    
    }
    

    继续点击 @Import(AspectJAutoProxyRegistrar.class)

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    	/**
    	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
    	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
    	 * {@code @Configuration} class.
    	 */
    	@Override
    	public void registerBeanDefinitions(
    			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
    		AnnotationAttributes enableAspectJAutoProxy =
    				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    		if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
    			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    		}
    		if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
    			AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    		}
    	}
    }
    

    看到 AspectJAutoProxyRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,继续点击该接口

    public interface ImportBeanDefinitionRegistrar {
    	/**
    	 * Register bean definitions as necessary based on the given annotation metadata of
    	 * the importing {@code @Configuration} class.
    	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
    	 * registered here, due to lifecycle constraints related to {@code @Configuration}
    	 * class processing.
    	 * @param importingClassMetadata annotation metadata of the importing class
    	 * @param registry current bean definition registry
    	 */
    	public void registerBeanDefinitions(
    			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    }
    

    该接口定义了可以给容器中自定义给 BeanDefinitionRegistry 注册组件

    分析

    利用 AspectJAutoProxyRegistrar 自定义给容器中注册 bean

    在方法 registerBeanDefinitions 上打断点,以 DEBUG 执行之前的程序,在断点处

    执行了 registerAspectJAnnotationAutoProxyCreatorIfNecessary() 方法,以单步模式继续下一步,看到依次执行了 registerAspectJAnnotationAutoProxyCreatorIfNecessary 的两个重载方法

    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    	return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
    }
    
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }
    

    继续单步执行,执行到 registerOrEscalateApcAsRequired() 方法,

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, 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;
    	}
    	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;
    }
    

    在第 110 行判断,如果容器中有此 AUTO_PROXY_CREATOR_BEAN_NAME 自动代理的 bean 名字,那就。。。,否则执行第 121 行开始的创建工作

    我这次由于是直接运行,容器里没有该 bean,所以在经过几次断言后,执行创建工作

    创建 bean 的定义信息:
    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

    查看该组件 AnnotationAwareAspectJAutoProxyCreator

    看给容器注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?


    有点复杂,未完待续…

    没有修不好的电脑
  • 相关阅读:
    VS-Visual Studio-IIS Express 支持局域网访问
    JAVA和C# 3DES加密解密
    Js调用Java方法并互相传参
    Cannot find SS.INI file for user *** 解决方法
    $.ajax()方法参数详解
    HANA Studio打开系统显示Secure storage is locked
    C#通过ODBC查询HANA数据库数据
    IIS7发布asp.net mvc提示404.0
    CentOS 搭建git服务
    解决用navicat远程连接数据库出现1045 access denied for user 'root'@'localhost' using password yes
  • 原文地址:https://www.cnblogs.com/duniqb/p/12702441.html
Copyright © 2020-2023  润新知