Spring Aop(十五)——Aop原理之Advised接口

简介: Spring Aop原理之Advised接口 通过之前我们介绍的ProxyFactory我们知道,Spring Aop是通过ProxyFactory来创建代理对象的。ProxyFactory在创建代理对象时会委托给DefaultAopProxyFactory.createAopProxy(AdvisedSupport config),DefaultAopProxyFactory内部会分情况返回基于JDK的JdkDynamicAopProxy或基于CGLIB的ObjenesisCglibAopProxy,它俩都实现了Spring的AopProxy接口。

Spring Aop原理之Advised接口

通过之前我们介绍的ProxyFactory我们知道,Spring Aop是通过ProxyFactory来创建代理对象的。ProxyFactory在创建代理对象时会委托给DefaultAopProxyFactory.createAopProxy(AdvisedSupport config)DefaultAopProxyFactory内部会分情况返回基于JDK的JdkDynamicAopProxy或基于CGLIB的ObjenesisCglibAopProxy,它俩都实现了Spring的AopProxy接口。AopProxy接口中只定义了一个方法,getProxy()方法,Spring Aop创建的代理对象也就是该接口方法的返回结果。

我们先来看一下基于JDK代理的JdkDynamicAopProxy的getProxy()的逻辑。

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

@Override
public Object getProxy(ClassLoader classLoader) {
	if (logger.isDebugEnabled()) {
		logger.debug("Creating JDK dynamic proxy: target source is " 
                + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils
            .completeProxiedInterfaces(this.advised);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

我们可以看到它最终是通过JDK的Proxy来创建的代理,使用的InvocationHandler实现类是它本身,而使用的接口是AopProxyUtils.completeProxiedInterfaces(this.advised)的返回结果。而这个this.advised对象是AdvisedSupport类型,它是ProxyFactory的父类(间接通过ProxyCreatorSupport继承,ProxyFactory的直接父类是ProxyCreatorSupportProxyCreatorSupport的父类是AdvisedSupport),AdvisedSupport的父类是ProxyConfigProxyConfig中包含创建代理对象时的一些配置项信息。以下是AopProxyUtils.completeProxiedInterfaces(this.advised)的内部逻辑。

public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
	Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
	if (specifiedInterfaces.length == 0) {
		// No user-specified interfaces: 
                //check whether target class is an interface.
		Class<?> targetClass = advised.getTargetClass();
		if (targetClass != null && targetClass.isInterface()) {
			specifiedInterfaces = new Class<?>[] {targetClass};
		}
	}
	boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
	boolean addAdvised = !advised.isOpaque() 
                && !advised.isInterfaceProxied(Advised.class);
	int nonUserIfcCount = 0;
	if (addSpringProxy) {
		nonUserIfcCount++;
	}
	if (addAdvised) {
		nonUserIfcCount++;
	}
	Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length
                 + nonUserIfcCount];
	System.arraycopy(specifiedInterfaces, 0, 
                proxiedInterfaces, 0, specifiedInterfaces.length);
	if (addSpringProxy) {
		proxiedInterfaces[specifiedInterfaces.length]
                     = SpringProxy.class;
	}
	if (addAdvised) {
		proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;
	}
	return proxiedInterfaces;
}

我们可以看到其会在!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)返回true的情况下加上本文的主角Advised接口。isOpaque()ProxyConfig中的一个方法,对应的是opaque属性,表示是否禁止将代理对象转换为Advised对象,默认是false!advised.isInterfaceProxied(Advised.class)表示将要代理的目标对象类没有实现Advised接口,对于我们自己应用的Class来说,一般都不会自己去实现Advised接口的,所以这个通常也是返回true,所以通常创建Aop代理对象时是会创建包含Advised接口的代理对象的,即上述的proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class会被执行。
前面我们已经提到,JdkDynamicAopProxy创建代理对象应用的InvocationHandler是其自身,所以我们在调用JdkDynamicAopProxy创建的代理对象的任何方法时都将调用JdkDynamicAopProxy实现的InvocationHandler接口的invoke(Object proxy, Method method, Object[] args)方法。该方法实现如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	MethodInvocation invocation;
	Object oldProxy = null;
	boolean setProxyContext = false;

	TargetSource targetSource = this.advised.targetSource;
	Class<?> targetClass = null;
	Object target = null;

	try {
		if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
			// The target does not implement 
                        // the equals(Object) method itself.
			return equals(args[0]);
		}
		if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
			// The target does not implement the hashCode() 
                        // method itself.
			return hashCode();
		}
		if (!this.advised.opaque 
                    && method.getDeclaringClass().isInterface() &&
		method.getDeclaringClass().isAssignableFrom(Advised.class)) {
			// Service invocations on ProxyConfig with the proxy config...
			return AopUtils.invokeJoinpointUsingReflection(
                                  this.advised, method, args);
		}

		Object retVal;

		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}

		// May be null. Get as late as possible to 
                // minimize the time we "own" the target,
		// in case it comes from a pool.
		target = targetSource.getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}

		// Get the interception chain for this method.
		List<Object> chain = this.advised
                .getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		// Check whether we have any advice. If we don't, 
                // we can fallback on direct
		// reflective invocation of the target, 
                // and avoid creating a MethodInvocation.
		if (chain.isEmpty()) {
			// We can skip creating a MethodInvocation: 
                        //just invoke the target directly
			// Note that the final invoker must be an 
                        // InvokerInterceptor so we know it does
			// nothing but a reflective operation on the target, 
                        // and no hot swapping or fancy proxying.
			retVal = AopUtils.invokeJoinpointUsingReflection(target, 
                                     method, args);
		} else {
			// We need to create a method invocation...
			invocation = new ReflectiveMethodInvocation(proxy, 
                               target, method, args, targetClass, chain);
			// Proceed to the joinpoint through the interceptor chain.
			retVal = invocation.proceed();
		}

		// Massage return value if necessary.
		Class<?> returnType = method.getReturnType();
		if (retVal != null && retVal == target && 
                                returnType.isInstance(proxy) &&
				!RawTargetAccess.class
                                .isAssignableFrom(method.getDeclaringClass())) {
			// Special case: it returned "this" and 
                        // the return type of the method
			// is type-compatible. Note that we can't help 
                        // if the target sets
			// a reference to itself in another returned object.
			retVal = proxy;
		} else if (retVal == null && returnType != Void.TYPE 
                        && returnType.isPrimitive()) {
		throw new AopInvocationException(
	"Null return value from advice does not match primitive return type for: " 
                + method);
		}
		return retVal;
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			// Must have come from TargetSource.
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}

