spring源码篇(七)AOP原理

2023-10-24 18:45:15 浏览数 (1)

前言

spring是如何实现AOP切面的,从原理上来说是动态代理,那么怎样去实现这个动态代理呢就是本篇的内容。

动态代理

spring中用到的动态代理有两种:JDK的动态代理和CGLIB的动态代理。

jdk的动态代理

要代理的对象,需要实现一个接口。

代码语言:javascript复制
public interface IPlay {

	void play();
}
代码语言:javascript复制
public class PlayGame implements IPlay {
	@Override
	public void play() {
		System.out.println("play 。。。");
	}
}
代码语言:javascript复制
public class PlayProxy implements InvocationHandler {

	private IPlay play;

	public PlayProxy(IPlay play) {
		this.play = play;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("代理。。。");
		method.invoke(play, args);
		return null;
	}
}
代码语言:javascript复制
// 要代理的对象
IPlay play = new PlayGame();
// 代理处理
PlayProxy playProxy = new PlayProxy(play);
// 生成代理的对象
IPlay instance =(IPlay) Proxy.newProxyInstance(play.getClass().getClassLoader(), new Class[]{IPlay.class}, playProxy);
instance.play();

CGLIB

代码语言:javascript复制
public class PlayCglibProxy implements MethodInterceptor {

	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("cglib proxy。。。");
		methodProxy.invokeSuper(o, objects);
		return null;
	}
}
代码语言:javascript复制
Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(PlayGame.class);
		enhancer.setCallback(new PlayCglibProxy());
		PlayGame o = (PlayGame) enhancer.create();
		o.play();

区别

jdk的动态代理只能为实现了接口的类做代理,面向接口;

而CGLIB动态代理是对字节码进行修改和动态生成的,面向方法。

AOP的动态代理

spring对jdk的动态代理和cglib代理,抽象出一个代理工程ProxyFactory,而其中有几个概念:

advice:代理的逻辑

pointcut:切点

advisor:advice pointcut;表示advice可以用在什么地方

使用spring提供的ProxyFactory类创建一个aop代理:

代码语言:javascript复制
IPlay play = new PlayGame();
ProxyFactory proxyFactory = new ProxyFactory();

proxyFactory.setTarget(play);
proxyFactory.addAdvice(new MethodBeforeAdvice() {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("method before。。。");
    }
});
IPlay proxy = (IPlay) proxyFactory.getProxy();
proxy.play();

第二种写法:

代码语言:javascript复制
@Component
public class CustomAdvisor implements PointcutAdvisor {
	@Override
	public Pointcut getPointcut() {
		NameMatchMethodPointcut methodPointcut = new NameMatchMethodPointcut();
		methodPointcut.addMethodName("play");
		return methodPointcut;
	}

	@Override
	public Advice getAdvice() {
		MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("执行方法前" method.getName());
			}
		};

		return methodBeforeAdvice;
	}

	@Override
	public boolean isPerInstance() {
		return false;
	}
}
代码语言:javascript复制
	IPlay play = new PlayGame();
		ProxyFactory proxyFactory = new ProxyFactory();

		proxyFactory.setTarget(play);
//		proxyFactory.addAdvice(new MethodBeforeAdvice() {
//			@Override
//			public void before(Method method, Object[] args, Object target) throws Throwable {
//				System.out.println("method before。。。");
//			}
//		});
		proxyFactory.addAdvisor(new CustomAdvisor());
		IPlay proxy = (IPlay) proxyFactory.getProxy();
		proxy.play();

那我们跟进源码看看:

代码语言:javascript复制
public Object getProxy() {
		return createAopProxy().getProxy();
	}
代码语言:javascript复制
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		// 如果设置的targetClass是一个接口,会使用jdk动态代理
		// 默认情况下(optimize为false, isProxyTargetClass为false), ProxyFactory添加了接口时,也会使用jdk动态代理

		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				// 抛异常 省略。。。
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

从上面代码看,我们可以得出结论,当我们设置了接口类时,它就会使用jdk动态代理,没有设置接口,使用cglib。

JdkDynamicAopProxy

代码语言:javascript复制
IPlay play = new PlayGame();
		ProxyFactory proxyFactory = new ProxyFactory();

		proxyFactory.setTarget(play);
