1. 实现方式概述
Spring AOP除了通过AspectJ的注解实现拦截之外,还可以通过编码方式实现拦截。这里介绍如下几种方式:
● 方式一
实现AbstractAutoProxyCreator类,在getAdvicesAndAdvisorsForBean方法返回Advisor或Advice实现拦截,而Advisor由Advice组成,所以最终都是Advice完成拦截增强动作。
另外AbstractAutoProxyCreator实现类要作为一个Spring Bean才能起作用,因此可以加上@Component注解,或者是通过@Bean定义成Bean,甚至@Service也可以。
● 方式二
直接将Advisor定义为一个Spring Bean,在Advisor中返回Advice则可。
● 方式三
通过ProxyFactory注入业务实现类,然后注入Advice或Advisor,接着返回业务类接口代理对象,调用业务方法。
2. AbstractAutoProxyCreator实现类方式
2.1 类结构
首先选几个要实现的Advice:
选择一个要实现的Advisor:
最后的类结构如下:
类 | 描述 |
---|---|
MyAutoProxyInterceptor | 示例在AutoProxyCreator中返回Advice,实现比较常用的MethodInterceptor接口,它也是Advice。 |
MyAutoProxyAfterAdvice | 示例被Advisor创建的场景,在方法调用后增加处理。 |
MyAutoProxyAdvisor | 示例在AutoProxyCreator中返回Advisor,创建MyAutoProxyAfterAdvice。 |
MyAutoProxyCreator | AbstractAutoProxyCreator的实现类。 |
2.2 代码
2.2.1 MyAutoProxyInterceptor
拦截目标类方法调用,在目标方法调用前做处理,这里没有加条件要拦截哪些类,在autoproxy类中加判断条件:
java
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@Slf4j
public class MyAutoProxyInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("MyInterceptorForAutoProxy be called.");
// 注:也可以在调用后处理
return invocation.proceed();
}
}
2.2.2 MyAutoProxyAfterAdvice
拦截目标类方法调用,在目标方法调用成功后处理:
java
import com.kengcoder.springframeawsome.aop.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Method;
@Slf4j
public class MyAutoProxyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// 注意这里要使用正确的判断条件
if (AopUtils.getTargetClass(target).isAssignableFrom(BookService.class)) {
log.info("MyAutoProxyAfterAdvice be called.");
}
}
}
2.2.3 MyAutoProxyAdvisor
Advisor类,包装Advice类。
java
import com.kengcoder.springframeawsome.aop.service.IBookService;
import org.aopalliance.aop.Advice;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import java.lang.reflect.Method;
public class MyAutoProxyAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 注意匹配条件要正确
return method.getDeclaringClass().isAssignableFrom(IBookService.class);
}
@Override
public Advice getAdvice() {return new MyAfterReturningAdvice();}
// 可以定义advisor的执行顺序
@Override
public int getOrder() {return 3;}
}
2.2.4 MyAutoProxyCreator
自动代理类创建者类:
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.stereotype.Component;
@Slf4j
// 必须定义为Spring Bean,通过其他方式如@Bean,@Service也可以,但这个场景通常是使用@Component或@Bean
@Component
public class MyAutoProxyCreator extends AbstractAutoProxyCreator {
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
// 注意正确的判断条件
if (beanName.equals("bookService")) {
log.info("MyAutoProxyCreator: beanName " + beanName);
// 返回Advice或Advisor组成的数组
return new Object[]{new MyAutoProxyInterceptor(),
new MyAutoProxyAdvisor()};
} else {
// 条件不匹配则返回null
return null;
}
}
}
Book相关类很简单,不再赘述。
2.3 调用日志
调用IBookService的queryBook方法,输出日志如下:
java
2023-12-23 23:31:14 INFO MyAutoProxyInterceptor:11 - MyAutoProxyInterceptor be called.
2023-12-23 23:31:14 INFO BookService:22 - queryBook be called.
2023-12-23 23:31:14 INFO MyAutoProxyAfterAdvice:16 - MyAutoProxyAfterAdvice be called.
3. Advisor直接定义Bean方式
3.1 类结构
类结构如下:
这里实现了两个Advisor,各自返回一个Advice做拦截。
类 | 描述 |
---|---|
MyAdvisorBeforeAdvice | 示例在Advisor中返回Advice,在方法调用前增加处理。 |
MyAdviceAdvisor | 示例Advisor Bean,创建MyAdvisorBeforeAdvice。 |
MyAdvisorInterceptor | 示例在Advisor中返回Advice,实现比较常用的MethodInterceptor接口,它也是Advice。 |
MyInterceptorAdvisor | 示例Advisor Bean,创建MyAdvisorInterceptor。 |
3.2 代码
3.2.1 MyAdvisorBeforeAdvice
拦截目标类方法调用,在目标方法调用前处理:
java
import com.kengcoder.springframeawsome.aop.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.support.AopUtils;
import java.lang.reflect.Method;
@Slf4j
public class MyAdvisorBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// 注意正确的匹配条件
if (AopUtils.getTargetClass(target).isAssignableFrom(BookService.class)) {
log.info("MyAdvisorBeforeAdvice be called.");
}
}
}
3.2.2 MyAdviceAdvisor
Advisor类,包装Advice类。
java
import com.kengcoder.springframeawsome.aop.service.BookService;
import org.aopalliance.aop.Advice;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
// 必须定义为Spring Bean,通过其他方式如@Bean,@Service也可以,但这个场景通常是使用@Component或@Bean
@Component
public class MyAdviceAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return true;
}
@Override
public ClassFilter getClassFilter() {
// 注意正确的匹配条件
return clazz -> clazz.isAssignableFrom(BookService.class);
}
@Override
public Advice getAdvice() {return new MyAdvisorBeforeAdvice();}
@Override
public int getOrder() {return 1;}
}
3.2.3 MyAdvisorInterceptor
拦截目标类方法调用,在目标方法调用前处理:
java
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@Slf4j
public class MyAdvisorInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("MyAdvisorInterceptor be called.");
// 注:也可以在调用后处理
return invocation.proceed();
}
}
3.2.4 MyInterceptorAdvisor
Advisor类,包装Advice类。
java
import com.kengcoder.springframeawsome.aop.service.BookService;
import org.aopalliance.aop.Advice;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
// 必须定义为Spring Bean,通过其他方式如@Bean,@Service也可以,但这个场景通常是使用@Component或@Bean
@Component
public class MyInterceptorAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return true;
}
@Override
public ClassFilter getClassFilter() {
return clazz -> clazz.isAssignableFrom(BookService.class);
}
@Override
public Advice getAdvice() {return new MyAdvisorInterceptor();}
@Override
public int getOrder() {return 2;}
}
3.3 调用日志
java
2023-12-23 23:43:36 INFO MyAdvisorBeforeAdvice:16 - MyAdvisorBeforeAdvice be called.
2023-12-23 23:43:36 INFO MyAdvisorInterceptor:11 - MyAdvisorInterceptor be called.
2023-12-23 23:43:36 INFO BookService:22 - queryBook be called.
4. ProxyFactory方式
ProxyFactory方式很简单,直接看代码。
4.1 代码
java
@Autowired
private IBookService bookService;
void test() {
ProxyFactory factory = new ProxyFactory(bookService);
// 可以注入多个Advice和Advisor,如果Advisor已经包含了某个Advice,重复添加该Advice,只会执行一次
factory.addAdvice(new MyAdvisorBeforeAdvice());
factory.addAdvisor(new MyAdviceAdvisor()); // 包含MyAdvisorBeforeAdvice,只会执行一次
IBookService proxyBookService = (IBookService) factory.getProxy();
Book book = proxyBookService.queryBook("Effective Java");
}
4.2 调用日志
java
2023-12-25 16:18:46 INFO MyAdvisorBeforeAdvice:23 - MyAdvisorBeforeAdvice[default] be called.
2023-12-25 16:18:46 INFO BookService:22 - queryBook be called.
5. 调用顺序
5.1 调用顺序验证
注:以下关于调用顺序不包含ProxyFactory方式。
到现在为止,我们实现了3个Advisor,都通过getOrder()方法返回了顺序如下:
Advisor类 | 包装Advice | Order |
---|---|---|
MyAdviceAdvisor | MyAdvisorBeforeAdvice | 1 |
MyInterceptorAdvisor | MyAdvisorInterceptor | 2 |
MyAutoProxyAdvisor | MyAutoProxyAfterAdvice | 3 |
输出日志:
erlang
2023-12-23 23:46:37 INFO MyAutoProxyInterceptor:11 - MyAutoProxyInterceptor be called.
2023-12-23 23:46:37 INFO MyAdvisorBeforeAdvice:16 - MyAdvisorBeforeAdvice be called.
2023-12-23 23:46:37 INFO MyAdvisorInterceptor:11 - MyAdvisorInterceptor be called.
2023-12-23 23:46:37 INFO BookService:22 - queryBook be called.
2023-12-23 23:46:37 INFO MyAutoProxyAfterAdvice:16 - MyAutoProxyAfterAdvice be called.
可以看到顺序似乎正常,但其实有两点并不能确定:
● MyAutoProxyAfterAdvice是After拦截器,其他都是Before拦截器,所以必然在最后,看起来是对的,但是未必就真是这个逻辑。
● 自动代理的MyAutoProxyInterceptor在最前,虽然它不是Advisor,但和MyAutoProxyAdvisor一样,都是在MyAutoProxyCreator类中返回的,它为什么排在最前?
为了验证这些疑点,我们在MyAutoProxyAdvisor中也返回MyAdvisorBeforeAdvice,并给MyAdvisorBeforeAdvice一个name为autoproxy,用于识别是MyAutoProxyAdvisor返回的:
Advisor类 | 包装Advice | Order | Name |
---|---|---|---|
MyAdviceAdvisor | MyAdvisorBeforeAdvice | 1 | default |
MyInterceptorAdvisor | MyAdvisorInterceptor | 2 | 无 |
MyAutoProxyAdvisor | MyAdvisorBeforeAdvice | 3 | autoproxy |
输出日志:
yaml
2023-12-24 00:29:04 INFO MyAdvisorBeforeAdvice:23 - MyAdvisorBeforeAdvice[autoproxy] be called.
2023-12-24 00:29:04 INFO MyAutoProxyInterceptor:11 - MyAutoProxyInterceptor be called.
2023-12-24 00:29:04 INFO MyAdvisorBeforeAdvice:23 - MyAdvisorBeforeAdvice[default] be called.
2023-12-24 00:29:04 INFO MyAdvisorInterceptor:11 - MyAdvisorInterceptor be called.
可以看到虽然MyAdvisorBeforeAdvice的order=3,但是只要是MyAutoProxyInterceptor返回的,还是排在最前,大致可以说明AutoProxyCreator实现类方式比直接定义Advisor为Bean的方式优先级高。
查看AutoProxyCreator的父类ProxyProcessorSupport实现了Ordered接口,并且order默认值为:Integer.MAX_VALUE,这样足以证明上面的说法了,因为值越小优先级越高,而这里定义的是Integer最大值还能排在最前面:
另外Advisor Bean方式的顺序没啥问题,再看看autoproxy方式内部顺序如何,将所有Advisor都让autoproxy返回,并且按照order逆序排列在返回数组中:
java
return new Object[]{new MyAutoProxyAdvisor(), new MyInterceptorAdvisor(),
new MyAdviceAdvisor(), new MyAutoProxyInterceptor()
};
输出日志:
java
2023-12-24 00:43:05 INFO MyAdvisorBeforeAdvice:23 - MyAdvisorBeforeAdvice[autoproxy] be called.
2023-12-24 00:43:05 INFO MyAdvisorInterceptor:11 - MyAdvisorInterceptor be called.
2023-12-24 00:43:05 INFO MyAdvisorBeforeAdvice:23 - MyAdvisorBeforeAdvice[default] be called.
2023-12-24 00:43:05 INFO MyAutoProxyInterceptor:11 - MyAutoProxyInterceptor be called.
2023-12-24 00:43:05 INFO BookService:22 - queryBook be called.
可以看到advisor的order字段并没有起作用,调用顺序就是数组元素的排列顺序,这也可以理解,因为该数组可以同时返回advice和advisor,而advice没有实现Orderd接口,无法定义顺序,在混排时想控制顺序,只能靠定义返回数组时排列数组元素顺序。
5.2 调用顺序小结
● autoproxy方式优先级始终高于Advisor Bean方式。
● autoproxy内部顺序是返回Advisor和Advice的数组元素排列顺序。
● Advisor Bean方式的顺序可以通过实现Orderd的getOrder()方法决定。
因此,在两种方式同时混排的场景下要考虑到上述规则,或者不要混排,统一用一种方式实现。
6. 总结
本文介绍了Spring AOP通过编码实现拦截的几种方式,以及调用顺序的注意点,编码方式虽然看起来比注解方式复杂一些,但也提供了更大的灵活性,可以自定义一些控制逻辑,两种方式比较如下:
实现方式 | 优点 | 适用场景 |
---|---|---|
编码方式 | 更灵活,可以通过编码自定义复杂逻辑。 | 偏底层框架 |
注解方式 | 使用起来更简单。 | 偏上层业务 |
可以结合实际业务场景进行选择。
其他阅读:
如何编写软件设计文档
Spring Cache架构、机制及使用
布隆过滤器适配Spring Cache及问题与解决策略
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(五)函数式接口-复用,解耦之利刃