Sring之AOP

Spring之AOP

0、概述

那么介绍完成ProxyFactory之后,和SpringAOP又有什么关系呢?现在来看几乎没有什么关联,那么说明还有更高层的顶层应用。

在使用SpringAOP的时候并不会这么直接的去使用ProxyFactory,假如说我们希望产生的代理对象能够成为一个bean,那么再通过容器来进行获取得到对应的代理对象。Spring支持这些,但是前提是我们开发者得告知Spring,被代理的是哪个对象,代理逻辑是什么。

1、ProxyFactoryBean

利用ProxyFactoryBean将对象注册成容器中的bean:

@Bean
public ProxyFactoryBean userServiceProxy(){
 UserService userService = new UserService();

 ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
 proxyFactoryBean.setTarget(userService);
 proxyFactoryBean.addAdvice(new MethodInterceptor() {
  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
   System.out.println("before...");
   Object result = invocation.proceed();
   System.out.println("after...");
   return result;
  }
 });
 return proxyFactoryBean;
}

只不过,作为开发者的我们肯定得告诉Spring,那些类需要被代理,代理逻辑是什么。ProxyFactoryBean类是一个FactoryBean,也是一个ProxyFactory,最终会根据设置的代理逻辑来产生一个UserService类型的bean。

但是缺点是这种方式只能针对某一个Bean

1.1、将Advice或者是Advisor定义为bean

ProxyFactoryBean还有额外的功能,比如可以把某个Advise或Advisor定义成为Bean,然后在ProxyFactoryBean中进行设置

//@Configuration
@ComponentScan(basePackages = "com.guang.spring.autowired.aop.demytic.factorybean")
public class MyConfig1 {
  
	@Bean
	public MethodInterceptor myadvice(){
		return new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before");
				Object result = invocation.proceed();
				System.out.println("common return");
				return result;
			}
		};
	}
  
	@Bean
	public ProxyFactoryBean userServiceProxy(){
		UserService userService = new UserService();
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setTarget(userService);
		proxyFactoryBean.setInterceptorNames("myadvice");
		return proxyFactoryBean;
	}
  
}

那么利用这里方式,就说明了如果还有其他的bean需要实现这个advesor的话,可以实现复用

2、BeanNameAutoProxyCreator

这个bean比上面的ProxyFactory强大一点,通过BeanNameAutoProxyCreator可以根据bean的名称对批量的Bean进行AOP,并且指定了代理逻辑,指定了一个InterceptorName,也就是一个Advice,前提条件是这个Advise也得是一个Bean,这样Spring才能找到的,但是BeanNameAutoProxyCreator的缺点很明显,它只能根据beanName来指定想要代理的Bean

@Configuration
@ComponentScan(basePackages = "com.guang.spring.autowired.aop.demytic.factorybean")
public class MyConfig2 {

	@Bean
	public UserService userService(){
		return new UserService();
	}


	@Bean
	public OrderServiceImpl orderService(){
		return new OrderServiceImpl();
	}

	@Bean
	public UserServiceImpl userServiceImpl(){
		return new UserServiceImpl();
	}


	@Bean
	public MethodInterceptor myadvice(){
		return new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before");
				Object result = invocation.proceed();
				System.out.println("proceed");
				return result;
			}
		};
	}

	@Bean
	public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
		BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
		beanNameAutoProxyCreator.setBeanNames("userSe*");
		beanNameAutoProxyCreator.setInterceptorNames("myadvice");
		beanNameAutoProxyCreator.setProxyTargetClass(true);

		return beanNameAutoProxyCreator;
	}
}

myadvice也必须是一个bean,beanName的名字得是以userSe开头的才会来生成对应的代理对象。

如果不是,也就是无法匹配,那么不可以生成对应的代理对象。

3、DefaultAdvisorAutoProxyCreator

@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
  // 匹配方法是test的都符合生成代理对象,对应的代理逻辑是advice中写的  
  NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
  pointcut.addMethodName("test");
  DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
  defaultPointcutAdvisor.setPointcut(pointcut);
  defaultPointcutAdvisor.setAdvice(new ZhouyuAfterReturningAdvise());
  return defaultPointcutAdvisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
  DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  return defaultAdvisorAutoProxyCreator;
}

通过DefaultAdvisorAutoProxyCreator会直接去找所有的Bean,这里是根据bean中的方法来进行匹配,如果找到了对应的test方法,根据Advisor中的PointCut和Advice信息生成对应的Advisor,确定要代理的Bean以及代理逻辑。

而DefaultAdvisorAutoProxyCreator就是将Advisor创建成对应的代理对象的。

对应的例子:

@Configuration
@ComponentScan(basePackages = "com.guang.spring.autowired.aop.demytic.factorybean")
public class MyConfig3 {

	@Bean
	public UserService userService(){
		return new UserService();
	}


	@Bean
	public OrderServiceImpl orderService(){
		return new OrderServiceImpl();
	}