//		proxyFactory.setTargetClass(PlayGame.class);
// 这里设置接口,不设置class
		proxyFactory.setInterfaces(IPlay.class);
		proxyFactory.addAdvice(new MethodBeforeAdvice() {
			@Override
			public void before(Method method, Object[] args, Object target) throws Throwable {
				System.out.println("method before。。。");
			}
		});
		IPlay proxy = (IPlay) proxyFactory.getProxy();
		proxy.play();
代码语言:javascript复制
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: "   this.advised.getTargetSource());
		}
		// 获取生成代理对象所需要实现的接口
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);

		// 判断这些接口中有没有定义equals方法,hashcode方法
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 针对所指定的接口生成代理对象,包括用户所添加的接口以及SpringProxy、Advised、DecoratingProxy
		// 所以生成的代理对象可以强制转换成任意一个接口类型
    // 因为JdkDynamicAopProxy实现了InvocationHandler接口,所以这里是this)
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

jdk动态代理需要接口,所以这里是获取接口的

代码语言:javascript复制
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);

这个方法,我们分两个部分来看

代码语言:javascript复制
  // 我们添加了接口,那么这里会获取到接口
// proxyFactory.setInterfaces(IPlay.class) 设置是添加到了属性 interfaces
// 这里getProxiedInterfaces是从属性:interfaces 拿的
		Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
		// 如果ProxyFactory中没有指定接口,就查看targetClass是不是接口,如果是则添加这个接口
		if (specifiedInterfaces.length == 0) {
			// 这里是,在发现没有设置接口时,做的一个后手准备
            // proxyFactory.setTarget(play) -》是设置targetSource
            // getTargetClass -》targetSource.getTargetClass(),
			Class<?> targetClass = advised.getTargetClass();
			if (targetClass != null) {
				if (targetClass.isInterface()) {
					advised.setInterfaces(targetClass);
				}
				else if (Proxy.isProxyClass(targetClass)) {
					advised.setInterfaces(targetClass.getInterfaces());
				}
				specifiedInterfaces = advised.getProxiedInterfaces();
			}
		}

所以上面这段基本上就是对必要属性的处理,因为我们要用jdk的动态代理,原始对象就必须实现一个接口,那么这里首先是判断我们有设置接口没,如果没有,它也不能出一个弹窗告诉你设置,所以它自己去找,那么它找的话,只能从原始对象去找,因为它猜测,接口都会忘记设置的人,其他属性也不能保证设置了,所以从最原始的对象去找才是最有效的,当然也只有它自己在用。

代码语言:javascript复制
		// 除开用户添加的接口之后,Spring还要添加几个默认的接口SpringProxy、Advised、DecoratingProxy
		boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
		boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
		boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
		int nonUserIfcCount = 0;
		if (addSpringProxy) {
			nonUserIfcCount  ;
		}
		if (addAdvised) {
			nonUserIfcCount  ;
		}
		if (addDecoratingProxy) {
			nonUserIfcCount  ;
		}
		Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length   nonUserIfcCount];
		System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
		int index = specifiedInterfaces.length;
		if (addSpringProxy) {
			proxiedInterfaces[index] = SpringProxy.class;
			index  ;
		}
		if (addAdvised) {
			proxiedInterfaces[index] = Advised.class;
			index  ;
		}
		if (addDecoratingProxy) {
			proxiedInterfaces[index] = DecoratingProxy.class;
		}
		return proxiedInterfaces;

上面这段就是添加3个默认的接口类型:SpringProxy、Advised、DecoratingProxy,这也意味着我们得到的代理对象,也可以强转为这3个接口中的一个,对我们也没多大用处。

在看invoke方法:

代码语言:javascript复制
	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;

			//  advised就是ProxyFactory
			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// 原始对象
             // targetSource分为两种:SingletonTargetSource、EmptyTargetSource
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// 根据当前方法获取对应的拦截器链
			// 就是一开始我们添加的advice
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
                // 这里的参数对应jdk动态代理里invoke方法里的参数,然后chain是拦截器链,因为它有多个拦截操作。
				// proxy ->代理对象
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// 执行操作
				retVal = invocation.proceed();
			}

			// 省略。。。
			return retVal;
		}
		finally {
			// 省略。。。
		}
	}
