源码分析:Spring是如何跟JDK动态代理结合

Posted Jeff.S

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析:Spring是如何跟JDK动态代理结合相关的知识,希望对你有一定的参考价值。

从jdk代理的demo说起

首先看下基于JDK的动态代理是怎么实现的,先来一个简单的demo。

package com.jeff.study.proxy;

public interface PersonService {
    void save(String user) throws Exception;
}
package com.jeff.study.proxy;

public class PersonServiceImpl implements PersonService {

    private String user = null;

    public PersonServiceImpl() {
    }

    public PersonServiceImpl(String user) {
        this.user = user;
    }

    public final void save(String personid) throws Exception {
        if (user != null) {
            System.out.println("============" + personid);
        } else {
            System.out.println("------------" + personid);
        }
    }

    public String getUser() {
        return user;
    }

}
package com.jeff.study.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyFactory implements InvocationHandler {

    private Object targetObject;

    //根据目标对象生成代理对象
    public Object createProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        Object proxyObj = Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                this.targetObject.getClass().getInterfaces(), this);
        return proxyObj;
    }

    /**
     * 当生成的代理对象调用相应的业务方法时,就会回调这个方法,
     * 并根据目标对象的user是否为空判断是否执行目标对象的业务方法。
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {//环绕通知
        PersonServiceImpl bean = (PersonServiceImpl) this.targetObject;
        Object result = null;
        if (bean.getUser() != null) {
            try {
                //......Advice--->前置通知
                System.out.println("before advice......");
                result = method.invoke(targetObject, args);
                System.out.println("after advice......");
                //......AfterAdvice--->后置通知
            } catch (Exception e) {
                //......ExceptionAdvice--->例外通知
                System.out.println("ExceptionAdvice......");
            } finally {
                //......finallyAdvice--->最后通知
                System.out.println("finally Advice......");
            }

        }
        return result;
    }

    public static void main(String[] args) throws Exception {
        JDKProxyFactory factory = new JDKProxyFactory();
        Object proxyObj = factory.createProxyInstance(new PersonServiceImpl("zs"));
        ((PersonService) proxyObj).save("888");
    }
}

可以看出基于JDK的动态代理是需要代理目标对象是实现了接口的,实际上JDK代理类就是代理接口基础上的一个代理实现类。

 private Object targetObject;

    //根据目标对象生成代理对象
    public Object createProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        Object proxyObj = Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
                this.targetObject.getClass().getInterfaces(), this);
        return proxyObj;
    }

在我们的demo中JDKProxyFactory实现了InvocationHandler接口,这个接口中只有一个方法就是invoke方法。用来执行目标对象的目标方法,执行的方式是反射。这个invoke方法在代理对象执行目标方法时触发调用。

SpringAOP中的JDK动态代理

接下来,让我们看下SpringAOP里的jdk动态代理:

org.springframework.aop.framework.JdkDynamicAopProxy

在这里插入图片描述
通过上图可以了解到JdkDynamicAopProxy通过实现接口AopProxy的getProxy方法获取代理对象。来看下这个方法,可以看出跟我们demo中实现的一模一样。

	@Override
	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		//代理的接口,比如PersonService
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

那么,这个JdkDynamicAopProxy#getProxy是被谁调用的呢?答案是ProxyFactory类,看下边。

//ProxyFactory.java
/**
	 * Create a new proxy according to the settings in this factory.
	 * <p>Can be called repeatedly. Effect will vary if we've added
	 * or removed interfaces. Can add and remove interceptors.
	 * <p>Uses a default class loader: Usually, the thread context class loader
	 * (if necessary for proxy creation).
	 * @return the proxy object
	 */
	public Object getProxy() {
		return createAopProxy().getProxy();
	}

既然是在ProxyFactory的getProxy方法中调用到,那么你一定可以想到createAopProxy()这个方法在这里返回的就是JdkDynamicAopProxy了。其实ProxyFactory的这个createAopProxy方法是由其父类ProxyCreatorSuppot提供的。
在这里插入图片描述

//ProxyCreatorSupport.java
	/**
	 * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
	 * create an AOP proxy with {@code this} as an argument.
	 */
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}

通过AOP代理Factory创建代理

我们重点分析下ProxyCreatorSupport#createAopProxy方法,看到是先获取一个AopProxyfactory来创建代理,同时传入当前对象ProxyCreatorSupport,这是因为它继承了ProxyConfig类,封装了创建代理的配置属性。
getAopProxyFactory()方法返回的是DefaultAopProxyFactory类,这是一个SpringAOP提供的默认代理类创建Factory实现,作用是创建JDK代理或者cglib代理。
创建规则就不多说了,如果目标对象实现了接口那就默认是JDK代理。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

	/**
	 * Determine whether the supplied {@link AdvisedSupport} has only the
	 * {@link org.springframework.aop.SpringProxy} interface specified
	 * (or no proxy interfaces specified at all).
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}

}

经过一波三折终于获取到了JdkDynamicAopProxy,那么ProxyFactory#getProxy方法就可以获取代理了。前边我们已经说过了JdkDynamicAopProxy中是如何根据接口生成目标对象代理实现类了,这里不再赘述。
在这里插入图片描述

JDK代理对目标方法切面的拦截

知道了如何获取jdk基于接口的代理对象,那么当代理对象执行目标方法时又会发生什么?当然是执行JdkDynamicAopProxy#invoke方法了。在执行过程中,目标方法会被AOP切面拦截,也就是会执行前置通知,后置通知,环绕通知等等拦截执行。我们看下是怎么做到的?

package com.jeff.study.spring.aop;

import com.jeff.study.spring.aop.interceptor.EchoServiceMethodInterceptor;
import com.jeff.study.spring.aop.pointcut.EchoServicePointcut;
import com.jeff.study.spring.aop.service.DefaultEchoService;
import com.jeff.study.spring.aop.service.EchoService;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;

/**
 * Pointcut接口Demo
 *
 * @Date 2021/1/10 9:20 下午
 * @Author jeff.sheng
 */
