自研Spring AOP(二)
声明
本篇文章除部分引用外,均为原创内容,如有雷同纯属巧合,引用转载请附上原文链接与声明。
阅读条件
读本篇文章需掌握java基础知识,熟悉代理模式,了解ClassLoader加载类的流程,掌握注解的使用方式,灵活运用反射,自研Spring IOC(一)
注意
本文若包含部分下载内容,本着一站式阅读的想法,本站提供其对应软件的直接下载方式,但是由于带宽原因下载缓慢是必然的,建立读者去相关官网进行下载,若某些软件禁止三方传播,请在主页上通过联系作者的方式将相关项目进行取消。
相关文章
文章大纲
- JDK动态代理
- CGLIB动态代理
- JDK和CGLIB动态代理异同
- 注解声明与切面定义
- 项目整合
- 功能演示
简述
项目的github源码地址传送门点此,该篇文章对应的分支为aop-1.0
该篇文章将阐述如何实现一个简易版本的AOP框架,以实现横切逻辑织入,该框架未加入对于AspectJ语法树的支持,即切点的定义较为简单,不能够做到类似于Spring把切点定义到方法级别上的功能,该版本旨在阐明AOP原理和实现方式,阐明其原理无需加入Aspect语法树支持。读者若有兴趣,该功能在自研Spring AOP2.0(三)文章中被实现,可以看作是该版本的一个升级版,支持和Spring一样的切点声明。
该自研AOP框架支持具有特下特征
- 支持JDK动态代理
- 支持CGLIB动态代理
- 不支持动态选取代理规则,只能在JDK和CGLIB动态代理中选取任意一种(在Spring中,默认采取的JDK动态代理,若被代理对象未实现接口或被代理方法不是接口方法时,才会采用CGLIB动态代理)
- 支持@Aspect注解,用于定义切面类,在该注解中直接可以声明切点范围,切面的顺序
JDK动态代理
自动JDK1.3之后,便提供了JDK动态代理功能。在介绍JDK动态代理之前,先简述一下类加载过程
- 通过ClassLoader获取class文件的二进制流
- 根据获取的二进制流,解释为静态存储存储结构,并转化为运行时数据结构
- 生成代表该类的Class对象,作为方法区该类的数据访问入口
那么如果需要从类加载这个过程来实现动态代理,只需要在第二步做解释以及转化时,根据原有字节码和动态代理配置计算出代理类的字节码,并加载到JVM中即可,实际上JDK动态代理以及CGLIB动态代理便是如此实现。
JDK动态代理的特点和要求如下
- 程序运行时动态生成类的字节码,并加载到JVM中
- 要求被代理的类必须实现接口,即只能实现接口方法的代理对象
- 并不要求被代理对象实现接口(对比代理模式),正因如此便提供了动态创建代理对象的能力
相关类
java.lang.reflect.InvocationHandler
该类本质为一个函数式接口,仅声明了一个方法,该方法签名如下。
其注释的意思是,当与之相关的代理类的方法被执行时,将会执行该方法实现。其三个参数和返回值分别为
- proxy:通过jdk动态代理生成的动态代理对象,注意不是被代理的对象,而是由JDK动态代理动态生成的代理对象,该动态代理对象作为入参之一是考虑到可能在执行过程中需要访问改对象,在大多数情况下不需要使用该对象
- method:所执行的方法,该方法是被代理类的实际方法
- args:所执行方法的参数,该代理类的执行方法的参数,如果代理类的的方法声明中的参数是空,则实际运行时,args将是null,如果是基础类型,将转化为包装类型
- 返回值:返回method实际执行的返回值,需要注意的是,如果原始方法的返回值是基础类型,但实际执行时返回null时将抛出NullPointerException。如果返回值类型和代理方法声明的返回类型不同将抛出ClassCastException。
public interface InvocationHandler {
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*
* @return the value to return from the method invocation on the
* proxy instance. If the declared return type of the interface
* method is a primitive type, then the value returned by
* this method must be an instance of the corresponding primitive
* wrapper class; otherwise, it must be a type assignable to the
* declared return type. If the value returned by this method is
* {@code null} and the interface method's return type is
* primitive, then a {@code NullPointerException} will be
* thrown by the method invocation on the proxy instance. If the
* value returned by this method is otherwise not compatible with
* the interface method's declared return type as described above,
* a {@code ClassCastException} will be thrown by the method
* invocation on the proxy instance.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy
该类是JDK动态代理中创建动态代理的入口,重点关注其newProxyInstance方法,该方法是真正创建动态代理的方法,部分签名如下
- loader:被代理类的类加载器
- interfaces:需要实现的代理类去代理的接口
- invocationHandler:当interfaces中的接口被执行时,代理去执行invocationHandler中的invoke方法
- 返回值:返回创建的动态代理对象
/**
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param invocationHandler the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler invocationHandler)
使用举例
该使用举例因为和框架实现无关,不在github项目源码中
首先创建InvocationHandler的实现类,如下图所示,该方法需要传入被动态代理的对象,进行初始化,在被代理代理方法被执行时,即执行该类的invoke方法,该方法首先打印“正在使用动态代理,准备调用原始方法”,然后执行其真正的原始方法,然后再打印“原始方法调用完毕,返回值为:" + returnValue + ",动态代理执行完毕”,而后返回,结束。
public class InvocationHandlerImpl implements InvocationHandler {
private Object proxyTarget;
public InvocationHandlerImpl(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在使用动态代理,准备调用原始方法");
Object returnValue = method.invoke(proxyTarget, args);
System.out.println("原始方法调用完毕,返回值为:" + returnValue + ",动态代理执行完毕");
return returnValue;
}
}
创建一个接口,如下
public interface IExamService {
String show();
}
创建改接口的实现类,如下
public class ExamServiceImpl implements IExamService {
@Override
public String show() {
System.out.println("原始方法被调用了");
return "这是返回值";
}
}
创建测试类,如下
public class ExamTest {
public static void main(String[] args) {
Object proxyTarget = new ExamServiceImpl();
InvocationHandler invocationHandler = new InvocationHandlerImpl(proxyTarget);
IExamService examService = (IExamService) Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader()
, proxyTarget.getClass().getInterfaces(), invocationHandler);
examService.show();
}
}
全局UML如下
执行结果如下,表示动态代理创建成功,执行正确
CGLIB动态代理
较JDK动态代理,CGLIB动态代理不需要被代理类实现相应的接口,所以能够代理的类也更加灵活,但自从Java9之后,Java对安全检查进行了修正升级,有了更严格的安全检查,笔者所使用的是Java11,CGLIB版本为3.3.0,在使用cglib生成动态代理对象时,会得到如下的警告信息,意思是CGLIB生成动态代理时发生了非法的访问操作,至于哪一步是非法访问操作未作深入研究,目前并不影响使用,相信在未来的版本中CGLIB将修复这一问题。
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/C:/Users/user/.m2/repository/cglib/cglib/3.3.0/cglib-3.3.0.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
这是由于上述原因,相较于JDK动态代理采用CGLIB也有优缺点
- 优点:CGLIB所需要动态代理的类不需要实现接口,本质上是动态生成一个子类,重写需要代理的非final方法,相较于JDK动态代理适用情形更广
- 缺点:可能在未来的JDK发行版中将禁止CGLIB内核的非法访问,导致动态代理失效,如果在做JDK升级时,可能出现CGLIB不兼容的情形,所以升级时还需要找到对应的CGLIB版本,但是JDK动态代理是由JDK开发团队维护,在升级JDK时肯定会进行兼容,不需再维护。
CGLIG动态代理的特点和要求如下
- 不要求被代理类实现接口(对比JDK动态代理)
- 内部依靠ASM Java字节码操控框架来修改字节码
- 本质上为动态生成被代理类的子类,重写需要代理的非final方法,并注册绑定相关的回调方法
相关类
net.sf.cglib.proxy.MethodInterceptor
可以看到该类也是函数式接口,其中只声明了intercept一个方法,注意到该类继承了Callback接口,也就是上文中提到动态代理对象所调用的方法,代取代原始的方法调用,所有被代理的方法都将转为调用该方法。该方法的方法签名,入参,返回值如下
- obj:通过Enhancer.create方法生成的动态代理对象,可能需要在执行中进行获取,与InvocationHandler中的proxy入参类似
- method:被代理对象类的原始方法,若需要执行被代理对象的方法,则可以采用该方法,与InvocationHandler中的method入参类似
- args:被代理类的被代理方法执行时的参数,如果是基础类型,这里将会被转换为包装类型,与InvocationHandler中的args入参类似
- methodProxy:该参数是InvocationHandler中没有的,这里表示的是CGLIB所生成的代理方法,通常与第一个入参联合使用。
- 返回值:返回一个与被代理方法返回值类型兼容的类型的值
public interface MethodInterceptor extends Callback{
/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
* @param obj "this", the enhanced object
* @param method intercepted Method
* @param args argument array; primitive types are wrapped
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy methodProxy) throws Throwable;
}
net.sf.cglib.proxy.Enhancer
该类是CGLIB提供创建动态代理对象的入口类,重点关注其create方法,该方法的方法签名,入参,返回值如下
- type:需要被代理类的class对象
- callback:当被代理类的方法被调用时,动态代理对象将调用callback中的方法,起的效果类似于JDK动态代理中InvocationHandler
- 返回值:返回创建完成的动态代理对象
/**
* Helper method to create an intercepted object.
* For finer control over the generated instance, use a new instance of <code>Enhancer</code>
* instead of this static method.
* @param type class to extend or interface to implement
* @param callback the callback to use for all methods
*/
public static Object create(Class type, Callback callback)
对比JDK动态代理,可以看到CGLIB在创建动态代理对象时,不需要指定其需要实现的接口(在Enhancer类中也有该特性的创建方法),不需要指明被代理类的类加载器
使用举例
该使用举例因为和框架实现无关,不在github项目源码中
首先创建MethodInterceptor实现类,对比JDK动态代理的InvocationHandlerImpl,其运行流程与InvocationHandlerImpl一直,也仅仅是替换了需要实现的接口,如下
public class MethodInterceptorImpl implements MethodInterceptor {
private Object proxyTarget;
public MethodInterceptorImpl(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("正在使用动态代理,准备调用原始方法");
Object returnValue = method.invoke(proxyTarget, args);
System.out.println("原始方法调用完毕,返回值为:" + returnValue + ",动态代理执行完毕");
return returnValue;
}
}
创建接口,如下
public interface IExamService {
String show();
}
创建接口的实现类,如下
public class ExamServiceImpl implements IExamService {
@Override
public String show() {
System.out.println("原始方法被调用了");
return "这是返回值";
}
}
创建测试类,如下,
public class ExamTest {
public static void main(String[] args) {
Object proxyTarget = new ExamServiceImpl();
MethodInterceptor methodInterceptor = new MethodInterceptorImpl(proxyTarget);
IExamService examService = (IExamService) Enhancer.create(ExamServiceImpl.class, methodInterceptor);
examService.show();
}
}
全局UML如下,如下
运行结果表明,代理成功,运行正确,但也得到了一些警告信息,原因已在上文提到
注意,在CGLIB动态代理中,MethodInterceptorImpl还有第二种实现方式,如下。可以看到在执行原始方法时,替换为了methodProxy.invokeSuper(obj, args)方法,这和method.invoke(proxyTarget, args)方法区别在于,后者使用的是以创建好的被代理对象进行执行,和JDK动态代理类似,需要执行原被代理类方法逻辑时,只能传入被代理类的实例,然后进行执行。但是前者可以通过methodProxy.invokeSuper(obj, args)也能达到执行被代理类方法逻辑的能力。但是不建议进行如此使用,这里可以理解为CGLIB动态的创建了一个被代理类的对象,然后进行执行,该对象是一个没有受用户管理和业务初始化的类,比如一些属性并没有没注入等等。
public class MethodInterceptorImpl implements MethodInterceptor {
private Object proxyTarget;
public MethodInterceptorImpl(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("正在使用动态代理,准备调用原始方法");
// Object returnValue = method.invoke(proxyTarget, args);
Object returnValue = methodProxy.invokeSuper(obj, args);
System.out.println("原始方法调用完毕,返回值为:" + returnValue + ",动态代理执行完毕");
return returnValue;
}
}
注解声明与切面定义
注解声明
上文完成对于动态代理的介绍,下面开始声明框架所需要使用的注解,并定义切面。首先声明用户声明切面的注解,用于标识注解。如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
/**
* 切点定义,对某一种注解标识的类生效,比如@Controller...
* @return 对哪一种注解表示的类进行切面织入
*/
Class<? extends Annotation> value();
/**
* @return 该切面类的执行顺序,按照从小到大执行,如果多个切面同时指向一个切点,按顺序执行
*/
int order() default Integer.MAX_VALUE;
}
切面类定义
切面类定义,这里为了实现简单,要求所有的切面必须继承该切面类,该切面类定义如下,方法实现均为空,其具体实现应该由具体的切面来覆写实现。
public abstract class DefaultAspect {
public void before() throws Exception {}
public void afterReturn() throws Exception {}
}
统一的InvocationHandler实现
该IncationHandler用于JDK动态代理种控制统一的切面执行流程。
实现如下,可以看出该类接受被代理类的原始对象和切面集合,首先按顺序执行切面的before方法,然后执行被代理类的原始方法,最后倒序执行切面的afterReturn方法
public class AspectChainInvocationHandler implements InvocationHandler {
private Object target;
private List<? extends DefaultAspect> sortedAspectList;
public AspectChainInvocationHandler(Object target, List<? extends DefaultAspect> aspectList) {
this.target = target;
this.sortedAspectList = aspectList.stream()
.sorted(Comparator.comparingInt(c -> c.getClass().getAnnotation(Aspect.class).order()))
.collect(Collectors.toList());
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for (DefaultAspect defaultAspect : sortedAspectList) {
defaultAspect.before();
}
Object returnValue = method.invoke(target, args);
List<? extends DefaultAspect> reverseOrderAspectList = sortedAspectList.stream()
.sorted(Comparator.comparingInt(c -> -1 * c.getClass().getAnnotation(Aspect.class).order()))
.collect(Collectors.toList());
for (DefaultAspect defaultAspect : reverseOrderAspectList) {
defaultAspect.afterReturn();
}
return returnValue;
}
}
统一的MethodInterceptor实现
该IncationHandler用于CGLIB动态代理种控制统一的切面执行流程。与上述中的AspectChainInvocationHandler对比,完全一致
public class AspectChainMethodInterceptor implements MethodInterceptor {
private Object target;
private List<? extends DefaultAspect> sortedAspectList;
public AspectChainMethodInterceptor(Object target, List<? extends DefaultAspect> aspectList) {
this.target = target;
this.sortedAspectList = aspectList.stream()
.sorted(Comparator.comparingInt(c -> c.getClass().getAnnotation(Aspect.class).order()))
.collect(Collectors.toList());
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
for (DefaultAspect defaultAspect : sortedAspectList) {
defaultAspect.before();
}
Object returnValue = method.invoke(target, args);
List<? extends DefaultAspect> reverseOrderAspectList = sortedAspectList.stream()
.sorted(Comparator.comparingInt(c -> -1 * c.getClass().getAnnotation(Aspect.class).order()))
.collect(Collectors.toList());
for (DefaultAspect defaultAspect : reverseOrderAspectList) {
defaultAspect.afterReturn();
}
return returnValue;
}
}
项目整合
工具类
若需要让项目快速整合创建动态代理的能力,则需要创建相关的动态代理工具类,该工具类的实现如下。
可以看到,外部在使用时,只需要调用其createDynamicProxy方法即可创建对应的动态代理对象,无论是创建JDK动态代理还是CGLIB动态代理,其流程一致。
public class DynamicProxyUtil {
/**
* 通过cglib的方式创建动态代理
* 注意:使用时java会warning 信息,标识cglib是通过非法的访问去修改的字节码,将在未来版本被禁止。目前版本为jdk11,暂时未受影响
* @param targetObject
* @param aspectList
* @return
*/
public static Object createCglibDynamicProxy(Object targetObject, List<? extends DefaultAspect> aspectList) {
return Enhancer.create(targetObject.getClass(), new AspectChainMethodInterceptor(targetObject, aspectList));
}
/**
* 创建jdk动态代理
* @param targetObject
* @param aspectList
* @return
*/
public static Object createJDKDynamicProxy(Object targetObject, List<? extends DefaultAspect> aspectList) {
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), new AspectChainInvocationHandler(targetObject, aspectList));
}
/**
* 创建动态代理,默认使用cglib
* @param targetObject
* @param aspectList
* @return
*/
public static Object createDynamicProxy(Object targetObject, List<? extends DefaultAspect> aspectList) {
// return createJDKDynamicProxy(targetObject, aspectList);
return createCglibDynamicProxy(targetObject, aspectList);
}
}
IOC容器修改
首先需要IOC容器能够管理通过动态代理创建出的动态代理对象,所以对IOC 容器新增doAop方法,并且将动态代理对象放入单独的缓存池中,且需要修改getBean方法,首先从动态代理对象的缓存池中查找,再去常规容器缓存池中查找,修改后IOC容器如下
public class BeanContainer {
private static Map<Class<?>, Object> container = new ConcurrentHashMap<>();
private static Map<Class<?>, Object> proxyBeanContainer = new ConcurrentHashMap<>();
private static List<Class<? extends Annotation>> beanAnnotations = Arrays.asList(Controller.class, Service.class, Repository.class, Component.class, Aspect.class);
private static Set<Class<?>> getClasses() {
// 未做改变
}
public static Map<Class<?>, Object> getContainer() {
// 未做改变
}
/**
* 从容器中获取受管理的bean,这里需要注意的是需要支持对于接口类型注入其实现类
* 这里应当先从代理类的容器中获取,若获取不到则从常规容器中获取。
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
T bean;
bean = (T) proxyBeanContainer.get(clazz);
if (Objects.isNull(bean)) {
bean = (T) container.get(clazz);
if (Objects.isNull(bean)) {
for (Class<?> cla : getClasses()) {
if (clazz.isAssignableFrom(cla) && !clazz.equals(cla)) {
bean = (T) getBean(cla);
break;
}
}
}
}
return bean;
}
public static void addBean(Class<?> clazz, Object bean) {
// 未做改变
}
public static void addBean2Proxy(Class<?> clazz, Object bean) {
proxyBeanContainer.put(clazz, bean);
}
public static Object remove(Class<?> clazz) {
// 未做改变
}
public static void doIoc() {
// 未做改变
}
public static void doAop() {
for (Map.Entry<Class<?>, Object> entry : container.entrySet()) {
if (needProxy(entry.getKey())) {
addBean2Proxy(entry.getKey(), createProxy(entry));
}
}
}
public static void scanBeanAndInit(String basePackage) {
// 未做改变
}
/**
* 创建指定类的代理类
*
* @param entry
* @return
*/
private static Object createProxy(Map.Entry<Class<?>, Object> entry) {
List<Class<?>> aspectsClasses = new ArrayList<>(getAspectByClass(entry.getKey()));
List<DefaultAspect> aspectList = new ArrayList<>();
container.forEach((k, v) -> {
if (aspectsClasses.contains(k)) {
aspectList.add((DefaultAspect) v);
}
});
return DynamicProxyUtil.createDynamicProxy(entry.getValue(), aspectList);
}
/**
* 判断该类是否存在其相关的切面配置
*
* @param clazz
* @return 该类是否存在其相关的切面配置
*/
private static boolean needProxy(Class<?> clazz) {
return !CollectionUtil.isNullOrEmpty(getAspectByClass(clazz));
}
public static Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
return container.keySet().stream().filter(cla -> cla.isAnnotationPresent(annotation)).collect(Collectors.toSet());
}
/**
* 获取class需要织入的切面类
* @param clazz
* @return
*/
public static Set<Class<?>> getAspectByClass(Class<?> clazz) {
return getClassesByAnnotation(Aspect.class).stream().filter(cla -> clazz.isAnnotationPresent(cla.getAnnotation(Aspect.class).value())).collect(Collectors.toSet());
}
private static void extractClassPath(String basePackage, String path, Set<Class<?>> classes) {
// 未做改变
}
private static boolean needLoad(Class<?> clazz) {
// 未做改变
}
}
SpringApllication修改
由于新增了AOP操作,需要在run方法中新增对于AOP的显式调用,修改后如下,可以看到是先做的AOP,在做的IOC操作,这样,在IOC中通过getBean方法获取bean进行装配时,即可获取最新的动态代理对象。
public class SpringApplication {
public static void run(Class<?> appClass, String... args) {
String basePackage = fetchBasePackage(appClass);
BeanContainer.scanBeanAndInit(basePackage);
BeanContainer.doAop();
BeanContainer.doIoc();
}
private static String fetchBasePackage(Class<?> appClass) {
SpringBootApplication annotation = appClass.getAnnotation(SpringBootApplication.class);
return (Objects.isNull(annotation) || Strings.isNullOrEmpty(annotation.value())) ? appClass.getPackageName() : annotation.value();
}
}
功能演示
Controller定义
@Controller
public class ExampleController {
@Autowired
private IExampleService service;
public String show() {
log.info("ExampleController.show()");
service.show();
return "controller show method";
}
}
Service定义
public interface IExampleService {
void show();
}
Service实现定义
@Service
public class ExampleServiceImpl implements IExampleService {
@Autowired
private ExampleRepository repository;
@Override
public void show() {
log.info("ExampleServiceImpl.show()");
repository.show();
}
}
Repository定义
@Repository
public class ExampleRepository {
public void show() {
log.info("ExampleRepository.show()");
}
}
Aspect定义
@Aspect(value = Repository.class, order = 1)
@Slf4j
public class LogAspect extends DefaultAspect {
private ThreadLocal<Long> startTime = ThreadLocal.withInitial(() -> 0L);
@Override
public void before() throws Exception {
startTime.set(System.currentTimeMillis());
log.info("logAspect before() was invoke at [{}]", startTime.get());
}
@Override
public void afterReturn() throws Exception {
log.info("logAspect afterReturn was invoke ,total cost [{}]", System.currentTimeMillis() - startTime.get());
startTime.remove();
}
}
测试类
@SpringBootApplication
public class NobitaSpringApplication {
public static void main(String[] args) {
SpringApplication.run(NobitaSpringApplication.class, args);
ExampleController bean = BeanContainer.getBean(ExampleController.class);
bean.show();
}
}