代码语言:javascript复制
public Object proceed() throws Throwable {
    // 判断当前的拦截方法执行完没
		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 {
				return proceed();
			}
		}
		else {
			// 执行对应的invoke方法
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

MethodBeforeAdviceInterceptor#invoke

代码语言:javascript复制
public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    // 这个方法又到上面的 proceed
    // 但是proceed方法,一开始有一个判断,判断拦截方法执行完没
		return mi.proceed(); 
	}

这里有一个递归的思想在里面,每当进入这个proceed方法,就会去执行拦截链里的方法,比如拦截链里有3个:before、after、return,那么第一次它只取一个,第一次取before,然后执行invoke后,又回到了proceed,这时还有2个方法没执行,然后它判断拦截链里的方法执行完没,它一看,还有,然后再取一个,在执行invoke方法,再执行proceed方法,直到拦截链里是空的。

到这里我想起之前在《mybatis解读篇》中的插件原理有介绍过,插件的原理,mybatis里的是直接invoke,返回proxy(代理对象),然后再代理,和这里的完全不一样,这其中的优劣在哪?

然后在执行原始对象方法的是在这里:

代码语言:javascript复制
protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}
代码语言:javascript复制
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
			throws Throwable {
// 省略。。。
			ReflectionUtils.makeAccessible(method);
			return method.invoke(target, args); 
		// 省略。。。
	}

ObjenesisCglibAopProxy

和jdk动态代理一样,这个方法依旧是先生成一个CGLIB的代理对象

代码语言:javascript复制
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating CGLIB proxy: "   this.advised.getTargetSource());
		}

		try {
			Class<?> rootClass = this.advised.getTargetClass();	//拿到被代理的类
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

			Class<?> proxySuperClass = rootClass;
			if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass, classLoader);

			// CGLIB的配置
			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 ClassLoaderAwareGeneratorStrategy(classLoader));

			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);
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of "   this.advised.getTargetClass()  
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

调用业务方法时,触发CGLIB的代理方法

代码语言:javascript复制
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				// 如果某个Advisor中的Advice没有实现MethodInterceptor接口,那么则会把这个Advice适配成MethodInterceptor
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 直接调用目标方法
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					// proxy: 代理类产生的代理对象
					// target: 被代理的目标对象
					// method: 当前正在执行的目标类中的方法对象
					// args: 当前正在执行的方法参数
					// targetClass: 被代理的类
					// chain: 和当前被代理的类和正在执行的方法匹配的所有的advisor
					// methodProxy:当前正在执行的代理类中的方法对象
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

然后这里的proceed方法,就和jdk里执行的是一样的。

自动代理

spring提供了自动代理功能,即只要生成一个bean注册到容器就行了。

有两种方式配置。

BeanNameAutoProxyCreator

例子

第一种,BeanNameAutoProxyCreator。

代码语言:javascript复制
@Component
public class PlayGame implements IPlay {
	@Override
	public void play() {
		System.out.println("play 。。。");
	}
}
代码语言:javascript复制
@Component
public class PlayGame2{

	public void play() {
		System.out.println("play 2...");
	}
}
代码语言:javascript复制
	@Bean
	public BeanNameAutoProxyCreator creator(){
		BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
		beanNameAutoProxyCreator.setBeanNames("playGame", "playGame2");
		beanNameAutoProxyCreator.setInterceptorNames("customAdvisor");
		return beanNameAutoProxyCreator;
	}
代码语言:javascript复制
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
IPlay playGame = applicationContext.getBean("playGame", IPlay.class);
playGame.play();
PlayGame2 playGame2 = (PlayGame2)applicationContext.getBean("playGame2");
playGame2.play();

**注意:**这里我配置了两个需要代理的类PlayGame、PlayGame2,PlayGame是实现了接口IPlay的,那么在getBean的时候不一样,我强转是用的接口, 因为我PlayGame实现了接口,那么就像上面源码那样,会去使用JDk的动态代理,而代理出来的代理对象,并非是之前的PlayGame,所以在创建bean时,返回的是代理对象,并不是PlayGame,所以,强制为PlayGame的话是会报下面的异常。

使用接口的类,被自动代理后,使用子类强制报错:

代码语言:javascript复制
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'playGame' is expected to be of type 'com.lry.aop.service.PlayGame' but was actually of type 'com.sun.proxy.$Proxy10'

原理

