Spring之ProxyFactory的实现

Spring中ProxyFactory动态代理

0、概述

其实Spring中的AOP大大简化了我们的开发。画了个流程图总结一下,如下所示

1、ProxyFactory

这个类是由Spring框架提供的,提供的目的是为了屏蔽掉是使用JDK的接口动态代理还是使用CGLIB的类代理实现方式的具体细节,使用ProxyFactory来做一个统一处理。

使用ProxyFactory可以做到更加精细的控制!比如说具体到类中的哪个方法、对同一类有相同方法或者是功能的做统一处理。

分别来看下对应的案例。

使用JDK

使用JDK的话必须要有接口

public interface UserServiceInter {
	void test();
}

public class UserService implements UserServiceInter {

	public void test() {
		System.out.println("test...");
	}

	public void hello() {
		System.out.println("test...");
	}
}

对应的测试案例:

public class ProxyFactoryTest1 {
	public static void main(String[] args) {
		UserServiceInter target = new UserService();
		ProxyFactory proxyFactory = new ProxyFactory();
		// 工厂代理的对象
		proxyFactory.setTarget(target);
         // 设置接口
		proxyFactory.addInterface(UserServiceInter.class);
		// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
		proxyFactory.addAdvice(new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before...");
				Object result = invocation.proceed();
				System.out.println("after...");
				return result;
			}
		});
		// 生成代理对象!可以做到有条件的来生成得到对应的对象
		UserServiceInter targetProxy = (UserServiceInter) proxyFactory.getProxy();
		targetProxy.test();
	}
}

使用CGLIB

还是上面的类,但是这里的测试代码中需要修改一下,去掉添加的接口,如下所示:

public class ProxyFactoryTest1 {
	public static void main(String[] args) {
		UserService target = new UserService();
		ProxyFactory proxyFactory = new ProxyFactory();
		// 工厂代理的对象
		proxyFactory.setTarget(target);
		// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
		proxyFactory.addAdvice(new MethodInterceptor() {
			@Nullable
			@Override
			public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
				System.out.println("before...");
				Object result = invocation.proceed();
				System.out.println("after...");
				return result;
			}
		});
		// 生成代理对象!可以做到有条件的来生成得到对应的对象
		UserService targetProxy = (UserService) proxyFactory.getProxy();
		targetProxy.test();
	}
}

2、动态代理对于JDK还是CGLIB的选择

那么问题来了,Spring利用ProxyFactory工具类是怎么知道到底是使用JDK动态代理还是CGLIB动态代理呢?

只需要关注如下代码:

ProxyFactory proxyFactory = new ProxyFactory();
...
proxyFactory.getProxy();

进入到源码中查看:

	public Object getProxy() {
		// 然后调用JDK或者是CGLIB对应的得到代理对象的方法
		return createAopProxy().getProxy();
	}

点击进入到createAopProxy方法中来:

	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		// 默认为DefaultAopProxyFactory
		// 先选定使用哪种代理方式:JDK还是CGLIB
		return getAopProxyFactory().createAopProxy(this);
	}

注意这里的this就是上面的proxyFactory对象,然后进入到createAopProxy方法中来

是不是指定的GraalVM虚拟机这个不由我们来进行控制,我们只需要关注下面的三个条件判断。

1、判断使用JDK还是CGLIB的条件

1.1、是否设置优化

为了需要设置优化呢?因为早期Spring认为CGLIB动态代理的效率比JDK动态代理效率高。但是随着JDK版本升级,二者性能上相差不大。

可以使用ProxyFactory来进行设置

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setOptimize(true);

optimize属性默认值是false,可以设置为true。

1.2、是否指定了代理目标类

是对类代理还是对接口来进行代理。proxyTargetClass属性默认为false

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setProxyTargetClass(true);

1.3、是否指定代理接口

是否在ProxyFactory对象中设置了接口,如果没有设置了接口,可以使用JDK动态代理

		DService dService = new DService();
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.setTarget(dService);
		proxyFactory.addInterface(MyInterface.class);
		proxyFactory.addAdvisor(new MyAdvisor3());
		MyInterface proxy = (MyInterface) proxyFactory.getProxy();
		proxy.caculate();

需要注意的是,上面的各种设置可以组合搭配起来进行使用。注意调用顺序

因为三个条件是或者判断,如果前面的满足了条件,后面的判断就不会进入了。

知道了使用哪个动态代理之后,那么就应该来创建对应的代理对象了。

2、具体代理选择

2.1、JDK动态代理

