Spring AOP总结

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/beans
        https://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="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 {
    @Override
    public 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/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://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;
@Service
public 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
@Aspect
public class UserServiceAnnotationAdvice {

    @Around(value="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 和 InvocationHandler

Interceptor拦截器接口

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 实现了 MethodInterceptor

2,xml配置方式

org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator //
org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor//切面 ,是一个 Advisor
org.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,Advice
		Advisor[] 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代理的类必须传他的接口:

    @Test
    public 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();
        }
    }

如果只传接口,没有实现类,那么他代理类将不实现接口:

如果不传接口,会报错:

查看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 :

@Override
public 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 above
        enhancer.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 进行生成。

用如下方法可以保存生成代理类的字节码文件:

    @Test
    public 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 的子类。处理方式和上边一样。

不甘心做码农的程序员