BeanNameAutoProxyCreator其实是一个后置处理器,我们看他的类图就知道了。

所以在创建bean的时候,就会经过后置处理器的初始化后方法:postProcessAfterInitialization;

调用点有两个地方:

第一个地方:实例化前

AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation

这个地方需要实现了InstantiationAwareBeanPostProcessor的后置处理器再postProcessBeforeInstantiation返回一个bean,这个地方是在实例化前,调用我们的后置处理器生成的bean(我们自己生成返回的bean),那么这意味着,bean的生命周期已经结束,想要在做干涉只能在postProcessBeforeInstantiation方法后面,所以才会有下面这一段:

代码语言:javascript复制
if (targetType != null) {
    // 实例化前
    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    if (bean != null) {
        // 在bean返回之前,做一些后置的初始化操作进行干涉
        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    }
}

第二个地方:初始化完

AbstractAutowireCapableBeanFactory#initializeBean

看一下这个代码,它是返回null是相当于不做任何操作的,那么在看处理器中的代码是怎么回事。

代码语言:javascript复制
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
    Object current = processor.postProcessAfterInitialization(result, beanName);
    // 这里返回null就直接返回了
    if (current == null) {
        return result;
    }
    result = current;
}
return result;

在处理器中是这样的,这段紧接着上面这段;AbstractAutoProxyCreator#postProcessAfterInitialization

代码语言:javascript复制
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// earlyProxyReferences中存的是哪些提前进行了AOP的bean,beanName:AOP之前的对象
			// 注意earlyProxyReferences中并没有存AOP之后的代理对象  BeanPostProcessor
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 没有提前进行过AOP,则进行AOP
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;   
	}

那么,再接着看wrapIfNecessary

代码语言:javascript复制
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// 在当前targetSourcedBeans中存在的bean,表示在实例化之前就产生了代理对象
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// 当前这个bean不用被代理
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}

		// 先判断当前bean是不是要进行AOP,比如当前bean的类型是Pointcut、Advice、Advisor等那就不需要进行AOP
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 这个由上面的那个判断就知道这个map的作用只是用了判断bean是否进行了aop
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// 获取当前beanClass所匹配的advisors
    // 这里的实现和DefaultAdvisorAutoProxyCreator的不一样
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

		// 如果匹配的advisors不等于null,那么则进行代理,并返回代理对象
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 基于bean对象和Advisor创建代理对象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 存一个代理对象的类型
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

这个createProxy方法,就和上面ProxyFactory的使用是一样的。

代码语言:javascript复制
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);	// 复制配置参数
		// 是否指定了必须用cglib进行代理
		if (!proxyFactory.isProxyTargetClass()) {
			// 如果没有指定,那么则判断是不是应该进行cglib代理(判断BeanDefinition中是否指定了要用cglib)
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 是否进行jdk动态代理,如果当前beanClass实现了某个接口,那么则会使用JDK动态代理
				evaluateProxyInterfaces(beanClass, proxyFactory); // 判断beanClass有没有实现接口
			}
		}

		// 将commonInterceptors和specificInterceptors整合再一起
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);

		proxyFactory.addAdvisors(advisors);	// 向ProxyFactory中添加advisor
		proxyFactory.setTargetSource(targetSource); // 被代理的对象
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);	//
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// 生成代理对象
		return proxyFactory.getProxy(getProxyClassLoader());
	}

怎么添加advisor的,其实到这里我也猜到它会怎么做了,我们在@Bean那里是这样定义的:

代码语言:javascript复制
beanNameAutoProxyCreator.setInterceptorNames("customAdvisor");

再看上面代码

代码语言:javascript复制
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 上面buildAdvisors方法进去的,开头第一句,这个resolveInterceptorNames方法就是从bean工厂中获取的bean
Advisor[] commonInterceptors = resolveInterceptorNames();
代码语言:javascript复制
private Advisor[] resolveInterceptorNames() {
		BeanFactory bf = this.beanFactory;
		ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
		List<Advisor> advisors = new ArrayList<>();

		for (String beanName : this.interceptorNames) {
			if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
				Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
				Object next = bf.getBean(beanName);
                // 这里就是它为我们注册advisor的地方
				advisors.add(this.advisorAdapterRegistry.wrap(next));
			}
		}
		return advisors.toArray(new Advisor[0]);
	}