	@Bean
	public UserServiceImpl userServiceImpl(){
		return new UserServiceImpl();
	}
     
  
    // 定义Advisor的组成部分
	@Bean
	public DefaultPointcutAdvisor defaultPointcutAdvisor(){
		// 方法匹配,只要bean中的方法中是有test的,都会生成对应的代理对象
		NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
		pointcut.addMethodName("test");
		DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
		defaultPointcutAdvisor.setPointcut(pointcut);
		defaultPointcutAdvisor.setAdvice(new MyBeforeAdvice());
		return defaultPointcutAdvisor;
	}

	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		return defaultAdvisorAutoProxyCreator;
	}
}

测试代码:

public class Test {
	public static void main(String[] args) {
		// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig);
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig3);
		UserService userService = context.getBean(UserService);
		OrderServiceImpl orderService = context.getBean(OrderServiceImpl);
		UserServiceImpl userService1 = context.getBean(UserServiceImpl);
		userService.test();
		System.out.println("-------");
		orderService.test();
		System.out.println("-------");
		userService1.test();
	}
}

响应结果:

before
test......
-------
before
test......
-------
before
test......

但是,我们发现通过这种方式,我们得依靠某一个类来实现定义我们的Advisor,或者Advise,或者Pointcut,那么这个步骤能不能更加简化一点呢?

4、使用注解定义Advisor

不使用类的方式来定义Advisor,而是使用注解的方式来进行定义。

如果要是能够定义一个类,通过类上的方法来定义PointCut和对应的Advice形成对应的Advisor呢?

@Aspect
@Component
public class ZhouyuAspect {

 @Before("execution(public void com.zhouyu.service.UserService.test())")
 public void zhouyuBefore(JoinPoint joinPoint) {
  System.out.println("zhouyuBefore");
 }
}

这就是所谓的SpringAOP的一种实现方式。通过上面这个类,我们就直接定义好了所要代理类和方法(通过一个表达式),以及代理逻辑(被@Before修饰的方法),简单明了,这样对于Spring来说,它要做的就是来解析这些注解了,解析之后得到对应的Pointcut对象、Advice对象,生成Advisor对象,扔进ProxyFactory中产生对应的代理对象,从上面可以看到DefaultAdvisorAutoProxyCreator可以解析Advisor生成对应的代理bean,但是如何将某个类中的注解和方法解析成Advisor呢?其实在spring提供的注解@EnableAspectJAutoProxy这个类中已经实现了。

上面的也说了DefaultAdvisorAutoProxyCreator仅仅只是会去找对应的Advisor而已,而将上面的类需要解析成Advisor是由@EnableAspectJAutoProxy这个注解导入的AnnotationAwareAspectJAutoProxyCreator这个类来进行解析的,将其标注了@Aspect注解的类中的方法解析成为对应的Advisor。然后让DefaultAdvisorAutoProxyCreator来找到之后来进行遍历。

5、解析成为Advisor

首先来搭建一个测试案例:

@Component
public class UserService {

	public void test(){
		System.out.println("test");
	}

	public void log(){
		System.out.println("log method");
	}

}
@Configuration
@ComponentScan(basePackages = "com.guang.spring.autowired.aop.demytic.aopannotation")
@EnableAspectJAutoProxy
public class MyConfig {

}
@Aspect
@Component
public class MyAspect {
   @Before("execution(public void com.guang.spring.autowired.aop.demytic.aopannotation.service.UserService.test())")
   public void before(){
      System.out.println("method execute before");
   }
}
public class Test {
   public static void main(String[] args) {
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig);
      UserService userService = context.getBean(UserService);
      userService.test();
      System.out.println("------------");
      userService.log();
   }
}

看下对应的输出

method execute before
test
------------
log method

可以看到仅仅只是针对了userService中的test方法来进行了代理逻辑,而对于log方法来说并没有执行对应的逻辑。

6、@EnableAspectJAutoProxy注解原理分析

看下@EnableAspectJAutoProxy这个注解,这个注解导入了@Import(AspectJAutoProxyRegistrar)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar)
public @interface EnableAspectJAutoProxy {
	// 为true表示自动代理类,使用CGLIB进行代理。
	boolean proxyTargetClass() default false;
	// 是否将代理对象暴露到当前线程中来
	boolean exposeProxy() default false;

}

6.1、@EnableAspectJAutoProxy注解属性分析

proxyTargetClass为true表示的是使用CGLIB作为动态代理,对应的代码在ProxyFactory中体现了:

