更多请关注:https://t.zsxq.com/fhroW
文章目录
ProxyFactory
ProxyFactory是Spring中的代理工具,其中封装了CGLIB和JDK动态代理,会自动判断使用哪种代理,AOP就是通过ProxyFactory实现的。
使用ProxyFactory
UserService userService =new UserService();
ProxyFactory proxyFactory =new ProxyFactory();
proxyFactory.setTarget(userService);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] argsObject target) throws Throwable {
System.out.println("before");
}
});
UserService userProxyService = (UserServiceproxyFactory.getProxy();
userProxyService.test();
如果通过setInterfaces方法设置了接口,将会使用JDK动态代理,getProxy方法返回的对象就会是接口类型。
Advice
通过addAdvice可以设置增强类,该方法接收Advice接口类型的参数,Advice接口的实现类,常用的有以下几种
- before MethodBeforeAdvice
- after AfterReturningAdvice
- around MethodInterceptor
- throw ThrowsAdvice
before和after不用多说,就是方法执行之前和执行之后执行
ThrowsAdvice
方法抛出异常后执行,该接口支持处理不同类型的异常,比如只处理空指针异常。为了做到支持不同类型的异常,该接口中没有定义方法声明。而是约定该类的实现类中有一个afterThrowing方法。
该方法有两种:
- void afterThrowing(Exception ex);
- void afterThrowing(Method method, Object[] args, Object target, Exception ex)
只是参数不同,没有其他含义。使用ThrowAdvice增强会在执行完afterThrowing方法后再抛出异常,而不是在原方法中直接抛出。
MethodInterceptor
环绕增强,注意是org.springframework.aop.MethodBeforeAdvice包下的,而不是CGLIB包下的,Spring拷贝了一份CGLIB的代码放在源码汇总,CGLIB也有一个MethodInterceptor。
使用其他的增强时不需要手动执行原方法,但是在环绕增强中需要手动执行原方法。所以涉及到执行顺序问题
proxyFactory.addAdvice(new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
System.out.println("before");
invocation.proceed();
System.out.println("after");
return null;
}
});
如果添加了多个增强,那么执行顺序应该是怎样的?
如果添加了多个前置增强,执行顺序就是添加顺序。
如果添加了多个环绕增强,执行顺序是链式执行的,比如填了两个环绕增强A、B,添加了一个前置增强C,添加顺序为A->C->B
proxyFactory.addAdvice(new MethodInterceptor() {
public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
System.out.println("aroundA before");
Object proceed = invocation.proceed();
System.out.println("aroundA after");
return proceed;
}
});
proxyFactory.addAdvice(new MethodBeforeAdvice() {
public void before(Method method, Object[args, Object target) throws Throwable {
System.out.println("before");
}
});
proxyFactory.addAdvice(new MethodInterceptor() {
public Object invoke(@NotNull MethodInvocatioinvocation) throws Throwable {
System.out.println("aroundB before");
Object proceed = invocation.proceed();
System.out.println("aroundB after");
return proceed;
}
});
打印顺序为:
aroundA before
before
aroundB before
test
aroundB after
aroundA after
理解为:
执行链上有A–>C–>B三个增强器。先从链上拿到A增强器,执行A的invoke方法,执行process方法时,又去执行链上拿增强器(此时A并未执行完),此时拿到C增强器,执行C的before方法,再去执行链上拿增强器,拿到B的增强器并执行B的invoke方法,执行B的prcess方法后又去执行链上拿增强器,发现没有了,就执行完自己的invoke方法。然后又回到A的aroundA after。
和递归一样,每次执行process方法就进行一次递归。
Advisor
使用advice会增强被代理对象中的所有方法,而Advisor可以对方法进行筛选,做到对类中的某些方法进行增强,可以理解为增加了切点
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("test");
}
};
@Override
public Advice getAdvice() {
return new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Objectarget) throws Throwable {
System.out.println("before");
}
};
@Override
public boolean isPerInstance() {
return false;
}
});
getPointcut方法最终返回Pointcut类型,Pointcut的实现类有很多,对应不同的筛选方式,比如表达式筛选,代码中的StaticMethodMatcherPointcut支持方法名或类的类型进行筛选
getAdvice方法即添加增强器。
所以advisor和advice的区别就在于advisor添加了切点pointCut,可以实现对某个方法进行增强。
通过spring产生代理对象
以上都是我们使用ProxyFactory手动创建代理对象的,spring也提供了对应的Bean,来帮助我们创建代理对象,有很多,比如:
- 针对单个bean进行代理,ProxyFactoryBean就是Spring提供的类
@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;
}
- 通过beanname产生代理对象
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("userSe*");
beanNameAutoProxyCreator.setInterceptorNames("zhouyuAroundAdvice");
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
- 用户指定切点和增强器,Spring生成代理
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("test");
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(pointcut);
defaultPointcutAdvisor.setAdvice(new ZhouyuAfterReturningAdvice());
return defaultPointcutAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();;
}
这里的DefaultAdvisorAutoProxyCreator最终实现了BeanPostProcessor,所以在Bean创建时会调用这里对应的逻辑生成代理对象。
这里就和AOP有点像了,用户只需要指定切点和增强逻辑,Spring就会帮助用户生成代理对象。
TargetSource
setTarget传入被代理对象,该方法会将被代理对象生成TargetSource对象。
用户也可以通过setTargetSource直接设置一个TargetSource对象,TargetSource类中有一个方法getTarget可以获取被代理对象,执行被代理对象的方法时会调用getTarget.
Spring Aop
创建流程
在配置类中添加@EnableAspectJAutoProxy注解表示开启AOP,这个注解的作用就是注册AnnotationAwareAspectJAutoProxyCreator,这个类最终实现了SmartInstantiationAwareBeanPostProcessor。
所以@EnableAspectJAutoProxy注解就是添加了一个BeanPostProcessor,会在Spring创建Bean初始化后的步骤调用。
在SmartInstantiationAwareBeanPostProcessor中会创建代理对象。步骤:
- 找到所有切面Bean(添加了@Aspect注解的类),解析其中添加了AOP注解的方法,并将每个方法封装为一个Advisor
- 判断当前Bean是否符合Advisor,如果符合,使用ProxyFactory创建代理对象(有多个符合的Advisor时,使用AddAdvisor方法添加即可)
通过这两步就创建好代理对象了
判断ProxyFactory使用JDK代理还是CGLIB代理
// config就是ProxyFactory对象
// optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// targetClass是接口,直接使用Jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用Cglib
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用Jdk动态代理
return new JdkDynamicAopProxy(config);
}
optimize属性和proxyTargetClass属性默认都是false,可以手动设置。
hasNoUserSuppliedProxyInterfaces会判断有没有添加接口,即
proxyFactory.setInterfaces()
optimize或proxyTargetClass为true,或者设置了接口则使用cglib代理,反之使用JDK动态代理。如果代理类实现了接口,但没有通过setInterfaces方法设置接口,hasNoUserSuppliedProxyInterfaces返回的是true,也就是没有接口。
Spring中如果发现代理类实现了接口,会通过setInterfaces方法设置接口
拓展
-
AspectJ
AOP是一种思想,SpringAOP是一种实现,AspectJ是另一个实现AOP思想的项目,Spring中的@Before、@After这些注解都是AspectJ中的注解,spring对这些注解实现了自己的逻辑。
所以@Before这些注解是在org.aspectj.lang.annotation包下的,所以允许使用@Before的配置类注解叫@EnableAspectJAutoProxy
另外,结合上文中使用的DefaultAdvisorAutoProxyCreator,注册了这个Bean只会去找Spring中定义的DefaultPointcutAdvisor。添加了EnableAspectJAutoProxy注解(实际上也是注册了一个类)就会去找AspectJ注解的增强 -
一个切面类中,同一个方法有两个After增强,如何判断顺序?
根据方法名称进行判断A->Z