我随意写,你随意看。

林老头儿

但愿绝望和无奈远走高飞

Fork me on Gitee

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已经被增强为代理类。

posted @ 2020-05-20 10:56  林老头儿  阅读(328)  评论(0编辑  收藏  举报