那么先来看下ProxyFactory使用JDK动态代理之后的效果,在org.Springframework.aop.framework.JdkDynamicAopProxy#getProxy()

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

看下具体的实现

public Object getProxy(@Nullable ClassLoader classLoader) {
  if (logger.isTraceEnabled()) {
    logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
  }
  // this实现了InvocationHandler,表示调用当前类中的invoke方法来进行实现
  return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

看下具体的实现过程

具体说来,分为以下几个步骤:

  • 1、获取得到被代理对象的来源;
  • 2、如果代理对象调用的是Equals、HashCode等方法的时候,直接调用不走代理过程;
  • 3、是否需要将代理对象暴露到当前线程中来。在代理对象执行方法的时候,可以在方法内部获取得到代理对象;
  • 4、执行被代理对象来源中的getTarget方法,获取得到被代理对象,即target对象;
  • 5、找到ProxyFactory对象中设置的拦截器并执行拦截器中的拦截方法;
  • 6、做一些清理工作;如:保存到线程中的代理对象等;

因为可以在ProxyFactory对象中设置了多个通知和匹配表达式,切入点表达式表示是横向针对哪些切面来进行拦截,对于通知来说,对于拦截到的面增强的逻辑是什么。

2.2、案例

接口和实现类:

public interface MyInterface {
	void caculate();
}

public class DService implements MyInterface {
	@Override
	public void caculate() {
		System.out.println("caculate execute............");
	}
}

对应的Advisor如下:

public class MyAdvisor3 implements PointcutAdvisor {
  @Override
  public Advice getAdvice() {
    return new MyAdvice();
  }

  @Override
  public boolean isPerInstance() {
    return false;
  }

  @Override
  public Pointcut getPointcut() {
    // 只匹配方法名称是caculate的方法名称
    return new StaticMethodMatcherPointcut() {
      @Override
      public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().equals("caculate");
      }
    };
  }
}

对应的Advice如下所示:

public class MyAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("hello,MyAdvice#before");
	}
}

对应的测试代码如下:

DService dService = new DService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dService);
proxyFactory.addInterface(MyInterface.class);
proxyFactory.addAdvisor(new MyAdvisor3());
proxyFactory.addAdvice(new MethodBeforeAdvice() {
  @Override
  public void before(Method method, Object[] args, Object target) throws Throwable {
    System.out.println("before");
  }
});
MyInterface proxy = (MyInterface) proxyFactory.getProxy();
proxy.caculate();

3、找到拦截器并执行方法

重点就来到了获取得到拦截器以及执行拦截器中的方法

来到org.Springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice中来,那么看看这里是如何来进行寻找对应的拦截器的。(从方法名称上来看,这里是分成了两种:Interceptor和DynamicInterceptionAdvice,在之后会分别来进行介绍)

看下面代码:

Advisor[] advisors = config.getAdvisors();

首先会将ProxyFactory对象中设置的Advisor拿出来

3.1、什么是Advisor?

那么什么是Advisor呢?Advisor是由PointCut和Advice组合而成的。

PointCut首先来判断哪些方法可以被代理,而Advice表示的是代理的逻辑是什么?

3.2、什么是PointCut?

PointCut表示的是切入点表达式,从横向上来进行匹配,要对哪些方法来进行切割,看下接口:

public interface Pointcut {
	ClassFilter getClassFilter();
	MethodMatcher getMethodMatcher();
	Pointcut TRUE = TruePointcut.INSTANCE;
}

存在着类过滤器和方法过滤器

  • 类过滤器指的是如果指定的类条件进行筛选,满足之后才会来进行下一步;
  • 方法匹配器指的是在类过滤器满足了的情况下,对方法的各种条件进行筛选,满足了进行下一步

对应的结构如下所示:

类过滤器
public interface ClassFilter {
   boolean matches(Class<?> clazz);
   ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

match方法是Class,表示可以从类信息上来进行判断。

方法匹配器
public interface MethodMatcher {
	boolean matches(Method method, Class<?> targetClass);
	boolean isRuntime();
	boolean matches(Method method, Class<?> targetClass, Object... args);
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

在方法匹配器中有两个match方法,为方法重载。

