zoukankan      html  css  js  c++  java
  • Spring注解驱动开发4:AOP使用和原理

    Spring注解驱动开发系列:

    1. Spring注解驱动开发1:组件注册
    2. Spring注解驱动开发2:生命周期和属性赋值
    3. Spring注解驱动开发3:自动装配
    4. Spring注解驱动开发4:AOP使用和原理
    5. Spring注解驱动开发5:Spring声明式事务
    6. Spring注解驱动开发6:Spring扩展原理

    Spring注解驱动开发4:AOP使用和原理

    使用AOP

    最后来看一下使用AOP的方式,假设我们需要对函数进行日志记录:

    第一件事,在pom中导入依赖:

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.10</version>
    </dependency>
    

    先定一个功能类:

    @Component
    public class MathCalculator {
        public int div(int i, int j) {
            return i / j;
        }
    }
    

    然后定义日志切面类:

    @Aspect
    @Component
    public class LogAspects {
    
        //定义统一的切入点
        @Pointcut("execution(public int com.lin.springL.aop.MathCalculator.*(..))")
        public void pointCut() {
        }
    
        //可以直接指向方法
        @Before("execution(public int com.lin.springL.aop.MathCalculator.*(..))")
        public void logStart(JoinPoint joinPoint) {
            //JoinPoint会被自动传入,里面包含了很多信息
            Object[] args = joinPoint.getArgs();
            System.out.println("logStart..., args:{" + Arrays.asList(args) + "}");
        }
    
        //可以使用定义的统一的切入点
        @After("pointCut()")
        public void logEnd() {
            System.out.println("logEnd...");
        }
    
        //使用returning指定返回值参数
        @AfterReturning(value = "pointCut()", returning = "result")
        public void logReturn(Object result) {
            System.out.println("logReturn..., result:{" + result + "}");
        }
    
        //外部类也可以使用本类定义的统一切入点
        @AfterThrowing(value = "com.lin.springL.aop.LogAspects.pointCut()", throwing = "e")
        public void logEcxeption(Exception e) {
            System.out.println("logException..., excpetion:{" + e.getMessage() + "}");
        }
    
    }
    

    编写配置类:

    //配置类==配置文件
    @Configuration
    //开启自动包扫描,传入要扫描的包路径
    @ComponentScan(basePackages = "com.lin.springL")
    //开启自动代理
    @EnableAspectJAutoProxy
    public class MainConfigure {
    
    }
    

    编写主测试类:

    public class MainTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext = new
                    AnnotationConfigApplicationContext(MainConfigure.class);
            MathCalculator bean = applicationContext.getBean(MathCalculator.class);
            int div = bean.div(3, 3);
            System.out.println(div);
        }
    }
    

    运行得到输出如下:

    logStart..., args:{[3, 3]}
    logEnd...
    logReturn..., result:{1}
    1
    

    AOP原理

    从上面可以看出要使用AOP,需要在Spring配置类中增加注解@EnableAspectJAutoProxy,因此以这个注解为入口,进入查看:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    

    发现其使用了@Import(AspectJAutoProxyRegistrar.class)向容器中注册了AspectJAutoProxyRegistrar这个类,所以进入到这个类查看:

    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) {
    	  //注册AspectJAnnotationAutoProxyCreator
          AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    
          AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
          if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
             AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
          }
          if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
             AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
          }
       }
    
    }
    

    这个类中只有一个registerBeanDefinitions方法,这个方法中主要注意到AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);向容器中注入了AspectJAnnotationAutoProxyCreator这个类,这个就是AOP的核心,接下来主要就是要理解这个=组件是做什么,什么时候工作。

    AspectJAnnotationAutoProxyCreator

    image-20200518172346198

    从关系图上关注到两点:

    • AspectJAnnotationAutoProxyCreator实现了BeanPostProcessor后置处理器,在Bean初始化完成前后做事情。
    • AspectJAnnotationAutoProxyCreator实现了BeanFactoryAware,自动装配BeanFactory。

    到这大概可以猜测是向容器中注入AspectJAnnotationAutoProxyCreator后,当Bean对象创建后会被AspectJAnnotationAutoProxyCreator所增强,也就是实现该Bean的代理。

    AspectJAnnotationAutoProxyCreator实现BeanPostProcessor中的方法如下:

    	@Override
    	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    		Object cacheKey = getCacheKey(beanClass, beanName);
    
    		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
    			if (this.advisedBeans.containsKey(cacheKey)) {
    				return null;
    			}
    			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.
    		if (beanName != null) {
    			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    			if (targetSource != null) {
    				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;
    	}
    

    看到Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);创建了代理对象并返回,所以对应的Bean已经被增强为代理类。

  • 相关阅读:
    stylus入门教程,在webstorm中配置stylus
    转载 IDEA/Pycharm使用总结
    Python中itertools.groupby分组的使用
    flex:1和flex:auto详解
    JAVA中的四种JSON解析方式详解
    idea中Entity实体中报错:cannot resolve column/table/...解决办法。
    springmvc之静态资源访问不到 -记一次惨痛的经历
    三款免费好用的Gif录屏神器
    设置ItelliJ IDEA里修改jsp不重启tomcat
    Java中List, Integer[], int[]的相互转换
  • 原文地址:https://www.cnblogs.com/jinchengll/p/12922336.html
Copyright © 2011-2022 走看看