zoukankan      html  css  js  c++  java
  • Spring Aop(十五)——Aop原理之Advised接口

    转发地址:https://www.iteye.com/blog/elim-2398726

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

  • 相关阅读:
    分享一个自己写的vue多语言插件smart-vue-i18n
    利用vw+rem实现移动web适配布局
    你说前端不了解业务?
    小程序开发总结一:mpvue框架及与小程序原生的混搭开发
    小码农的职场人生一:由张小平离职引发的一些吐槽
    javascript本地缓存方案-- 存储对象和设置过期时间
    手淘移动适配方案flexible.js兼容bug处理
    微信小程序入坑之自定义组件
    vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理
    非域环境下使用证书部署数据库(SqlServer2008R2)镜像
  • 原文地址:https://www.cnblogs.com/Jeely/p/11947654.html
Copyright © 2011-2022 走看看