AOP(Aspect Oriented Programing),即面向切面编程。它的主要思想是将分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。通常可以用于性能监测,访问控制,事务管理和日志记录等。
1. Spring AOP的使用
Spring AOP同时支持XML和注解两种方式进行配置。
1.1 基于注解方式
- 首先Spring文件中启用Spring注解支持和AspectJ注解支持。
<!-- 开启Spring注解支持 -->
<context:component-scan base-package="io.cheergoivan.github.aop"/>
<!-- 开启AspectJ注解支持,从而我们能够使用注解定义切面,切点等 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
- 下面是切面的定义:
@Component
@Aspect
public class LogServiceAspect {
private static final Log log = LogFactory.getLog(LogServiceAspect.class);
//切点
@Pointcut("execution(* io.cheergoivan.github.aop.service..*(..))")
public void servicePointcut(){ }
//前置Advice
@Before("servicePointcut()")
public void before(JoinPoint joinPoint){
log.info("Before " + joinPoint);
}
//后置Advice
@After("servicePointcut()")
public void after(JoinPoint joinPoint){
log.info("After " + joinPoint);
}
//环绕Advice
@Around("servicePointcut()")
public void around(JoinPoint joinPoint){
log.info("Before around " + joinPoint);
((ProceedingJoinPoint) joinPoint).proceed();
log.info("After around " + joinPoint);
}
//后置返回Advice
@AfterReturning("servicePointcut()")
public void afterReturn(JoinPoint joinPoint){
log.info("AfterReturn " + joinPoint);
}
//抛出异常后Advice
@AfterThrowing(pointcut="servicePointcut()", throwing="e")
public void afterThrow(JoinPoint joinPoint, Exception e){
log.info("AfterThrow " + joinPoint + " " + ex.getMessage());
}
}
JoinPoint保存着被拦截的方法。而@Around用于配置环绕执行,这意味着我们可以在通过@Around实现@Before和@After的效果。
1.2 基于XML方式
下面是基于XML的配置方式:
<bean id="logServiceAspect" class="io.github.cheergoivan.aop.aspect.LogServiceAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 声明一个切面,等价于@Aspect -->
<aop:aspect id="logAspect" ref="logServiceAspect">
<!-- 配置切点,等价于@Pointcut -->
<aop:pointcut expression="execution(* io.github.cheergoivan.aop.service..*(..))" id="servicePointcut"/>
<!-- 配置通知,等价于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="servicePointcut" method="before"/>
<aop:after pointcut-ref="servicePointcut" method="after"/>
<aop:after-returning pointcut-ref="servicePointcut" method="afterReturn"/>
<aop:after-throwing pointcut-ref="servicePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config>
以上的两种AOP配置方式是等价的。
2. Spring AOP基本概念
下面是Spring AOP中的基本概念:
- Aspect:切面。包含了切点(Pointcut)定义和通知(Advice)定义的一个模块。用于定义程序何时织入以及具体织入代码的模块。
- JoinPoint:连接点。 程序执行时的某个特定的点。例如方法的执行或者处理一个exception等。在Spring中,JoinPoint特指一个方法的执行。因此,Spring AOP只支持方法级别的织入。
- Advice:通知。当到达JoinPoint时,额外要执行的行为。通知又分为前置通知(@Before),后置通知(@After),环绕通知(@Around)等等。
- Pointcut:切点。切点是一个断言,用于设置目标JoinPoint。定义Advice时需要指定Pointcut,从而当执行到目标JoinPoint时,就会执行这个Advice。
- Target object:目标对象。即被Spring AOP代理的对象。
- AOP proxy:AOP 代理。Spring AOP创建的已经被织入了Advice的代理(proxy)对象。
- Weaving:织入。将切面定义的代码在目标对象中生效的过程被称为织入。织入的本质是创建一个和目标对象具有相同接口的代理对象。织入又分为编译期织入,加载期织入和运行期织入。Spring AOP使用的是运行期织入。
3. Spring AOP源码分析
3.1 <aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy/>
作为AOP的入口,是为了开启注解支持,从而我们能够通过诸如@Aspect,@Pointcut等注解定义切面,切点等信息。下面是它的源码,位于AopNamespaceHandler#init
方法中:
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
AspectJAutoProxyBeanDefinitionParser 实现了BeanDefinitionParser接口,因此,Spring容器在启动时会调用其parse方法。下面是AspectJAutoProxyBeanDefinitionParser#parse
方法的实现:
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
核心代码为AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
,它的目的是注册一个AnnotationAwareAspectJAutoProxyCreator
的Bean。
AnnotationAwareAspectJAutoProxyCreator
实现了BeanPostProcessor
接口,因此Spring AOP创建代理的过程是在Spring容器创建Bean实例时进行的。
和
<aop:aspectj-autoproxy/>
配置等价的一个注解是@EnableAspectJAutoProxy
,@EnableAspectJAutoProxy
注解调用了@Import(AspectJAutoProxyRegistrar.class)
,AspectJAutoProxyRegistrar
类实现了ImportBeanDefinitionRegistrar
接口,主要目的是向Spring容器中注入AnnotationAwareAspectJAutoProxyCreator
。
3.2 AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
实现了BeanPostProcessor
接口,创建proxy的过程主要位于postProcessAfterInitialization
方法中,下面是其实现:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
其中wrapIfNecessary
承担了创建proxy的职责,它的主要实现如下:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//...
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
//...
}
wrapIfNecessary
方法创建代理的过程主要分为两步:
1. getAdvicesAndAdvisorsForBean
获得所有支持目标Bean的Advisor。
2. createProxy
读取这些Advisor定义,通过CglibProxy或DynamicJdkProxy创建proxy。
下面我们将阐述Advisor的概念。
3.3 Advisor
Advisor
是一个Advice
的容器,下面是其接口定义:
public interface Advisor {
Advice EMPTY_ADVICE = new Advice() {};
Advice getAdvice();
//...
}
Advice
本身是一个标记接口,表明这是一个Advice。AbstractAspectJAdvice
是它的主要抽象实现类。继承了AbstractAspectJAdvice
的子类包括AspectJAfterAdvice
,AspectJMethodBeforeAdvice
,AspectJAfterReturningAdvice
等等。
AbstractAspectJAdvice
的主要数据结构如下:
private final Class<?> declaringClass;
private final String methodName;
private final Class<?>[] parameterTypes;
protected transient Method aspectJAdviceMethod;
private final AspectJExpressionPointcut pointcut;
因此AspectJMethodBeforeAdvice
封装了Advice调用时所需要的一些数据,例如具体的Method,具体的Potincut等等。
而MethodInterceptor
接口则负责了具体的Advice
调用逻辑,它的接口定义如下:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
AspectJAfterAdvice
实现了MethodInterceptor
接口,它的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
我们可以看到,AspectJAfterAdvice
的invoke(MethodInvocation mi)
方法首先会继续执行MethodInvocation#proceed
,最后再通过invokeAdviceMethod
调用Advice的实际增强代码。
3.4 getAdvicesAndAdvisorsForBean
getAdvicesAndAdvisorsForBean
获取符合该Bean的Advisor的过程如下:
1. 调用AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors
获取Spring容器中所有的Advisor。
2. AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors
会调用BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors
方法。
3. BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors
方法。读取cache,如果cache不为空,则直接返回。如果cache为空,则遍历所有的Bean,对每一个带有@Aspect注解的Bean,通过ReflectiveAspectJAdvisorFactory.getAdvisors
来解析对应的Advisor。此方法会依次解析该Bean方法上的Advice注解,包括:@Around
, Before
, @After
, @AfterReturning
, @AfterThrowing
等,然后封装成Advisor对象排序并返回。
4. 至此,AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors
方法结束,此时返回了Spring容器内的所有Advisor。
5. 调用AbstractAdvisorAutoProxyCreator#List<Advisor> findAdvisorsThatCanApply( List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName)
方法过滤出符合当前Bean的Advisor,然年返回。
Advisor的实现类中包含了Pointcut实例,判断一个Pointcut是否符合一个Bean的代码如下:
boolean canApply(Pointcut pc, Class<?> targetClass) {
MethodMatcher methodMatcher = pc.getMethodMatcher();
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
3.5 createProxy
createProxy
的主要实现如下:
`protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
//....
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
}
我们可以看到,createProxy
方法调用了ProxyFactory.getProxy
来创建代理对象。下面是ProxyFactory.getProxy
的实现:
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
createAopProxy()
方法则是创建了一个AopProxy对象,然后通过这个AopProxy来创建真正的代理对象。下面是createAopProxy()
方法的实现:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
可以看到,默认情况下使用JdkDynamicAopProxy
,除非目标对象没有接口并且在<aop:aspectj-autoproxy>
将proxyTargetClass属性设置为true。
下面是JdkDynamicAopProxy.getProxy
方法的实现:
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
可以看到,本质上是通过jdk的动态代理即Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)
来实现的,因此JdkDynamicAopProxy
实现了InvocationHandler
接口,下面是它的实现:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//...
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
//...
return retVal;
}
1. getInterceptorsAndDynamicInterceptionAdvice
目的是获取符合该Bean和Method的MethodInterceptor集合,它本质上是获取所有符合该Bean和Method的Advisor,然后转化为MethodInterceptor(如果Advisor实现了MethodInterceptor接口,则直接返回,否则调用AdvisorAdapter.getInterceptor(advisor)
进行转化)。
2. 包装一个ReflectiveMethodInvocation
对象,它内部维护了一个MethodInterceptor链,依次调用这个MethodInterceptor链的invoke(MethodInvocation invocation)
方法,最终返回这个被MethodInterceptor链处理过的实例。
下面是ReflectiveMethodInvocation.proceed
的实现:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 如果所有的增强都执行完成,则执行增强方法
return this.invokeJoinpoint();
}
// 获取下一个需要执行的拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 动态拦截器:执行动态方法匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 动态匹配成功,执行对应的拦截方法
return dm.interceptor.invoke(this);
} else {
// 动态匹配失败,忽略当前拦截器方法,递归执行下一个拦截器
return this.proceed();
}
} else {
// 普通拦截器:直接应用拦截方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
一个常见的Before Advice的MethodInterceptor的实现如下:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
//这个invoke方法是拦截器的回调方法,会在代理对象的方法被调用的时候出发回调
return mi.proceed();
}
}
4. 总结
AOP的流程如下:
1. 通过<aop:aspectj-autoproxy>
或@EnableAspectJAutoProxy
注册了AnnotationAwareAspectJAutoProxyCreator
,这是一个BeanPostProcessor
,因此会在Spring容器创建Bean时调用其postProcessAfterInitialization
方法。
2. postProcessAfterInitialization
方法首先会获得符合该Bean定义的所有Advisor,然后调用createProxy
方法。
3. createProxy
方法构造一个ProxyFactory
对象,并传入上面的Advisor List。然后根据配置选择创建一个AopProxy
对象(JdkDynamicAopProxy
或CglibAopProxy
),并调用AopProxy.getProxy
创建代理对象。
4. JdkDynamicAopProxy
的getProxy
方法实际上依赖了JDK的动态代理即Proxy.newInstance
,同时JdkDynamicAopProxy
实现了InvocationHandler
负责实现proxy回调函数invoke(Object proxy, Method method, Object[] args)
,此方法会遍历这个Bean的MethodInterceptor链,然后判断是否match当前Method,如果match则调用这个MethodInterceptor,否则继续试探下一个MethodInterceptor,直到遍历MethodInterceptor链完成。
网友评论