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

Posted talk.push

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动态代理源码分析

2020-07-26 带着疑问看源码 -- springboot aop默认采用啥动态代理机制