Spring AOP总结一 , 使用AOP先看spring aop具体怎么使用 , 再分析源码 。
1 , xml配置方式xml配置文件 spring.xml
<?xml version="1.0" encoding="utf-8" ?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/><bean id="userService2" class="bean.UserService2" /><bean id="beforeAdvice" class="bean.UserServiceBeforeAdvice"/><bean id="methodInterceptor" class="org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor"><constructor-arg ref="beforeAdvice"/></bean><bean class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"><property name="expression" value="https://tazarkount.com/read/execution(* bean.IUserService.*(..))"/><property name="advice" ref="methodInterceptor"/></bean></beans>service接口:
package bean;public interface IUserService {String queryUserInfo();}被代理的UserService实现类 , jdk代理只会代理接口 , 被代理的类需要实现一个接口 , 使用的时候用接口
package bean;import java.util.Random;public class UserService2 implements IUserService {public String queryUserInfo() {System.out.println("这是第二个方法");return "queryUserInfo 返回值";}}Advice增强类:
package bean;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class UserServiceBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("拦截方法:" + method.getName());}}测试结果:

文章插图
2 , 注解方式
<?xml version="1.0" encoding="utf-8" ?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="beans.yml" file-encoding="UTF-8"/><context:component-scan base-package="bean"/><aop:aspectj-autoproxy /></beans>被代理的UserService 增强类:package bean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import java.util.Random;@Servicepublic class UserService implements IUserService {public String queryUserInfo() {System.out.println("执行queryUserInfo方法");try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "返回值";}}注解式的增强类package bean;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Component@Aspectpublic class UserServiceAnnotationAdvice {@Around(value="https://tazarkount.com/read/execution(* bean.IUserService.*(..))")public Object adviceAround(ProceedingJoinPoint joinPoint) throws Throwable{//获取连接点签名Signature signature = joinPoint.getSignature();//将其转换为方法签名MethodSignature methodSignature = (MethodSignature) signature;//通过方法签名获取被调用的目标方法Method method = methodSignature.getMethod();long startTime = System.currentTimeMillis();//调用proceed方法 , 继续调用下一个通知Object returnVal = joinPoint.proceed();long endTime = System.currentTimeMillis();long costTime = endTime - startTime;//输出方法信息System.out.println(String.format("%s , 耗时:%s", method.toString(), costTime));//返回方法的返回值return returnVal;}}测试效果:
文章插图
二 , 源码剖析1 , AOP相关的几个类Joinpoint:切点定义接口
public interface Joinpoint { /*** Proceed to the next interceptor in the chain.* 得到调用链的下一个拦截器*/ Object proceed() throws Throwable; /*** Return the object that holds the current joinpoint's static part.*/ Object getThis(); /*** Return the static part of this joinpoint.*/ AccessibleObject getStaticPart();}代理类接口 , jdk动态代理和cglib动态代理都实现了这个接口package org.springframework.aop.framework;public interface AopProxy { /*** 创建代理对象* Create a new proxy object.* <p>Uses the AopProxy's default class loader (if necessary for proxy creation):* usually, the thread context class loader.*/ Object getProxy();}InvocationHandler:package java.lang.reflect;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.*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}jdk动态代理类 JdkDynamicAopProxy , 实现了 AopProxy 和 InvocationHandlerInterceptor拦截器接口
package org.springframework.cglib.proxy;import java.lang.reflect.Method;public interface MethodInterceptor extends Callback {Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;}cglib动态代理类 CglibAopProxy的 DynamicAdvisedInterceptor 实现了 MethodInterceptor2 , xml配置方式
org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator //org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor//切面 , 是一个 Advisororg.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor//切面增强类org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator// AOP核心处理类 , DefaultAdvisorAutoProxyCreator的抽象父类DefaultAdvisorAutoProxyCreator 实现了 BeanPostProcessor , 代理类在创建时 , spring在初始化这个(代理)类时会调用 DefaultAdvisorAutoProxyCreator的postProcessAfterInitialization方法 , 然后根据切面 AspectJExpressionPointcutAdvisor中的切入点AspectJExpressionPointcut(expression) 去找到当前代理类 代理方法 的所有增强 Advisor 。具体来说是在AbstractAdvisorAutoProxyCreator类的findEligibleAdvisors方法 , 通过PointcutAdvisor(MethodBeforeAdviceInterceptor的父接口)找到在ProxyFactory创建代理类的过程中(AbstractAutoProxyCreator中的wrapIfNecessary方法) , 会将所有Advisor和Advice 注入给 ProxyFactory 。
最后创建代理类 , 代理类有两种 , jdk动态代理(org.springframework.aop.framework.JdkDynamicAopProxy)或者cglib动态代理(org.springframework.aop.framework.CglibAopProxy)

文章插图
AbstractAutoProxyCreator类中的createProxy方法
/*** Create an AOP proxy for the given bean.*/ protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {//...ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}//寻找并注入被增强类的所有 Advisor , AdviceAdvisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);//...//创建jdk动态代理或者cglib动态代理return proxyFactory.getProxy(getProxyClassLoader()); }如果目标类是接口 用jdk动态代理 , 否则用cglib动态代理
文章插图
2.1 jdk动态代理2.1.1 创建代理类:

文章插图
2.1.2 为什么jdk代理类必须是接口:因为jdk动态代理生成的类已经继承了Proxy , 而java是单继承的 , 所以是基于jdk动态代理是基于接口的 。
jdk代理的类必须传他的接口:
@Testpublic void test_jdk_proxy2(){ClassLoader classLoader= Thread.currentThread().getContextClassLoader();IUserService target=new UserService2();IUserService proxy= (IUserService) Proxy.newProxyInstance(classLoader,target.getClass().getInterfaces() ,(obj, method, args)->{System.out.println(obj.getClass()+",这个类被代理了 。。。");return "代理返回值";});System.out.println("父类:"+proxy.getClass().getSuperclass());for (Class<?> anInterface : proxy.getClass().getInterfaces()) {System.out.println("接口:"+anInterface);}System.out.println(proxy.queryUserInfo());byte[] bytes = ProxyGenerator.generateProxyClass("UserService", target.getClass().getInterfaces());File file = new File("E:\\temp\\UserService.class") ;FileOutputStream fo = null;try {fo = new FileOutputStream(file);fo.write(bytes);fo.flush();fo.close();} catch (Exception e) {e.printStackTrace();}}如果只传接口 , 没有实现类 , 那么他代理类将不实现接口:
文章插图
如果不传接口 , 会报错:

文章插图
【Spring AOP总结】查看Proxy生成代理类的方法 , 没看懂:

文章插图
将代理类生成出来 , 会发现 , 他继承了Proxy , 实现了自定义接口 。
由于java只能是单继承 , 所以jdk代理只能是接口

文章插图

文章插图
2.1.3 调用被代理的方法:
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 {//各种校验 。。。Object retVal; //增强方法返回值// 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())) {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;}}
文章插图
ReflectiveMethodInvocation类的 proceed 方法:
每次调用 currentInterceptorIndex 加 1 , 最后一次就是执行 目标方法
org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint
->org.springframework.aop.support.AopUtils#invokeJoinpointUsingReflection
不是最后一次依次执行MethodInterceptor的实现类:

文章插图
2.2 cglib动态代理org.springframework.aop.framework.CglibAopProxy , 核心类是Enhancer:
@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {//log...try {Class<?> rootClass = this.advised.getTargetClass();Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");Class<?> proxySuperClass = rootClass;//...// 创建cglib的 Enhancer。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 ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));//得到回调方法 , 这个方法会初始化 DynamicAdvisedInterceptor , 得到拦截器链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);}}org.springframework.aop.framework.CglibAopProxy#getCallbacks 方法返回代理类所有的拦截器:Callback是MethodInterceptor的父接口

文章插图
配置了DynamicAdvisedInterceptor , 在其中会创建ReflectiveMethodInvocation , 会在调用方法时候执行

文章插图

文章插图
最终cglib会调用被增强类的构造方法 。这也是为什么cglib动态代理可以代理 实现类

文章插图
Enhancer#create生成代理类对象 。生成类是使用 ASM 进行生成 。

文章插图
用如下方法可以保存生成代理类的字节码文件:
@Testpublic void test_scan_aop_cglib() {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\temp\\cglib");ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");UserServiceCglib userService = applicationContext.getBean("userServiceCglib", UserServiceCglib.class);System.out.println("测试结果:" + userService.queryUserInfo());}可以看到生成的代理类继承了目标类 , 并且方法是final的 , 所以cglib动态代理的类不能是final的
文章插图

文章插图
3 , 注解方式需要在xml里配置 <aop:aspectj-autoproxy /> , 或者springboot用特殊的starter 。
加载了 org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator , 这个类是 AbstractAutoProxyCreator 的子类 。处理方式和上边一样 。
不甘心做码农的程序员
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