其中关于Advised接口方法调用最核心的一句是如下这句。我们可以看到,当我们调用的目标方法是定义自Advised接口时,对应方法的调用将委托给AopUtils.invokeJoinpointUsingReflection(this.advised, method, args)invokeJoinpointUsingReflection方法的逻辑比较简单,是通过Java反射来调用目标方法。在这里invokeJoinpointUsingReflection传递的目标对象正是AdvisedSupport类型的this.advised对象。

if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
		method.getDeclaringClass().isAssignableFrom(Advised.class)) {
	// Service invocations on ProxyConfig with the proxy config...
	return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

AdvisedSupport类是实现了Advised接口的,所以Spring Aop创建了基于Advised接口的代理对象后在调用Advised接口方法时可以把它委托给AdvisedSupport。而我们知道Spring Aop代理对象的创建正是基于AdvisedSupport的配置进行的(配置项主要都定义在AdvisedSupport的父类ProxyConfig类中)。创建代理对象时应用AdvisedSupport,调用Advised接口方法也用同一个实现了Advised接口的AdvisedSupport对象,所以这个过程在Spring Aop内部就可以很好的衔接。接着我们来看一下Advised接口的定义。

public interface Advised extends TargetClassAware {

	boolean isFrozen();

	boolean isProxyTargetClass();

	Class<?>[] getProxiedInterfaces();

	boolean isInterfaceProxied(Class<?> intf);

	void setTargetSource(TargetSource targetSource);

	TargetSource getTargetSource();

	void setExposeProxy(boolean exposeProxy);

	boolean isExposeProxy();

	void setPreFiltered(boolean preFiltered);

	boolean isPreFiltered();

	Advisor[] getAdvisors();

	void addAdvisor(Advisor advisor) throws AopConfigException;

	void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

	boolean removeAdvisor(Advisor advisor);

	void removeAdvisor(int index) throws AopConfigException;

	int indexOf(Advisor advisor);

	boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

	void addAdvice(Advice advice) throws AopConfigException;

	void addAdvice(int pos, Advice advice) throws AopConfigException;

	boolean removeAdvice(Advice advice);

	int indexOf(Advice advice);

	String toProxyConfigString();

}

Advised接口中定义的方法还是非常多的,通过它我们可以在运行时了解我们的代理对象是基于CGLIB的还是基于JDK代理的;可以了解我们的代理对应应用了哪些Advisor;也可以在运行时给我们的代理对象添加和删除Advisor/Advise。本文旨在描述Spring Aop在创建代理对象时是如何基于Advised接口创建代理的,以及我们能够应用Advised接口做哪些事。文中应用的是Spring创建基于JDK代理对象的过程为示例讲解的,其实基于CGLIB的代理也是一样的。关于CGLIB的代理过程、本文中描述的一些核心类以及本文的核心——Advised接口的接口方法说明等请有兴趣的朋友参考Spring的API文档和相关的源代码。
(注:本文是基于Spring4.1.0所写,Elim写于2017年5月15日)

目录
相关文章
|
22天前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
683 111
|
3月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
191 6
|
4月前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
196 6
|
13天前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
58 0
|
1月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
62 0
|
1月前
|
Java 开发者 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——什么是AOP
本文介绍了Spring Boot中的切面AOP处理。AOP(Aspect Oriented Programming)即面向切面编程,其核心思想是分离关注点。通过AOP,程序可以将与业务逻辑无关的代码(如日志记录、事务管理等)从主要逻辑中抽离,交由专门的“仆人”处理,从而让开发者专注于核心任务。这种机制实现了模块间的灵活组合,使程序结构更加可配置、可扩展。文中以生活化比喻生动阐释了AOP的工作原理及其优势。
42 0
|
2月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
169 25
|
2月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
108 24
|
3月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
163 8
|
4月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
346 14