spring aop 创建代理

news/2024/7/4 7:45:27

为什么80%的码农都做不了架构师?>>>   hot3.png

spring aop 创建代理


我们知道spring org\springframework\aop\framework\DefaultAopProxyFactory.java#createAopProxy的方法返回的JdkDynamicAopProxy 或 CglibProxyFactory 实现了创建代理类的逻辑,分别支持jdk和cglib两种代理方式。

jdk 代理方式仅支持对一个接口创建代理对象,也就是被代理的对象必须实现某个接口。

cglib 代理主要是针对类实现代理,基本实现思路是动态生成被代理类的子类,从而可以代理父类的所有方法。

在了解spring aop实现逻辑之前我们先回顾一下这两种代理方式。

jdk 代理

回顾

package framework.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 这是 spring  JdkDynamicAopProxy 的精简版实现,
 * 其实原理很简单,spring也是用Proxy和 InvocationHandler 这两个类创建代理对象。
 *
 * 在整个过程中,InvocationHandler 的 invoke 方法最重要,所有的代理逻辑全部在这里实现。
 */
public class JdkProxyDemo {
    public static void main(String[] args) {
        FooInterface foo = new FooImpl();
        FooHandler fooHandler = new FooHandler(foo);
        foo = (FooInterface)fooHandler.getProxy();
        foo.hello();
    }
}


interface FooInterface{
    void hello();
}

class FooImpl implements  FooInterface{

    @Override
    public void hello() {
        System.out.println("hello.");
    }
}

class FooHandler implements InvocationHandler{

    Object target;
    public FooHandler(Object obj){
        this.target = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before...");
        Object result = method.invoke(target,args);
        System.out.println("after...");
        return result;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),this);
    }
}

运行结果如下:

before...
hello.
after...

spring 实现

JdkDynamicAopProxy的getProxy方法返回代理方法

	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 代理的关键逻辑是在invoke中,下面是sping 的实现

	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 {
			// equals 方法的处理
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			// hascode 方法的处理
			if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}

			/*
			class 类的isAssignableFrom(Class cls)方法:
			如果调用这个方法的是class或接口与参数cls表示的类或接口相同,
			或者是参数cls表示的类或接口的弗雷,则返回true.
			形象地:自身类.class.isAssignableFrom(自身类或四类.class) 返回true
			eg.
			System.out.println(ArrayList.class.isAssignableFrom(Object.class) == false);
			System.out.println(Object.class.isAssignableFrom(ArrayList.class) == true);

			 */
			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 {
				// importance 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);
			}
		}
	}

上面最重主要的工作就是创建一个 ReflectiveMethodInvocation 封装了连接器链。

ReflectiveMethodInvocation#proceed

	public Object proceed() throws Throwable {
		//	We start with an index of -1 and increment early.
		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;
			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
//			普通拦截器,直接调用拦截器,如:
//			ExposeInvocationInterceptor
//			DelegatePertargetObjectInteroductionInterceptor
//			MethodBeforeAdviceInterceptor
//			AspectJAroundAdvice
//			AspectJAfterAdvice
//			将 this作为参数传递一保证当前实例中调用连的执行
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

这个方法的逻辑并不负责,主要负责责任链的调用,记录当前责任链调用位置,以便有序的进行调用。

cglib代理

回顾

CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实 现的)。EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿 (moke)对象。

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文 件的格式和指令集都很熟悉。


public class CglibEnhancerDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibEnhancerDemo.class);
        enhancer.setCallback(new MethodInterceptorImpl());
        
        CglibEnhancerDemo demo = (CglibEnhancerDemo)enhancer.create();
        demo.test();
        System.out.println(demo);
    }

    public void test() {
        System.out.println("test...");
    }

    private static class MethodInterceptorImpl implements MethodInterceptor{
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before invoke" + method);
            Object result = methodProxy.invokeSuper(o,objects);
            System.out.println("after invoke"+method);
            return result;
        }
    }
}

