前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring源码篇(七)AOP原理

spring源码篇(七)AOP原理

作者头像
用针戳左手中指指头
修改2023-10-24 18:45:15
3750
修改2023-10-24 18:45:15
举报
文章被收录于专栏:学习计划学习计划

前言

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();
image-20210306224301277
image-20210306224301277

第二种写法:

代码语言: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();
image-20210308014042999
image-20210308014042999

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

image-20210308013629727
image-20210308013629727

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

代码语言: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'
image-20210308014117257
image-20210308014117257

原理

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

image-20210308014343090
image-20210308014343090

所以在创建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;
	}
image-20210308184057513
image-20210308184057513

流程梳理

  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

image-20210308181659567
image-20210308181659567

看这个的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看:

image-20210308231649771
image-20210308231649771

它也实现了后置处理器接口,也是只有: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,这个比起上面两种不是很有应用场景,毕竟这种方式比较底层,而且代码量多,甚至可能出现错误。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-05-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 动态代理
    • jdk的动态代理
      • CGLIB
        • 区别
        • AOP的动态代理
          • JdkDynamicAopProxy
            • ObjenesisCglibAopProxy
            • 自动代理
              • BeanNameAutoProxyCreator
                • 例子
                • 原理
                • 流程梳理
              • DefaultAdvisorAutoProxyCreator
                • 原理
                • 流程梳理
                • 流程梳理
            • @EnableAspectJAutoProxy
            • 总结
            相关产品与服务
            云顾问
            云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档