public class PointcutDemo {

    public static void main(String[] args) {
//        EchoServiceStaticPointcut echoServicePointcut = new EchoServiceStaticPointcut("echo", EchoService.class);
        EchoServicePointcut echoServicePointcut = new EchoServicePointcut();
        //将Pointcut适配成Advisor,也就是说advice和pointcut之间需要一个advisor来做一个承载
        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(echoServicePointcut, new EchoServiceMethodInterceptor());

        DefaultEchoService defaultEchoService = new DefaultEchoService();
        ProxyFactory proxyFactory = new ProxyFactory(defaultEchoService);
        //添加advisor
        proxyFactory.addAdvisor(defaultPointcutAdvisor);
        proxyFactory.addAdvice(new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, Object target) throws Throwable {
                System.out.printf("[MethodBeforeAdvice] 当前将要执行的方法是:%s , 参数key:[%s]\\r\\n", method.getName(), args[0]);
            }
        });
        proxyFactory.addAdvice(new AfterReturningAdvice() {
            @Override
            public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
                System.out.printf("[AfterReturningAdvice] 当前将要执行的方法是:%s , 参数key:[%s],value:[%s]\\r\\n", method.getName(), args[0], returnValue);
            }
        });
        //获取代理对象
        EchoService echoService = (EchoService) proxyFactory.getProxy();
        System.out.println(echoService.echo("hello,world"));
    }
}

ackage com.jeff.study.spring.aop.interceptor;

import com.jeff.study.spring.aop.service.EchoService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * @Description 实现 {@link EchoService} 的拦截器对象
 * @Date 2021/1/1 11:03 上午
 * @Author jeff.sheng
 * @see MethodInterceptor 继承了{@link org.aopalliance.intercept.Interceptor}
 * @see org.aopalliance.intercept.Interceptor 继承了 {@link org.aopalliance.aop.Advice}
 */
public class EchoServiceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("[EchoServiceMethodInterceptor] 当前执行的方法是:" + methodInvocation.getMethod());
        return methodInvocation.proceed();
    }
}

代码中我只设置了EchoServiceMethodInterceptor、MethodBeforeAdvice、AfterReturningAdvice三个Advive。
注意,EchoServiceMethodInterceptor实现了MethodInterceptor接口,而MethodInterceptor最终也是实现了Advice,这里不再解释。
当我们调用ProxyFactory#addAdvice方法时,advice会封装为DefaultPointcutAdvisor并保存在父类AdvisedSupport私有属性advisors列表中。

//AdvisedSupport.java
/**
	 * List of Advisors. If an Advice is added, it will be wrapped
	 * in an Advisor before being added to this List.
	 */
	private List<Advisor> advisors = new ArrayList<>();

这个AdviseSupport会作为JdkDynamicAopProxy的构造器参数传入。在JdkDynamicAopProxy#invoke执行时会作为一个chain拦截器链被执行。


	/**
	 * Construct a new JdkDynamicAopProxy for the given AOP configuration.
	 * @param config the AOP configuration as AdvisedSupport object
	 * @throws AopConfigException if the config is invalid. We try to throw an informative
	 * exception in this case, rather than let a mysterious failure happen later.
	 */
	public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}

过滤器模式的实践

看下invoke方法中如何执行Advice的?这里封装了一个ReflectiveMethodInvocation对象来执行具体逻辑(参看proceed方法)。

	// 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();
//ReflectiveMethodInvocation.java
@Override
	@Nullable
	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;
			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);
		}
	}

这里用到了过滤器模式,在当前例子中过滤器有三个,封装在interceptorsAndDynamicMethodMatchers列表中。
在这里插入图片描述
先执行环绕通知,再执行前置通知,然后是后置通知。执行完毕后满足ReflectiveMethodInvocation#proceed方法的退出条件。反射执行目标对象的目标方法,return返回结果。

	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
/**
	 * Invoke the joinpoint using reflection.
	 * Subclasses can override this to use custom invocation.
	 * @return the return value of the joinpoint
	 * @throws Throwable if invoking the joinpoint resulted in an exception
	 */
	@Nullable
	protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

到此,关于JDK动态代理如何执行AOP切面的拦截流程已经完成,其实cglib的流程也是一样的,不同的是生成代理的方式不一样,cglib是根据asm字节码来生成目标对象的代理子类,无所谓是否实现接口。

以上是关于源码分析:Spring是如何跟JDK动态代理结合的主要内容,如果未能解决你的问题,请参考以下文章

源码分析:Spring是如何跟JDK动态代理结合

Spring杂谈 | JDK动态代理源码分析

javaJDK动态代理源码分析 到生成字节码

基于JDK动态代理实现的Spring AOP源码学习

JDK动态代理源码分析

Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口