之前的一篇博客,我们简单分析了下aop是如何创建代理对象的。这一篇博客会继续介绍spring的aop的一些概念,通知、切面。以及相对应的源码实现。
aop配置
在开启aop到代理类生成这篇博客里,我们举了一个例子。
//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
@Slf4j
public class ServiceAspect {
//配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("execution(* com.hdj.learn.spring.aop.service..*(..))")
public void aspect() {
}
/*
* 配置前置通知,使用在方法aspect()上注册的切入点
* 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint) {
log.info("before " + joinPoint);
}
//配置后置通知,使用在方法aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint) {
log.info("after " + joinPoint);
}
//配置环绕通知,使用在方法aspect()上注册的切入点
@Around("aspect()")
public void around(JoinPoint joinPoint) {
long start = System.currentTimeMillis();
try {
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
} catch (Throwable e) {
long end = System.currentTimeMillis();
log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
//配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint) {
log.info("afterReturn " + joinPoint);
}
//配置抛出异常后通知,使用在方法aspect()上注册的切入点
@AfterThrowing(pointcut = "aspect()", throwing = "ex")
public void afterThrow(JoinPoint joinPoint, Exception ex) {
log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
}
}
首先,我们思考使用aop的目的,就是不改变源码的前提下,往一个方法的前后插入一个代码块
了解这个后,再看一次这个配置就很清楚了,我们需要知道:
1)需要往哪个类的哪个方法前后插入代码块?
2)究竟需要插入什么样的代码
比如:
@Pointcut("execution(* com.hdj.learn.spring.aop.service..*(..))")
public void aspect() {
}
这个配置说明了,需要对com.hdj.learn.spring.aop.service
这个包及其子包下的任意类的任意方法 进行代理。
@Before("aspect()")
public void before(JoinPoint joinPoint) {
log.info("before " + joinPoint);
}
这个配置,说明要在被代理的方法前,插入一段代码:
log.info("before " + joinPoint);
了解这些后,就可以带着问题开始分析这些配置:
1)ServiceAspect 这些配置,是如何被读取到spring里的
2)spring是如何应用这些配置,生成代理类的
Aspect 的配置如何读取到spring中
流程比较长,这里就不贴代码了,说下结论,spring在真正createBean方法前,会调用各个BeanPostProcessor
,在各个BeanPostProcessor里,有一个AnnotationAwareAspectJAutoProxyCreator,spring会在该类的 postProcessBeforeInitialization 里进行Advisor的初始化
。
可以这样理解,spring在创建一个类之前,会看下有没有配置aop啊,如果有的话,会把配置给转换成一个个advisor,然后缓存起来(这样后面需要生成代理类时候,就可以直接使用了)
findCandidateAdvisors的方式有两种,一种是上图第7步的findAdvisorBeans还有一种是第8步的buildAspectJAdvisors
findAdvisorBeans方式
代码很长,其实去掉那些读缓存的代码,就一句话:
advisors.add(this.beanFactory.getBean(name, Advisor.class));
找到实现了Advisor接口的类,并返回。(这里没有讲的很详细,有时间补充下细节 todo)
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = null;
synchronized (this) {
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new LinkedList<>();
}
List<Advisor> advisors = new LinkedList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
//核心方法 <-------------------------
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
动态生成advisors方式
我们的ServiceAspect例子里,并没有实现任何接口,只是使用了一个 @Aspect注解。因此不能使用上述方式,spring会通过我们的AspectJ注解(比如@Pointcut、@Before) 动态的生成各个Advisor
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<>();
aspectNames = new LinkedList<>();
//找到所有的类(因为是Object所以基本上就是所有被spring管理的类)
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//是否是Aspect(比如含有@Aspect注解)
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//生成Advisor
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new LinkedList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
流程挺长的,总结下:
1)找到所有被spring管理的类(父类是Object的类)
2)如果类含有 @Aspect 注解,调用advisorFactory.getAdvisors
方法生成对应的advisor
3)返回advisors
我们继续看看,最核心的,Advisor的创建
Advisor的创建
org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new LinkedList<>();
//遍历所有没有 @Pointcut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
最核心的,就是遍历所有的 没有Pointcut注解的方法,调用getAdvisor生成对应的Advisor
也就是,生成的Advisor的实现类,其实是 InstantiationModelAwarePointcutAdvisorImpl
不同类型通知
使用过Aop都知道,不同的注解,比如@Before、@After、@Around 都是不一样的。InstantiationModelAwarePointcutAdvisorImpl 这个类,实际上,是对底层Advisor的包装,它记录了所对应 @AspectJ的类、配置的方法、对应的切入点、以及最重要的通知
,这个通知会在InstantiationModelAwarePointcutAdvisorImpl的构造函数中被初始化。
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
很简单,spring会根据不同的注解的类型,生成对应的Advice
到了这里,简单的总结下,spring会在真正创建一个类之前,根据我们带有@Aspect类的配置生成对应的 Advise 对象,这些对象会被缓存起来。在这之后,就是在spring创建完bean后,根据这个bean生成对应的代理对象,并替换掉(也就是说,实际调用方法时候调用的对象变为这个生成的代理对象) 代理对象的创建,已经在这一篇博客里讲过了,这里就不在说明了,补充一点,生成的代理的方法。
代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//省略...
try {
//equals 方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
//hashCode方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
//如果是 DecoratingProxy类
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
//实现了Advised接口
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// ThreadLocal里记录下当前被代理的对象
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 核心方法,获取当前方法的拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
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();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
方法很长,总结下:
1)hashCode、equals方法单独处理
2)根据当前方法等,生成所需的方法拦截器
3)调用方法及拦截器
ReflectiveMethodInvocation.proceed() 方法有点长,就不分析了,大概逻辑是:
1)会根据我们之前生成的各个Advisor对应的切入点,判断下当前的方法是否满足该切入点。如果满足,将其适配为MethodInterceptor 接口并返回。
2)核心调用逻辑,就是取出一个个拦截器,先判断下方法是否满足拦截器条件,如果满足就调用。
这一步还是很复杂的,相关的设计模式也挺值得学习的,后续有时间单独写个博客分析下。
总结
到了这里,spring aop advisor相关应该算是解释清楚了。总结下:
1)在创建spring之前,spring会根据我们的配置(可能是xml、可能是注解)生成一个个Advisor
2)创建对应的对象
3)将创建的对象替换为我们对应的代理对象
而生成的代理的方法,其实就是遍历一个个的方法拦截器(通过第一步的Advisor生成/ 适配器模式)然后一个个调用