  • 对于第一个matches来说,传入进去的是目标类和方法对象;
  • 对于第二个matches来说,只有在isRuntime=true的情况下,才会执行第二个matches方法,比第一个多一个参数。

3.3、Advice和Advisor的关系

代码如下所示:

UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
// 工厂代理的对象
proxyFactory.setTarget(target);
// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
proxyFactory.addAdvice(new MethodInterceptor() {
  @Nullable
  @Override
  public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
    System.out.println("before...");
    Object result = invocation.proceed();
    System.out.println("after...");
    return result;
  }
});

据我们所示,我们添加的应该是Advisor,而不是Advice。所以Advisor和Advice之间是什么关系呢?

实际上,在代码proxyFactory.addAdvice方法中,在源码中如下所示:

// 将Advice封装成DefaultPointcutAdvisor对象,里面的PointCut默认是匹配所有的
addAdvisor(pos, new DefaultPointcutAdvisor(advice));

将advice包装成了一个默认的DefaultPointcutAdvisor。那么回到上面所说的,对于Advisor来说,都是由PointCut和Advice组成的,那么现在有了Advice,那么PointCut是什么呢?

在DefaultPointcutAdvisor中可以看到

	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}

PointCut是Pointcut.TRUE,那么这个对象肯定也有类过滤器和方法匹配器,那么看一下对应的匹配规则

	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}
	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

观察下二者中的match方法,发现对应的match方法如下所示:

ClassFilter中的匹配规则:

	public boolean matches(Class<?> clazz) {
		return true;
	}

MethodMatcher中的匹配规则:

	public boolean matches(Method method, Class<?> targetClass) {
		return true;
	}

说明对于默认的DefaultPointcutAdvisor来说,对于类过滤器和方法匹配器来说,是不会进行任何过滤拦截即可得到代理对象的,只会调用对应的增强逻辑来完成。

3.4、Advisor和MethodIntecepter关系

看下下面的代码:

proxyFactory.addAdvisor(new MyAdvisor3());
proxyFactory.addAdvice(new MethodBeforeAdvice() {
  @Override
  public void before(Method method, Object[] args, Object target) throws Throwable {
    System.out.println("before");
  }
});

我们在上面看到一行代码如下所示:

Advisor[] advisors = config.getAdvisors();

那么对于Advisor和MethodIntecepter关系是什么呢?

3.5、具体匹配操作

下面开始对得到的Advisor来进行匹配操作

1、PointcutAdvisor解析过程

这里将Advisor分为了三类:PointcutAdvisorIntroductionAdvisor以及其他类型

在上面的代码中

proxyFactory.addAdvisor(new MyAdvisor3());

这里的MyAdvisor3就是一个PointcutAdvisor

public class MyAdvisor3 implements PointcutAdvisor {
	@Override
	public Advice getAdvice() {
		return new MyAdvice();
	}

	@Override
	public boolean isPerInstance() {
		return false;
	}

	@Override
	public Pointcut getPointcut() {
		return new StaticMethodMatcherPointcut() {
			@Override
			public boolean matches(Method method, Class<?> targetClass) {
				return method.getName().equals("caculate");
			}
		};
	}
}

当前的Advisor中就存在着Advice+Pointcut

  • 对于PointCut来说,方法过滤器没有处理,只对方法来进行处理,只要方法名称命名是caculate即可;
  • 对于Advice来说,增强的逻辑如下所示:
public class MyAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("hello,MyAdvice#before");
	}
}

那么看一下对PointcutAdvisor的解析过程:

首先调用类过滤器中的matches方法,匹配之后,再调用方法匹配器的matches方法,如果二者都匹配了,会将advisor转换成MethodInterceptor对象。

具体是如何来进行转换的待会儿再看,那么会再次判断方法匹配器中的isRuntime方法,如果返回为true,那么会将MethodInterceptor包装一层,然后将方法中的参数传入进去,创建一个InterceptorAndDynamicMethodMatcher对象放入到集合中去。

2、将Advice转换成MethodInterceptor操作

那么接下来看下,是如何将advice转换成MethodInterceptor的,首先获取得到advisor中的advice对象:

如果advice是MethodInterceptor类型的,那么直接添加到interceptors集合中来。

如果advice是MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice的一种,那么将会调用AdvisorAdapter适配器来进行转换。

看一下AdvisorAdapter中的两个方法:

public interface AdvisorAdapter {
    // 判断是否支持
	boolean supportsAdvice(Advice advice);
    // 如果支持就将Advisor转换成MethodInterceptor
	MethodInterceptor getInterceptor(Advisor advisor);
}

可以看下具体的转换过程:

MethodBeforeAdviceAdapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {	
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}

}