**还有一点:**从wrap这个方法来看,自定义拦截的类支持下面三种类型:Advisor、MethodInterceptor、AdvisorAdapter

代码语言:javascript复制
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
			return new DefaultPointcutAdvisor(advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			// Check that it is supported.
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}

这里MethodInterceptor其实是实现了advice接口的,advice没有一个抽象接口,所以这里需要强转为MethodInterceptor

而后,在进行封装为DefaultPointcutAdvisor,如果都不属于advisor,和MethodInterceptor,那么有必要再检查看看是否是AdvisorAdapter类型的,AdvisorAdapter接口可以实现获取MethodInterceptor,也算是一种类型吧。

这个方法就是进行了转化,说成适配还更合适一点。

简单做一个验证,只验证一种:

代码语言:javascript复制
@Component
public class CustomMethodInterceptor implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("customMethodIntercepter...");
		return invocation.proceed();
	}
}
代码语言:javascript复制
@Bean
	public BeanNameAutoProxyCreator creator(){
		BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
		beanNameAutoProxyCreator.setBeanNames("playGame", "playGame2");
		beanNameAutoProxyCreator.setInterceptorNames("customMethodInterceptor");
		return beanNameAutoProxyCreator;
	}

流程梳理

  1. bean初始化完(两个地方),进入后置处理器AbstractAutoProxyCreator#postProcessAfterInitialization的方法
  2. 判断当前bean是否需要代理
  3. 判断当前bean是否需要aop
  4. 获取advisor
    1. 返回DO_NOT_PROXY或者不是DO_NOT_PROXY
  5. 判断是否用cglib
  6. beanFactory获取advisor bean
  7. ProxyFactory生成代理对象

DefaultAdvisorAutoProxyCreator

看这个的UML图,发现它也是个后置处理器,那么它和上面BeanNameAutoProxyCreator的逻辑基本差不多,不同的是怎么注册advisor。

代码语言:javascript复制
@Bean
	public DefaultAdvisorAutoProxyCreator creator() {
		return new DefaultAdvisorAutoProxyCreator();
	}

这个类,它的自动性比BeanNameAutoProxyCreator强,不需要做其他操作,直接注入后,它自己会去查找代理,那为什么spring会弄出这两种?

我的理解,两种方式,一种全自动注入,可以少敲几行代码,可以不用去为了配置,将beanName设置进去,也不用设置advisor名称,另一种,是需要手动设置要代理的beanName,和设置advisor名称,显然第二种的方式灵活性更高,可以依据自己的需要进行配置。

原理

前3步:

  1. bean初始化完,进入后置处理器AbstractAutoProxyCreator#postProcessAfterInitialization的方法
  2. 判断当前bean是否需要代理
  3. 判断当前bean是否需要aop

如果不理解,跟不上,重新再看一遍BeanNameAutoProxyCreator的原理部分。

他们实现了一样的postProcessor,所以从:AbstractAutoProxyCreator#wrapIfNecessary 下的接口实现开始看,这个方法里面获取advisor的实现是:AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

代码语言:javascript复制
protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		// 针对当前bean查找合格的Advisor
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

下面这段是获取advisor的方法,看看它是否与BeanNameAutoProxyCreator里的一样。

代码语言:javascript复制
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 得到所有的Advisor
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 进行筛选
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

		// 提供给子类去额外的添加advisor
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			// 按order进行排序
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}

		// 返回匹配的Advisor
		return eligibleAdvisors;
	}

方法findCandidateAdvisors所找的类型是advisor

代码语言:javascript复制
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);

然后,在找到所有的advisor后,需要进一步的过滤