if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {

而exposeProxy属性表示的是是否将代理对象暴露到被代理对象类中的方法中来。

if (this.advised.exposeProxy) {
  // 设置到上下文中来
  oldProxy = AopContext.setCurrentProxy(proxy);
  setProxyContext = true;
}

6.2、导入AspectJAutoProxyRegistrar类作用

那么看看导入的类做了什么?AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar这个类,那么接下来是要注册bean,看看这个类:AnnotationAwareAspectJAutoProxyCreator

这个类间接的继承了AbstractAutoProxyCreator,这个是BeanPostProcessor,也就是说会在bean初始化后阶段中来做处理。

注册完了对应的bean信息之后,还会会bean定义来做一些处理:

6.3、后置处理逻辑判断

既然是在bean的初始化后来做处理,那么应该看下对应的逻辑。直接进入到父类AbstractAutoProxyCreator类中的postProcessAfterInitialization方法中来

这个方法之前分析过,直接进入到wrapIfNecessary方法中来:

6.3.1、查找Advisor

那么这里的重点就是如何根据beanClass和bean名称找到对应的Advisor的。

进入到org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean方法中来

那么看下如何来进行选择的:

继续跟进查找的逻辑,进入到AnnotationAwareAspectJAutoProxyCreator中的findCandidateAdvisors方法

其实这里分为了两步:1、查找实现了Advisor接口的Bean;2、查找注解@Aspect的,那么这里就直接分析加了注解的

查找得到Advisor类型的Bean

可以看下如何来进行查找得到对应的Advisor的

解析得到@AspectJ中的Advisor

看看如何来分析的:

主要分为了以下几个步骤:

  • 1、从beanFactory中获取得到所有的beanName;
  • 2、根据bean的名称从beanFactory中获取得到bean名称对应的类;
  • 3、用类Class判断类上是否存在@AspectJ注解;
  • 4、封装切面的注解信息,然后对封装信息进行解析;

重点就是需要知道如何来进行解析的

MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 利用BeanFactoryAspectInstanceFactory来解析Aspect类
// 如何解析?
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

主要分为了以下几个步骤:

  • 1、首先遍历得到类中所有的方法(包括继承和实现);
  • 2、遍历每个方法上,判断注解上的@Before、@AfterRetrunning等注解上的PointCut信息;(如果当前方法上是这么写的@After("pointcut()"),那么此时得到的Pointcut并没有去解析pointcut()得到对应的表达式);
  • 3、排除掉方法上没有PointCut注解的方法,将PointCut进一步进行封装成InstantiationModelAwarePointcutAdvisorImpl对象,本质上也是一个Advisor;

6.3.2、筛选得到后的Advisor

上面已经得到了实现了Advisor接口的bean和加了@AspectJ注解的bean中的advisor,那么并非是所有的advisor都会生效,还需要来进行筛选,得到满足条件的Advisor,那么看下如何来进行筛选的。

List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

看下具体的解析方法

上述过程分为以下几个步骤

  • 1、首先利用类匹配器来进行是否符合;如果不符合,继续下一个Advisor;如果符合,进入到下一个步骤;
  • 2、下面就是利用Advisor中的PointCut来匹配对应的方法;
  • 3、将匹配了的Advisor加入到集合中来;

6.3.3、对得到的Advisor排序

但是感觉没有什么卵用,这里就先不来看了。

至此,在每个bean在初始化后经过BeanPostProcessor对象的过程中遍历筛选得到Advisor对象成功。

6.4、创建代理对象过程

当前bean经过筛选,得到了容器中的Advisor对应的bean的Advisor和@AspectJ注解对应的PointCut筛选后的Advisor了,接下来就是需要根据具体的代理过程来进行创建了。

可以看到几个重点步骤:

  • 1、创建ProxyFactory对象,代理被代理对象进去;
  • 2、设置对应的Advisor(只不过这里对Advisor做了一层封装);
  • 3、返回代理对象;

注意看这里的最后一步,将从容器中得到的Advisor设置到ProxyFactory中的Advisor之后,并不是说在代理对象执行方法的时候,就不会来进行筛选了。

通过之前对ProxyFactory的分析可以知道,在ProxyFactory产生代理对象之后,在执行目标方法的时候,依然会再次进行筛选。

7、Advice对应的API

  1. @Before
  2. @AfterReturning
  3. @AfterThrowing
  4. @After
  5. @Around

Spring自己也提供了类似的执行实际的实现类:

  1. 接口MethodBeforeAdvice,继承了接口BeforeAdvice
  2. 接口AfterReturningAdvice
  3. 接口ThrowsAdvice
  4. 接口AfterAdvice
  5. 接口MethodInterceptor

Spring会把五个注解解析为对应的Advice类:

  1. @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
  2. @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
  3. @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
  4. @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
  5. @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor

8、总结

Advisor组成

Advisor
    PointCut
    	ClassFilter:类过滤器
        MethodMatcher:方法匹配器
    Advice:对连接点进行增强的逻辑

开启AOP后bean初始化并执行过程

  • 1、在利用@EnableAspectJAutoProxy开始AOP后,会向容器中注册一个AnnotationAwareAspectJAutoProxyCreator类型(BeanPostProcessor)的BeanDefinition。某个bean在执行到初始化后的方法的时候,会执行到AOP对应的BeanPostProcessor,在对应的BeanPostProcessor来执行对应的AOP逻辑生成代理对象;
  • 2、在代理对象执行某个方法时,根据当前的方法,找到匹配的Advisor,然后执行对应的代理逻辑;
posted @ 2022-06-30 17:29  写的代码很烂  阅读(6)  评论(0编辑  收藏  举报