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
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
Spring自己也提供了类似的执行实际的实现类:
- 接口MethodBeforeAdvice,继承了接口BeforeAdvice
- 接口AfterReturningAdvice
- 接口ThrowsAdvice
- 接口AfterAdvice
- 接口MethodInterceptor
Spring会把五个注解解析为对应的Advice类:
- @Before:AspectJMethodBeforeAdvice,实际上就是一个MethodBeforeAdvice
- @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
- @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
- @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
- @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,然后执行对应的代理逻辑;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-06-30 MySQL第一篇