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日)

目录
相关文章
|
8天前
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
38 6
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
31 0
|
3天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
36 14
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
20天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
46 5
|
24天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
52 8
|
24天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
24天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
36 5
|
24天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
35 4
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
39 0