基本概念
切面类
Spring切面类,@Aspect,可以在不侵入代码的情况下执行,如日志的打印。通常定义一个切面类使用@Aspect,@Component注解。定义好一个切面类。
切点
切点@PointCut,切点的定义通常使用该注解,一般有两种方式
1.@PointCut("execution(public * com.test.aspect..*.*(..))"),第一个*表示匹配任意的方法返回值,..表示另个或多个;
2.@PointCut("@annotation(com.test.utils.log)"),这种请求需要去定义一个注解,在方法上加注解即可打印日志;
增强
增强处理,Advice,主要有五个注解:
@Before,在切点方法执行之前执行
@After,在切点方法执行之后执行
@AfterReturning,在切点方法返回后执行
@AfterThrowing,在切点方法抛异常执行
@Aroud,能控制切点执行前,执行后
定义切面示例如下:
import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Service; @Aspect @Service public class LogPrint { @Pointcut("execution(public * com.test.demo.TestMainService.printLogPointCut())") private void logPrintPointCut(){ System.out.println("logPrintPointCut log"); } @Around("execution(public * com.test.demo.TestMainService.printLogAroud())") private void logPrintAroud(){ System.out.println("logPrintAroud log"); } @Before("execution(public * com.test.demo.TestMainService.printLogBefore())") private void logPrintBefore(){ System.out.println("logPrintBefore log"); } }
其他参数处理,比如需要获取方法的入参、出参等,使用JoinPoint可以获取;
ProceedingJoinPoint,继承了JoinPoint用于支持环绕通知,增加了proceed方法,用于启动目标方法执行;示例如下
@Aspect @Service public class LogPrint { @Around("execution(public * com.test.demo.TestMainService.printLogAroud(..))") //注意黄色的..,这个地方掉坑里很久,加了参数后直接切面不生效了 private void logPrintAroud(ProceedingJoinPoint pjp) throws Throwable {
//获取参数 Object[] args = pjp.getArgs(); System.out.println("logPrintAroud log "+args[0]); //执行方法 Object result = pjp.proceed(); System.out.println("logPrintAroud log "+result); } }
执行结果如下
logPrintAroud log aaaa printLogAroud logPrintAroud log result
除了定义具体类,也可以定义一个注解,在方法的上添加注解打印;
@PointCut("@annotation(com.test.utils.log)
切点注解示例
定义一个注解
@Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogAnnotation { String value() default ""; }
定义切面
@Around("@annotation(com.test.demo.LogAnnotation))") private void logAnnoAroud(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); System.out.println("logAnnoAroud log "+args[0]); //执行方法 Object result = pjp.proceed(); System.out.println("logAnnoAroud log "+result); }
在类上使用注解
@LogAnnotation("anno") public String logAnnoAroud(String id){ System.out.println("logAnnoAroud "); return "annotation"; }
达到一样的效果
源码分析
第一步,搜索aspect对应的parse类
第二步,查看parser类,目的就是为了注册一个bean类
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册bean类 AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
...
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
//注册bean到容器中
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
...
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
第三步,AnnotationAwareAspectJAutoProxyCreator,继承关系
第四步,查看AbstractAutoProxyCreator,继承了BeanPostProcessor(在bean初始化前后使用)
/** bean初始化后进行封装 * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ... /** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 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()); //直接返回代理,而不是返回bean return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } ... /** 创建代理,返回bean的代理类 * Create an AOP proxy for the given bean. * @param beanClass the class of the bean * @param beanName the name of the bean * @param specificInterceptors the set of interceptors that is * specific to this bean (may be empty, but not null) * @param targetSource the TargetSource for the proxy, * already pre-configured to access the bean * @return the AOP proxy for the bean * @see #buildAdvisors */ protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } return proxyFactory.getProxy(getProxyClassLoader()); }
第五步,getProxy创建proxy,本例具体的使用的代理类为JdkDynamicAopProxy,创建JDK代理
@Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); }
第六步,查看具体代理类的invoke方法,看是如果代理bean执行的
/** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.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) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // Get the interception chain for this method. 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... MethodInvocation 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 != Object.class && 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); } } }
具体的proceed方法
@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. 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 { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }