一、创建几个切面。

package com.tpw.newday.aspect;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.tpw.newday.annation.CacheProcesser;
import com.tpw.newday.common.MyConstants;
import com.tpw.newday.redis.RedisParam;
import com.tpw.newday.redis.RedisService;
import com.tpw.newday.utils.RedisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;import javax.validation.constraints.Null;
import java.lang.reflect.Method;/*** <h3>newday</h3>* <p>xx</p>** @author : lipengyao* @date : 2021-04-30 15:56:19**/
@Aspect
@Component
@Order(2)
public class CacheAspect {private static final Log logger = LogFactory.getLog(CacheAspect.class);private RedisUtil redisUtil = new RedisUtil(MyConstants.redis_host_ip,MyConstants.redis_port,MyConstants.redis_password);@Autowiredprivate RedisService redisService;@Pointcut("execution(* com.tpw.newday.service..*.*(..)))")private void cacheMethod() {}/*** 前置通知:在目标方法执行前调用*/@Before("cacheMethod()")public void begin() {logger.info("==@Before== lipy cache method : begin");}/*** 后置通知:在目标方法执行后调用,若目标方法出现异常,则不执行*/@AfterReturning(value = "cacheMethod()",returning = "ret")public void afterReturning(JoinPoint jp,Object ret) {Object[] args = jp.getArgs();logger.info("==@AfterReturning== lipy cache method : after returning ret:" + JSONUtil.toJsonStr(ret)+" args:" + JSONUtil.toJsonStr(args));}/*** 后置/最终通知:无论目标方法在执行过程中出现一场都会在它之后调用*/@After("cacheMethod()")public void after() {logger.info("==@After== lipy cache method : finally returning");}/*** 异常通知:目标方法抛出异常时执行*/@AfterThrowing(value = "cacheMethod()",throwing = "ex")public void afterThrowing(Throwable ex) {logger.info("==@AfterThrowing==  lipy cache method : after throwing ex:" + ex.getMessage());}/*** 环绕通知:灵活自由的在目标方法中切入代码*/@Around("cacheMethod()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 获取目标方法的名称String methodName = joinPoint.getSignature().getName();// 获取方法传入参数Object[] params = joinPoint.getArgs();logger.info("==@Around== lipy cache method --》begin method name " + methodName + " args " + (params.length > 0 ? params[0] :null));Object result  =  handleMethod(joinPoint);logger.info("==@Around== lipy cache method --》end method name " + methodName + " result " + JSONUtil.toJsonStr(result));return  result;}/*** 获取redis的key*/private String parseKey(String fieldKey, Method method, Object[] args) {//获取被拦截方法参数名列表(使用Spring支持类库)LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();String[] paraNameArr = u.getParameterNames(method);//使用SPEL进行key的解析ExpressionParser parser = new SpelExpressionParser();//SPEL上下文StandardEvaluationContext context = new StandardEvaluationContext();//把方法参数放入SPEL上下文中for (int i = 0; i < paraNameArr.length; i++) {context.setVariable(paraNameArr[i], args[i]);}String key= parser.parseExpression(fieldKey).getValue(context, String.class);return  key;}/*** 获取方法中声明的注解** @param joinPoint* @return* @throws NoSuchMethodException*/public Object handleMethod(ProceedingJoinPoint joinPoint) throws Throwable {// 获取方法名String methodName = joinPoint.getSignature().getName();// 反射获取目标类Class<?> targetClass = joinPoint.getTarget().getClass();// 拿到方法对应的参数类型Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();// 根据类、方法、参数类型(重载)获取到方法的具体信息Method objMethod = targetClass.getMethod(methodName, parameterTypes);// 获取方法传入参数Object[] params = joinPoint.getArgs();// 拿到方法定义的注解信息CacheProcesser annotation = objMethod.getDeclaredAnnotation(CacheProcesser.class);if (ObjectUtil.isNull(annotation)){// 执行源方法return joinPoint.proceed();}if (annotation.cacheOperation() == CacheProcesser.CacheOperation.QUERY){String cacheKey = annotation.key()+ ":"+this.parseKey(annotation.fieldKey(),objMethod ,params );RedisParam redisParam = new RedisParam(cacheKey,annotation.expireTime());Object cacheResult = redisService.get(redisParam);if (ObjectUtil.isNotNull(cacheResult)){logger.info(" get from cache key:" + cacheKey);return cacheResult;}else{Object obj = joinPoint.proceed();redisService.set(redisParam,obj );logger.info(" call method,set to  cache key:" + cacheKey);return  obj;}}else if (annotation.cacheOperation() == CacheProcesser.CacheOperation.UPDATE ||annotation.cacheOperation() == CacheProcesser.CacheOperation.DELETE ){Object obj = joinPoint.proceed();String cacheKey = annotation.key()+ ":"+this.parseKey(annotation.fieldKey(),objMethod ,params );RedisParam redisParam = new RedisParam(cacheKey,annotation.expireTime());logger.info(" delete from cache key:" + cacheKey);redisService.remove(redisParam);return  obj;}return  joinPoint.proceed();}
}

二、生成代理对象流程

1.在application.refresh方法中实例化代理对象前,会有一个扩展点

搜索容器中所有的InstantiationAwareBeanPostProcessor类,进行对象替换。

2.在@EnableAspectJAutoProxy(proxyTargetClass = true)中会注册AnnotationAwareAspectJAutoProxyCreator生成动态代理切面拦截类

可以看到这个类实现了InstantiationAwareBeanPostProcessor类,所以会被触发。

3.AbstractAutoProxyCreator.postProcessBeforeInstantiation中的shouldSkip方法,会搜索当前系统所有的aspect注册类。

可以看到搜索到了所有系统的aspect注解类的BEAN名字,并且每个@Before,@After注解方法都变成了一个adviser(由pointCut和advice)包含。

4.在具体的对象初始化后,会触发AbstractAutoProxyCreator.postProcessAfterInitialization,在这里根据代理类是否能满足当前系统中所有切面的pointcut,如果满足,则生成AOP的责任链代理类(责任链为所有的adviser封装成methodInterrput拦截器列表再加上jointPoint的实际调用方法)。

目前只有UserService类满足切面的要求,我们再具体看下切面的列表。

5.接着我们看到使用了cglib代理类,并配置了拦截回调对象。

 

6.最终生成的代理对象如下

生成代理类的方法

CglibAopProxy
@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());}try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();Class<?>[] additionalInterfaces = rootClass.getInterfaces();for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);}}// Validate the class, writing log messages as necessary.validateClassIfNecessary(proxySuperClass, classLoader);// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}enhancer.setSuperclass(proxySuperClass);enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));Callback[] callbacks = getCallbacks(rootClass);Class<?>[] types = new Class<?>[callbacks.length];for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}// fixedInterceptorMap only populated at this point, after getCallbacks call aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +": Common causes of this problem include using a final class or a non-visible class",ex);}catch (Throwable ex) {// TargetSource.getTarget() failedthrow new AopConfigException("Unexpected AOP exception", ex);}}