cglib 是通过吧MethodInterceptor加到 callBack中,对于方法的链接是通过连接器中的intercept方法来实现的。在吧MethodInterceptor加入到callback中后,在调用代理是会直接调用intercept方法,实现代理逻辑。

运行结果如下


before invoke public void bean.aop.CglibEnhancerDemo.test()
test...
after invoke public void bean.aop.CglibEnhancerDemo.test()
before invoke public java.lang.String java.lang.Object.toString()
before invoke public native int java.lang.Object.hashCode()
after invoke public native int java.lang.Object.hashCode()
after invoke public java.lang.String java.lang.Object.toString()
bean.aop.CglibEnhancerDemo$$EnhancerByCGLIB$$efbf68ac@a8ceb6

我们看到System.out.println(demo)这行代码运行时,会调用对象的toString,hasCode,最后打印的是bean.aop.CglibEnhancerDemo$$EnhancerByCGLIB$$efbf68ac@a8ceb6,是由cglib运行时产生的代理对象。

spring 实现

spring 委托CglibAopProxy创建 cglib 代理,看过了 jdk代理的初始化方式,我们知道 getProxy 方法肯定实现了 Enhancer 的创建和接口的封装。


	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating CGLIB proxy: target source is " + 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 (ClassUtils.isCglibProxyClass(rootClass)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}

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

			// Configure CGLIB Enhancer...
			// 创建和配置 Enhancer
			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 MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
			enhancer.setInterceptDuringConstruction(false);

			//importance 设置拦截器
			Callback[] callbacks = getCallbacks(rootClass);
			Class<?>[] types = new Class<?>[callbacks.length];
			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			enhancer.setCallbackFilter(new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
			enhancer.setCallbackTypes(types);
			enhancer.setCallbacks(callbacks);

			// Generate the proxy class and create a proxy instance.
//			生成代理类,创建对象
			Object proxy;
			if (this.constructorArgs != null) {
				proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
			}
			else {
				proxy = enhancer.create();
			}

			return proxy;
		}
		catch (CodeGenerationException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of class [" +
					this.advised.getTargetClass() + "]: " +
					"Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of class [" +
					this.advised.getTargetClass() + "]: " +
					"Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Exception ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

这里最重要的而是通过 getCallbacks 方法设置连接器链。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		// Parameters used for optimisation choices...
		boolean exposeProxy = this.advised.isExposeProxy();
		boolean isFrozen = this.advised.isFrozen();
		boolean isStatic = this.advised.getTargetSource().isStatic();

		// Choose an "aop" interceptor (used for AOP calls).
//		将拦截器封装在 DynamicAdvisedInterceptor
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

		// Choose a "straight to target" interceptor. (used for calls that are
		// unadvised but can return this). May be required to expose the proxy.
		Callback targetInterceptor;
		if (exposeProxy) {
			targetInterceptor = isStatic ?
					new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
		}
		else {
			targetInterceptor = isStatic ?
					new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
					new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
		}

		// Choose a "direct to target" dispatcher (used for
		// unadvised calls to static targets that cannot return this).
		Callback targetDispatcher = isStatic ?
				new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

		Callback[] mainCallbacks = new Callback[]{
//				将拦截器加入 callback
			aopInterceptor, // for normal advice
			targetInterceptor, // invoke target without considering advice, if optimized
			new SerializableNoOp(), // no override for methods mapped to this
			targetDispatcher, this.advisedDispatcher,
			new EqualsInterceptor(this.advised),
			new HashCodeInterceptor(this.advised)
		};

		Callback[] callbacks;

		// If the target is a static one and the advice chain is frozen,
		// then we can make some optimisations by sending the AOP calls
		// direct to the target using the fixed chain for that method.
		if (isStatic && isFrozen) {
			Method[] methods = rootClass.getMethods();
			Callback[] fixedCallbacks = new Callback[methods.length];
			this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);

			// TODO: small memory optimisation here (can skip creation for methods with no advice)
			for (int x = 0; x < methods.length; x++) {
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
				fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
						chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
				this.fixedInterceptorMap.put(methods[x].toString(), x);
			}

			// Now copy both the callbacks from mainCallbacks
			// and fixedCallbacks into the callbacks array.
			callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
			System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
			System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
			this.fixedInterceptorOffset = mainCallbacks.length;
		}
		else {
			callbacks = mainCallbacks;
		}
		return callbacks;
	}