如果Advice是MethodBeforeAdvice类型的,那么将调用getInterceptor将Advisor转换成MethodInterceptor,看下具体的转换过程。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
	private final MethodBeforeAdvice advice;
	public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}
	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		// 首先先执行代理之前的逻辑
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		// 然后执行链路中下一个Advice中的逻辑
		return mi.proceed();
	}
}

因为在MethodInterceptor是存在着invoke方法的,可以看到在代理方法执行之前。

AfterReturningAdviceAdapter
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
  
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof AfterReturningAdvice);
	}
  
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
		return new AfterReturningAdviceInterceptor(advice);
	}
}
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;

	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}


	@Override
	@Nullable
	public Object invoke(MethodInvocation mi) throws Throwable {
		// 首先获取得到结果,然后又会执行到下面的代码
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}

首先来执行获取得到结果之后,才会执行最终的方法。

ThrowsAdviceAdapter
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
	@Override
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof ThrowsAdvice);
	}
	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		return new ThrowsAdviceInterceptor(advisor.getAdvice());
	}
}

看下org.Springframework.aop.framework.adapter.ThrowsAdviceInterceptor中的方法

要求方法名称必须得是:afterThrowing,而且方法参数个数必要要是1个或者是4个,而且最后一个参数必须要是Throwable异常类型及其子类。

在exceptionHandlerMap中key对应的是异常类型,value为对应的方法对象;然后看下对应的invoke方法

在调用结果中出现了异常,那么会根据异常类型从exceptionHandlerMap中来进行查找,找到了对应的方法

至此,大致上清晰了。

3.5、代理对象执行方法

那么再次回到上面,这个时候已经找到了所有的方法拦截器,但是还没有开始进行调用,下面看看调用的时候需要来做什么事情。

// 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,那么看下ReflectiveMethodInvocation中的proceed方法

  • 1、currentInterceptorIndex的初始值是-1,如果拦截器链路中什么都没有的话,那么会执行连接点方法;
  • 2、开始按照ProxyFactory中添加的MethodIntercepter的顺序开始调用的对应的方法拦截器中的方法;这里没有看到任何for循环,但是在ProxyFactory是可以添加多个的;
  • 3、按照分类条件来进行执行。①如果是InterceptorAndDynamicMethodMatcher类型的,则根据MethodMatcher中match方法进行匹配,这个时候取决于isRuntime,来决定是调用两个参数的match方法还是三个参数的match方法;②如果是MethodInterceptor类型的,则会调用对应的invoke方法

那么没有看到任何for循环,如何实现多个方法拦截器的多个调用的呢?

可以看到两个地方:invoke(this)和proceed()方法,对于proceed()方法来说,在调用的时候肯定就是递归调用,会再次来到当前方法中调用。

而对于invoke方法来说,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor,又都会调用到proceed()方法这里来。

所以最终执行完成了拦截器中的整个通知方法。

非常清晰明白,至此,利用ProxyFactory产生的代理对象调用方法过程结束。

4、CGLIB动态代理

CGLIB的和JDK的非常类似,在org.Springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)方法中

// 获取和被代理类所匹配的Advisor=====advice+pointcut
Callback[] callbacks = getCallbacks(rootClass);

在getCallbacks方法中,使用下面这行代码得到对应的Callback

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

然后会调用DynamicAdvisedInterceptor类中的intercept方法来进行使用:

和ProxyFactory中的动态代理类似

5、小结

通过上面的JDK的动态代理,有以下总结:

  • 1、可以在代码运行过程中,拿到代理对象。前提是在proxyfactory中设置exposeProxy为true;通过AopContext.getCurrentProxy可以获取得到;
  • 2、在proxyfactory中可以获取得到多个advise,将advice适配成对应的方法拦截器,然后在调用的时候依次执行里面被代理方法的逻辑;执行完链路的最后一个时,会来执行最终的被代理方法(如果没有设置isRuntime的时候);如果设置了runTime方法,那么就会立即来执行其中的逻辑;
  • 3、advice也会被封装成一个advisor,用一个DefaultPointcutAdvisor来进行封装,只不过封装的ClassFilter和MethodMatcher都会为true,也就是说只要是代理对象设置好了,都会来执行这里面的方法;
  • 4、自定义PointcutAdvisor的时候,可以指定对类的筛选、对方法的筛选,以及筛选匹配成功之后的被代理方法的逻辑。
  • 5、ProxyFactory利用CGLIB来做动态代理以及利用JDK来做动态代理的情况;

画个图总结一下对应的流程:

posted @ 2022-06-25 20:44  雩娄的木子  阅读(220)  评论(0编辑  收藏  举报