代码语言:javascript复制
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				eligibleAdvisors.add(candidate);
			}
		}
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {

			// IntroductionAdvisor和@DeclareParents注解配合使用
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

这一段是筛选的逻辑,它过滤出IntroductionAdvisor、PointcutAdvisor这两种类型

代码语言:javascript复制
	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pca = (PointcutAdvisor) advisor;
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

其他代码都是AbstractAutoProxyCreator里的,所以省略。。。

流程梳理

  1. bean初始化完(两个地方),进入后置处理器AbstractAutoProxyCreator#postProcessAfterInitialization的方法
  2. 判断当前bean是否需要代理
  3. 判断当前bean是否需要aop
  4. 获取advisor
    1. 获取所有的advisor
    2. 筛选出IntroductionAdvisor、PointcutAdvisor两种类型
  5. 判断是否用cglib
  6. beanFactory获取advisor bean
  7. ProxyFactory生成代理对象

@EnableAspectJAutoProxy

我们使用aop一般是注解@Aspect、@Pointcut、@Before等等这些,然后使用这些注解还需要@EnableAspectJAutoProxy开启功能。

那么它是怎么实现的?

这个注解上导入了一个配置类:@Import(AspectJAutoProxyRegistrar.class)

进入AspectJAutoProxyRegistrar的,回调方法有下面这一句:

代码语言:javascript复制
// 根据方法名分析:注册Aspect的自动代理注册器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

跟踪到底层:

代码语言:javascript复制
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);

找到,最后是AnnotationAwareAspectJAutoProxyCreator注册到了bean工厂,那么从AnnotationAwareAspectJAutoProxyCreator看:

它也实现了后置处理器接口,也是只有:findCandidateAdvisors 不同

其中有一个熟悉的方法findCandidateAdvisors,它重写了:AbstractAdvisorAutoProxyCreator#findCandidateAdvisors

代码语言:javascript复制
// 它重写了AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		// Advisor=Pointcut Advice (切点加建议) ,获得到所有Advisor类型的bean
		List<Advisor> advisors = super.findCandidateAdvisors();

		// Build Advisors for all AspectJ aspects in the bean factory.
		// 找的是通过AspectJ的方式定义的Advisor
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

出现了super,没错,它的这个方法和DefaultAdvisorAutoProxyCreator那边是一样的,因为他们都继承了了同一个AbstractAdvisorAutoProxyCreator

那么,不同点就是在下面的buildAspectJAdvisors方法。

代码语言:javascript复制
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 拿到beanFactory中所有的bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
    // 是不是一个合格的Aspect的beanName
    if (!isEligibleBean(beanName)) {
        continue;
    }
    Class<?> beanType = this.beanFactory.getType(beanName);
    if (beanType == null) {
        continue;
    }
    // beanType是不是一个切面,判断beanType上是否存在@Aspect注解
    if (this.advisorFactory.isAspect(beanType)) {
        aspectNames.add(beanName);
        AspectMetadata amd = new AspectMetadata(beanType, beanName);
        //
        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

            MetadataAwareAspectInstanceFactory factory =
                new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
            // 解析Advisor (解析@before,@after这些注解)
            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
            if (this.beanFactory.isSingleton(beanName)) {
                this.advisorsCache.put(beanName, classAdvisors);
            }
            else {
                this.aspectFactoryCache.put(beanName, factory);
            }
            advisors.addAll(classAdvisors);
        }
        else {
            if (this.beanFactory.isSingleton(beanName)) {
                // 省略。。。。
            }
            MetadataAwareAspectInstanceFactory factory =
                new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
            this.aspectFactoryCache.put(beanName, factory);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
}
this.aspectBeanNames = aspectNames;
return advisors;

看一下List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

这个方法是解析注解的。

代码语言:javascript复制
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();		validate(aspectClass);		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);		List<Advisor> advisors = new ArrayList<>();		// 遍历所有非@PointCut的方法,获取@Before、@After等注解中的表达式切点,加上当前方法(也就是代理逻辑)一起封装为一个Advisor		for (Method method : getAdvisorMethods(aspectClass)) {			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);			if (advisor != null) {				advisors.add(advisor);			}		}		// If it's a per target aspect, emit the dummy instantiating aspect.		if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {			Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);			advisors.add(0, instantiationAdvisor);		}		// Find introduction fields.		// 处理DeclareParents注解(这个没用过,就不看了)		for (Field field : aspectClass.getDeclaredFields()) {			Advisor advisor = getDeclareParentsAdvisor(field);			if (advisor != null) {				advisors.add(advisor);			}		}		return advisors;	}

上面getAdvisorMethods是获取到所有的非@Pointcut的方法,然后在循环再判断是否是配标注了@Before、@After等这些注解的。