spring在这个方法里考虑了很多情况,如exposeProxy暴露代理对象等,我们只解释DynamicAdvisedInterceptor。DynamicAdvisedInterceptor加入callBack实现代理逻辑,核心逻辑一定在intercept方法中。

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

		private final AdvisedSupport advised;

		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}

		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Class<?> targetClass = null;
			Object target = null;
			try {
				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 = getTarget();
				if (target != null) {
					targetClass = target.getClass();
				}
//				获取拦截器链
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// 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 = methodProxy.invoke(target, args);
				}
				else {
					// We need to create a method invocation...
//					调用拦截器链
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null) {
					releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

总结

不管是jdk代理还是cglib代理,都是首先构造责任链,然后封装此链记性串联调用,稍微有些区别就是jdk中直接构造ReflectiveMethodInvocation,而cglib中使用CglibMethodInvocation。

转载于:https://my.oschina.net/MrW/blog/807889


http://www.niftyadmin.cn/n/2436252.html

相关文章

BZOJ 3542 [Poi2014]Couriers ——可持久化线段树

【题目分析】 查找区间内出现次数大于一半的数字。 直接用主席树&#xff0c;线段树上维护区间大小&#xff0c;由于要求出现次数大于一半&#xff0c;每到一个节点可以分治下去。 时间复杂度(NQ)logN 【代码】 #include <cstdio> #include <cstring> #include <…

Linux系统从指定目录下的所有文件中查找某个关键字

2019独角兽企业重金招聘Python工程师标准>>> 命令 find test/ -name *.* | xargs grep "hello" 该命令主要应用于仅记住了某个文件中的部分内容&#xff0c;但已不记得该文件的具体名字&#xff0c;或者所在路径&#xff0c;那么可以使用此命令根据 记住的…

JAVA NIO简单实现Socket Server

为什么80%的码农都做不了架构师&#xff1f;>>> 使用JAVA NIO简单实现Socket Server package com.flyer.cn.javaIO;import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio…

LeetCode-448. Find All Numbers Disappeared in an Array C#

Given an array of integers where 1 ≤ a[i] ≤ n (n size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You…

MyBatisPlus 入门学习笔记(版本:3.4.3)

文章目录学习辅助资料MyBatisPlus概述1. MyBatisPlus是什么2. 特性快速开始1. 创建数据库 mybatis_plus2. 导入相关依赖3. 数据源配置3. 快速开始3.1 User实体类编写3.2 mapper编写3.3 启动类设置3.4 测试配置日志Mapper层自带的的CRUD方法1. Insert插入操作1.1 产生奇妙ID的原…

利用反射机制获取属性的值遇到的坑

类&#xff1a; public Class Test { public string name; public string value; } Test tnew Test(); t.name"abc"; t.value"123"; string str(string)t.GetType().GetProperty("name").GetValue(t,null); 找了3个小时&#xff0c;都找不出问题…

IDEA常用快捷键和Live Templates for Mac

1. IDEA常用快捷键 CmdShiftEnter&#xff1a;将输入的if&#xff0c;for&#xff0c;函数等等补上{}或者&#xff1b;使代码语句完整ShiftEnter&#xff1a;在当前行的下方开始新行OptEnter: 正则表达式验证CmdOptEnter&#xff1a;在当前行的上方插入新行OptEnter: 代码快速…

Qt 查找功能

版权声明该文章原创于Qter开源社区&#xff08;www.qter.org&#xff09;&#xff0c;作者yafeilinux&#xff0c;转载请注明出处&#xff01;导语这一篇我们来加上查找菜单的功能。因为要涉及Qt Creator的很多实用功能&#xff0c;所以单独用一篇文章来介绍。以前都用设计器设…