CGLib与JDKProxy的区别

无情 阅读:181 2022-04-08 10:52:34 评论:0

Spring AOP 的实现主要有两种:CGLib与JDK自带的Proxy。
他们主要的区别是,需要JDKProxy修改的类必须实现接口(因此也只能代理public方法),在创建Proxy时可以使用class.getInterfaces()获得所有接口并进行代理。
而CGLib不受这个限制可以修改任何非private非final方法。
以上只是一些大家都知道的方面。在这两种方法的实现中,其实还是有其他一些重要的差别的,那就是调用代理类的方法内部同时调用了自己的另一个方法的话他们的最终结果将是不一样的,下面上代码!

//被代理对象接口 
public interface DummyInterface { 
 
	void fun2(); 
 
	void fun1(); 
 
} 
//被代理对象实现 
public class Dummy implements DummyInterface { 
 
	public Dummy(){ 
	} 
	public void fun1(){ 
		System.out.println("fun1 start"); 
		fun2();    //调用内部方法fun2 
		System.out.println("fun1 end"); 
	} 
 
	public void fun2() { 
		System.out.println("-fun2 start"); 
		System.out.println("-fun2 end"); 
	} 
} 
import java.lang.reflect.Method; 
 
import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 
 
//CGLib实现 
public class CGlibEnhance implements MethodInterceptor{ 
	public static void main(String[] args) { 
		CGlibEnhance ce = new CGlibEnhance(); 
		Dummy dummy = (Dummy) ce.getProxy(Dummy.class);//获得代理对象 
		dummy.fun1(); 
		System.out.println("----"); 
		dummy.fun2();//直接调用fun2 
	} 
	 
	public Object getProxy(Class<Dummy> clazz) { 
	    Enhancer enhancer = new Enhancer();   
		enhancer.setSuperclass(clazz); 
		enhancer.setCallback(this); 
		return enhancer.create(); 
	} 
	 
	@Override 
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
		System.out.println("pre-"+method.getName());   
        	Object result = proxy.invokeSuper(obj,args);   
        	System.out.println("post-"+method.getName());   
        	return result;   
	} 
} 
import java.lang.reflect.InvocationHandler; 
import java.lang.reflect.Method; 
import java.lang.reflect.Proxy; 
 
//JDKProxy实现 
public class JavaProxy implements InvocationHandler { 
	private Object target;//被代理对象 
 
	public JavaProxy(Object target) { 
		super(); 
		this.target = target; 
	} 
 
	public static void main(String[] args) { 
		JavaProxy handler = new JavaProxy(new Dummy()); 
 
		DummyInterface proxy = (DummyInterface) handler.getProxy();//获得代理对象 
		proxy.fun1(); 
		System.out.println("----"); 
		proxy.fun2();//直接调用fun2 
	} 
 
	@Override 
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
		Object result = null; 
		System.out.println("pre-" + method.getName()); 
		result = method.invoke(target, args); 
		System.out.println("post-" + method.getName()); 
 
		return result; 
	} 
 
	public Object getProxy() { 
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); 
	} 
} 

以上两个方法都代理了Dummy对象并分别调用了fun1 fun2两个方法,fun1内部又调用了fun2。
两个结果却不一样

//cglib 输出 
pre-fun1 
fun1 start 
pre-fun2  //★ 
-fun2 start 
-fun2 end  
post-fun2 //★ 
fun1 end 
post-fun1 
---- 
pre-fun2 
-fun2 start 
-fun2 end 
post-fun2 
 
//JDKProxy输出 
pre-fun1 
fun1 start 
-fun2 start //★ 
-fun2 end   //★ 
fun1 end 
post-fun1 
---- 
pre-fun2 
-fun2 start 
-fun2 end 
post-fun2 

可以看出在fun1调用fun2时(★处),JDKProxy并没有截获调用注入pre-fun2/post-fun2,但直接调用fun2时却截获到了方法调用。
其中原因是CGLib是使用继承的方式来改写原类,同时也可以看到在CGLib中我们并没有手动创建Dummy对象,因为CGLib create方法内部会自动创建。
而JDKProxy正如其名是Proxy,只是对原对象使用了代理模式,无法渗透到方法的内部调用。
这个从侧面说明了在spring AOP中如果被代理类进行类内部调用时会导致无法注入方法的情况。


标签:java
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

搜索
排行榜
关注我们

一个IT知识分享的公众号