美文网首页
Spring源码之AOP原理

Spring源码之AOP原理

作者: 写代码的杰西 | 来源:发表于2020-08-17 20:31 被阅读0次

    AOP的基本概念

    (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
    (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象
    (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
    (4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
    (5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

    例子

    @Component
    public class BizClass {
        public void doSth(){
            System.out.println(0/0);
            System.out.println("executing a biz code");
        }
    }
    
    
    
    @Aspect
    @Component
    public class LogAspect {
        /**
         * 定义切入点,切入点为com.example.demo.aop.AopController中的所有函数
         *通过@Pointcut注解声明频繁使用的切点表达式
         */
        @Pointcut("execution(public * com.jesse.aop.BizClass.*(..)))")
        public void LogAspect(){
    
        }
    
        /**
         * @description  在连接点执行之前执行的通知
         */
        @Before("LogAspect()")
        public void doBefore(){
            System.out.println("连接点执行之前");
        }
    
        /**
         * @description  在连接点执行之后执行的通知(返回通知和异常通知的异常)
         */
        @After("LogAspect()")
        public void doAfter(){
            System.out.println(" 在连接点执行之后执行的通知(返回通知和异常通知的异常)");
        }
    
        /**
         * @description  在连接点执行之后执行的通知(返回通知)
         */
        @AfterReturning("LogAspect()")
        public void doAfterReturning(){
            System.out.println("在连接点执行之后执行的通知(返回通知)");
        }
    
        /**
         * @description  在连接点执行之后执行的通知(异常通知)
         */
        @AfterThrowing("LogAspect()")
        public void doAfterThrowing(){
            System.out.println(" 在连接点执行之后执行的通知(异常通知)");
        }
    }
    
    
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes={StudyProApplication.class})
    public class SpringTest {
        @Autowired
        BizClass bizClass;
    
        @Test
        public void test(){
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        }
        @Test
        public void testAop(){
            bizClass.doSth();
        }
    }
    
    

    结果

    连接点执行之前
    executing a biz code
    在连接点执行之后执行的通知(返回通知)
    在连接点执行之后执行的通知(返回通知和异常通知的异常)

    源码分析

    @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}.
         */
      //使用cglib还是java动态代理的方式 默认是动态代理
        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;
     
    

    而能够让这个类参与到bean初始化功能,并为bean添加代理功能的还是因为它实现了BeanPostProcessor这个接口.这个接口的postProcessAfterInitialization方法会在bean初始化结束后(赋值完成)被调用。
    这里先看一下最顶部的抽象类:AbstractAutoProxyCreator,这个抽象类主要抽象了实现代理的逻辑:

    
    @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            return bean;
        }
     
        // 主要看这个方法,在bean初始化之后对生产出的bean进行包装
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (!this.earlyProxyReferences.contains(cacheKey)) {
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }
     
        // wrapIfNecessary
        protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
            if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
                return bean;
            }
            if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
                return bean;
            }
            if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
     
            // Create proxy if we have advice.
            // 意思就是如果该类有advice则创建proxy,
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                // 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                
                // 2.返回该proxy代替原来的bean
                return proxy;
            }
     
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    

    总结
    1)将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中
    2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy

    JDK Proxy 的优势:
    最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。代码实现简单。
    基于类似 cglib 框架的优势:有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。只操作我们关心的类,而不必为其他相关类增加工作量。高性能。

    相关文章

      网友评论

          本文标题:Spring源码之AOP原理

          本文链接:https://www.haomeiwen.com/subject/vewtjktx.html