生成CGLIB的CALLBACK回调数组,其实就是方法拦截器的列表。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {// Parameters used for optimization choices...boolean exposeProxy = this.advised.isExposeProxy();boolean isFrozen = this.advised.isFrozen();boolean isStatic = this.advised.getTargetSource().isStatic();// Choose an "aop" interceptor (used for AOP calls).Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);// Choose a "straight to target" interceptor. (used for calls that are// unadvised but can return this). May be required to expose the proxy.Callback targetInterceptor;if (exposeProxy) {targetInterceptor = (isStatic ?new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));}else {targetInterceptor = (isStatic ?new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));}// Choose a "direct to target" dispatcher (used for// unadvised calls to static targets that cannot return this).Callback targetDispatcher = (isStatic ?new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());Callback[] mainCallbacks = new Callback[] {aopInterceptor,  // for normal advicetargetInterceptor,  // invoke target without considering advice, if optimizednew SerializableNoOp(),  // no override for methods mapped to thistargetDispatcher, this.advisedDispatcher,new EqualsInterceptor(this.advised),new HashCodeInterceptor(this.advised)};Callback[] callbacks;// If the target is a static one and the advice chain is frozen,// then we can make some optimizations by sending the AOP calls// direct to the target using the fixed chain for that method.if (isStatic && isFrozen) {Method[] methods = rootClass.getMethods();Callback[] fixedCallbacks = new Callback[methods.length];this.fixedInterceptorMap = new HashMap<>(methods.length);// TODO: small memory optimization here (can skip creation for methods with no advice)for (int x = 0; x < methods.length; x++) {Method method = methods[x];List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());this.fixedInterceptorMap.put(method, x);}// Now copy both the callbacks from mainCallbacks// and fixedCallbacks into the callbacks array.callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);this.fixedInterceptorOffset = mainCallbacks.length;}else {callbacks = mainCallbacks;}return callbacks;}

三、查看调用流程

1.可以看到先调第一个方法拦截器,DynamicAdvisedInterceptor.interrupt

我们再看下DynamicAdvisedInterceptor中生成的责任链

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {private final AdvisedSupport advised;public DynamicAdvisedInterceptor(AdvisedSupport advised) {this.advised = advised;}@Override@Nullablepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {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);List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {// 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 = methodProxy.invoke(target, argsToUse);}else {// We need to create a method invocation...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}

这里会封装一个

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();CglibMethodInvocation继承于ReflectiveMethodInvocation
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

接着来看ReflectiveMethodInvocation.proceed()

可以看到他会依次递归调用所有责任链上的拦截器,如果都处理完了,再调用真实的代理方法。

@Override@Nullablepublic 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);}}@Nullableprotected Object invokeJoinpoint() throws Throwable {return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);}