代码语言:javascript复制
	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,			int declarationOrderInAspect, String aspectName) {		validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());		// 得到当前candidateAdviceMethod方法上的所定义的expression表达式,也就是切点		AspectJExpressionPointcut expressionPointcut = getPointcut(				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());		if (expressionPointcut == null) {			return null;		}		// 构造一个Advisor,封装了切点表达式和当前方法		return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,				this, aspectInstanceFactory, declarationOrderInAspect, aspectName);	}

上面代码,是在获取到被注解标注的切点信息,如:@Before(value = “execution(* com.lry.aop.*.play(…))”)

而下面这个代码是获取到方法上的注解信息,并解析成切入点对象。

代码语言:javascript复制
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
		// 获取当前方法上是否存在Pointcut、Around, Before, After, AfterReturning, AfterThrowing注解
		AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		if (aspectJAnnotation == null) {
			return null;
		}

		// 注解中所配置的表达式
		AspectJExpressionPointcut ajexp =
				new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
		ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
		if (this.beanFactory != null) {
			ajexp.setBeanFactory(this.beanFactory);
		}
		return ajexp;
	}

上面步骤最终是将解析的切入点信息保存为InstantiationModelAwarePointcutAdvisorImpl对象,添加到advisor列表里。

那么它是怎么匹配到我们的pointCut的,上面的匹配被我忽略了,是因为,他们可以根据方法名称去匹配到,但是这里用了注解,使用的是表达式,具体看:AopUtils#canApply

这个方法是找出所有advisor后,进行筛选动作的一个判断,这个里面就是进行了一些匹配操作

代码语言:javascript复制
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}
    // 这里好像PointcutAdvisor的类都是true
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}
// 这里是我们使用@EnableAspectJAutoProxy后,通过注解扫描生成的对象
		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

		// 遍历targetClass以及targetClass的父类和接口中的所有方法
		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
                    // 表达式的匹配,具体的就是在这个方法里进行匹配的。
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

流程梳理

  1. 配置注解:@EnableAspectJAutoProxy
  2. 配置了Aspect类,即标注了@Aspect注解的aop类,有@Before这些注解的类
  3. 启动spring,
  4. 扫描到@EnableAspectJAutoProxy注解,执行AnnotationAwareAspectJAutoProxyCreator注册
  5. 两个地方调用后置处理器方法,
  6. 判断当前bean是否需要代理
  7. 判断当前bean是否需要aop
  8. 获取advisor
    1. 获取原始对象的beanType
    2. 通过beanType判断是否有@Aspect注解
    3. 查找被@Before、@After这些注解,进行解析,然后生成InstantiationModelAwarePointcutAdvisorImpl(PointCutAdvisor实现类)添加advisor
    4. 处理@DeclareParents注解,添加advisor
  9. 判断是用cglib
  10. beanFactory.getBean拿到对应的advisor
  11. 生成代理对象
  12. 返回并添加单例池

它和BeanNameAutoProxyCreator不同的是获取advisor的方式不同个,它自己实现了自己的获取方法。

总结

  1. 我们所用的注解方式(@Before)怎么实现代理的?
  2. aop 用的什么动态代理?
  3. aop 是否可以不用注解?

我们所用的注解方式(@Before)怎么实现代理的?

  1. 使用@EnableAspectJAutoProxy,该注解上标注了@Import,所以在spring启动扫描时,会将@Import导入的类注册,这个类属于后置处理器,在bean初始化完后会调用这个处理器,
  2. 处理器方法中:查找到标注了@Aspect注解的类,然后解析方法上的注解,生成一个pointCutAdvisor的子类,
  3. 判断是否用cglib
  4. beanFactory.getBean
  5. 生成代理对象

aop 用的什么动态代理?

aop中用到了两种代理:jdk的动态代理,cglib代理;

使用jdk的动态代理,需要有接口,要注意的是,有实现的接口的原始对象生成代理对象后,不能强转为子类,因为是同一个接口,但不是同一个类。

aop 是否可以不用注解?

aop不用注解实现自动代理,有两种方式,一是注册BeanNameAutoProxyCreator,二是注册DefaultAdvisorAutoProxyCreator。

第一种需要手动设置要代理的beanName,和设置代理的advisor的名称,而第二种是自动的查找代理,那么就会将所有匹配的方法代理。

还有就是非自动代理的方式,使用spring提供的ProxyFactory,这个比起上面两种不是很有应用场景,毕竟这种方式比较底层,而且代码量多,甚至可能出现错误。

0 人点赞