 - Introductions进行类扩展方法)
源码仓库Gitee · GitHubIntroductions引用在 Aspect 中称为类型间的声明使切面能够声明被通知的对象拦截的对象实现给定的接口并提供该接口的实现。简单点说可以将一个类的实现方法复制到未实现的类中动态的扩展类没有的方法。通过 DeclareParents 注解进行声明声明在一个父类型的属性上比如接口其中注解的属性 value 指定对哪些路径包下的类进行类方法扩展defaultImpl 指定以哪个类为模板。如下案例1切面类Aspect Component public class LogAspects { DeclareParents(value com.hrh.aop.*, defaultImpl IndexDao.class) public static Dao dao; Pointcut(execution(public * com.hrh.aop.*.*(..))) public void pointcut() { } Before(pointcut()) public void LogStart() { System.out.println(LogStart...); } }2接口和实现类public interface Dao{ void query(); } Repository public class IndexDao implements Dao{ public void query(){ System.out.println(querythis.getClass().getSimpleName()); } }3配置类Configuration EnableAspectJAutoProxy ComponentScan(com.hrh.aop) public class Config { }4空类Repository(orderDao) public class OrderDao{}5测试和结果public static void main(String[] args) { AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(Config.class); Dao bean (Dao) context.getBean(indexDao); bean.query(); System.out.println(----------); Dao dao (Dao) context.getBean(orderDao); dao.query(); } 结果 LogStart... queryIndexDao ---------- queryIndexDao根据前文Spring笔记(3) - SpringAOP基础详解和源码探究可以得知如下EnableAspectJAutoProxy(proxyTargetClassfalse)表示使用JDK动态代理默认EnableAspectJAutoProxy(proxyTargetClasstrue)表示使用CGLIB代理debug运行后查看beanFactory的OrderDao和IndexDao代理后的对象如下图从上面可以看到IndexDao的对象是使用JDK代理的而OrderDao的对象是CGLIB代理的。那么当设置EnableAspectJAutoProxy(proxyTargetClasstrue)时是如下图由此可以判定DeclareParents注解给拦截到的类进行代理使用的是CGLIB代理其实OrderDao没有实现任何接口所以最终实现的是CGLIB那么可以查看下代理类生成的内容是什么可以在context前面加一行代码将代理类class文件存入本地磁盘System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, xxx);//xxx是存放代理类class的本地路径 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(Config.class);下面是代理类class反编译后的完整代码感兴趣可以完全打开完全的很多内容包含了各种父类的方法比如toString、clone、equals、hashCodeView Code【完整代码查看Spring(11) - Introductions进行类扩展方法 - Minion~~ - 博客园】从上面可以看到代理类里面有query方法代理类继承了OrderDao和实现了Dao接口public class OrderDao$$EnhancerBySpringCGLIB$$ee33136 extends OrderDao implements Dao, SpringProxy, Advised, Factory { ............................ final void CGLIB$query$4() { super.query(); } public final void query() { try { MethodInterceptor var10000 this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 null) { CGLIB$BIND_CALLBACKS(this); var10000 this.CGLIB$CALLBACK_0; } if (var10000 ! null) { var10000.intercept(this, CGLIB$query$4$Method, CGLIB$emptyArgs, CGLIB$query$4$Proxy); } else { super.query(); } } catch (Error | RuntimeException var1) { throw var1; } catch (Throwable var2) { throw new UndeclaredThrowableException(var2); } } ............................ }从前文Spring笔记(3) - SpringAOP基础详解和源码探究可以得知在 AbstractAutoProxyCreator#wrapIfNecessary()中会对bean进行包装增强代理下面打下断点debug跟踪下流程当执行到wrapIfNecessary方法下面的代码时bean还是原对象未被代理Object[] specificInterceptors getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);当上面的代码执行完后可以看到specificInterceptors数组包含了advice(通知)、introducedInterface(引用接口、typePatternClassFilter(class过滤规则重点)其中advice包含了defaultImplType(默认实现类型上面代码中定义的可以看到是IndexDaointerfaceType(接口类型)可以看到是Dao以上信息是通过解析DeclareParents注解而来的下面typePatternClassFilter里面的详情可以看到上面的信息又包含在typePatternClassFilter里面同时里面包含了过滤规则最后通过DefaultAopProxyFactory#createAopProxy方法创建的对象如下图当执行query方法时CglibAopProxy.DynamicAdvisedInterceptor#intercept方法进行拦截时会执行父类的方法ReflectiveMethodInvocation#ReflectiveMethodInvocation执行完会跳转到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 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; if (dm.methodMatcher.matches(this.method, this.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 { // Its an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);//执行方法 } }下面执行delegate对象获取到IndexDao然后AopUtils.invokeJoinpointUsingReflection执行IndexDao的query实现方法其中getIntroductionDelegateFor方法中的delegate通过反射对默认实现类型进行构造方法实例化后调用实例化的方法由此可以看出上面的第二次打印结果没有打印前置通知的内容因为此时IndexoDao的对象是原对象不是代理对象总结Introductions引用通过 DeclareParents 注解实现可以给一个空白类进行方法复制它通过解析DeclareParents 注解信息代理时将定义的静态接口变量作为它的父类并实现它的方法方法执行时调用的是defaultImpl 指定的类模板的实现方法参考https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-introductions