我们看下CglibMethodInvocation的拦截器调用顺序

可看到,先调用aroud->before->after->afterreturning->afterthrowing

aroud

@Around("cacheMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 获取目标方法的名称String methodName = joinPoint.getSignature().getName();// 获取方法传入参数Object[] params = joinPoint.getArgs();logger.info("==@Around== lipy cache method --》begin method name " + methodName + " args " + (params.length > 0 ? params[0] :null));
if (ObjectUtil.isNull(annotation)){// 执行源方法return joinPoint.proceed();
}return  result;
}

MethodInvocationProceedingJoinPoint@Overridepublic Object proceed() throws Throwable {return this.methodInvocation.invocableClone().proceed();}

before adviser

@Override
public Object invoke(MethodInvocation mi) throws Throwable {this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());return mi.proceed();
}

after adviser

@Override
public Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}finally {invokeAdviceMethod(getJoinPointMatch(), null, null);}
}

after returning adviser

@Override
public Object invoke(MethodInvocation mi) throws Throwable {Object retVal = mi.proceed();this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());return retVal;
}

after throwing adviser

@Override
public Object invoke(MethodInvocation mi) throws Throwable {try {return mi.proceed();}catch (Throwable ex) {if (shouldInvokeOnThrowing(ex)) {invokeAdviceMethod(getJoinPointMatch(), null, ex);}throw ex;}
}

这个adviser,因为他是最后一个拦截器,调用了真实代理方法。

调用完后,再不断返回退出堆栈,按调用方向,反向返回。

因为没有throwing,所以after returning->after

如果有异常,AfterThrowing->after

因为afterThrowing会继续抛出异常,而after returning没有捕获异常,所以不会处理。

这时会直接到Aftering的finally

 After执行完,再回到aroud的结束通知,但是前提是aroud要对jointpoint.procesed()捕获异常。

springboot aop加载流程相关推荐

  1. springboot启动加载流程

    springboot 启动类有两大核心: 一个是注解@SpringBootApplication,一个是main方法里面的SpringApplication.run. 1.通过main方法 启动spr ...

  2. spring ioc加载流程

    一.总框架加载流程 1.applicationContext创建beanFactory-> 2.beanFactory通过XMLbeandefineReader解析文件,获取BeanDefini ...

  3. Springboot默认加载application.yml原理

    Springboot默认加载application.yml原理以及扩展 SpringApplication.run(-)默认会加载classpath下的application.yml或applicat ...

  4. QT程序启动加载流程简介

    1. QT应用程序启动加载流程简介 1.1      QWS与QPA启动客户端程序区别 1.1.1   QWS(Qt Window System)介绍 QWS(Qt Windows System)是Q ...

  5. AngularJS 初始化加载流程

    一.AngularJS 初始化加载流程 1.浏览器载入HTML,然后把它解析成DOM. 2.浏览器载入angular.js脚本. 3.AngularJS等到DOMContentLoaded事件触发. ...

  6. android内存加载dex,安卓8.1版本dex加载流程笔记--第一篇:oatfile,oatdexfile与dexfile...

    本帖最后由 L剑仙 于 2020-3-1 18:53 编辑 看雪发一遍了,在52再发一次 菜鸟最近初学脱壳,必须得先搞明白dex的加载流程,才能搞懂哪里到了脱壳的时机.n6装的8.1,最近跟了一遍8. ...

  7. iOS进阶之底层原理-应用程序加载(dyld加载流程、类与分类的加载)

    iOS应用程序的入口是main函数,那么main函数之前系统做了什么呢? 我们定义一个类方法load,打断点,查看栈进程,我们发现dyld做了很多事,接下来就来探究到底dyld做了什么. 什么是dyl ...

  8. spring启动加载流程

    上次看了spring的加载流程,今天发现或多都忘记了,今天又看了一下,顺便总结一下: 标题spring的web项目启动: 1.首先web容器(比如Tomcat)会读取配置在web.xml中的监听器,从 ...

  9. Android6.0 keyguard锁屏加载流程分析

    锁屏界面的加载通常在android中有两种方式触发:android系统开机和screenOff(灭屏)后,再screenOn; 先来看 android系统开机时候的锁屏加载流程: 首先在系统启动过程中 ...

最新文章

  1. MIT Graph实践概述
  2. spring 数组中随机取几个_别给孩子取这三种名字!截止年末,名字中的这几个字已经烂大街了...
  3. 植物大战僵尸食人花无cd逆向分析
  4. rust怎么拆自己石墙_房屋征拆:遭遇非法强拆怎么办?如何依法维护自己的权益...
  5. 【人脸对齐-Landmarks】300W 数据集
  6. C#刷剑指Offer | 二叉树中和为某一值的路径
  7. 【今日CV 视觉论文速览】05 Dec 2018
  8. asp无组件上传图片 动态保存文件名 upload.inc逐句翻译
  9. 华为VLAN聚合原理与实验
  10. python 小技巧之获取固定下面包含的某种类型文件的个数
  11. 【渗透测试】常用工具总结
  12. js将月份转换为英文简写的形式
  13. 【树形DP】保镖排队
  14. mysql数据库教程外联_活字格外联数据库SQLServer和Mysql的经验(大多数经验也适合其它使用外联数据库的平台)...
  15. python实现12306火车票查询
  16. MySQL传统无损同步
  17. js image对象
  18. 面试角度分析:微信里面让你删掉一个功能,你选哪个?
  19. 摩托车新手驾驶教程[1]
  20. win32 24内存管理和文件操作

热门文章

  1. OA实施成功率提升,流程梳理是关键
  2. 用SAP Authority Object 对权限控制
  3. 租房还是买房结婚?大数据告诉你年轻人的真实想法
  4. 半个月两次投资,百度健康再扩服务版图
  5. 拼多多暴跌的原因找到了
  6. 3306端口_Spring Boot随机端口你都不会,怎么动态扩容?
  7. ie-css3.htc参数无效,ie-css3.htc怎么用?
  8. mysql数据库连接jar_mysql数据库连接包
  9. python模拟浏览器模块,python模块学习---mechanize(模拟浏览器)
  10. 计算机与广播电视论文,计算机技术在广播电视节目的应用论文