java动态代理

代理模式
代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

上述例子中,类A写死持有B,就是B的静态代理。
动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

代理类(该类型是 $Proxy+数字 的“新的类型”,动态生成的)代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。
动态代理就是要生成一个包装类对象(实现InvocationHandler接口),该InvocationHandler包含被代理对象,并通过反射调用(Object result = method.invoke(target, args))代理对象的方法,调用前后均可以做增强。
从原理可以看出,JDK动态代理是“对象”的代理。

public class PackTarget implements InvocationHandler{
    // 目标类、委托类,也就是被代理对象
    private Object target;
    public void setTarget(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        // 这里可以做增强
        Object result = method.invoke(target, args);
        // 这里可以做增强
        return result;
    }
    
    // 生成代理类
    public Object CreatProxyedObj()
    {
        return ;
    }  
   
}
		
		// 目标类、委托类对象,也就是被代理对象
		TargetInterface target= new Target();
		//包装类对象
        PackTarget packTarget= new PackTarget ();
        packTarget.setTarget(target); 
        //代理对象,之后就可以通过代理对象调方法了
		TargetInterface proxyXXX = (Target )Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), packTarget);
		//第一个参数 委托类的Classloader类加载器
		//第二个参数 委托类的接口信息(主要是方法的定义)
		//第三个参数 包装类对象
				

下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法
Proxy创造的ProxyXXX是自己(Proxy)的子类,且实现了目标类的接口

//简化的ProxyXXX (代理类)
public final class ProxyXXX extends Proxy implements TargetInterface//这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成ProxyXXX 时留下的痕迹
  	private static Method m0;
  	private static Method m1;
  	...
  	static{
  	 	m0 = Class.forName("XXX.TargetInterface ").getMethod("methodName0", new Class[0]);
      	m1 = Class.forName("XXX.TargetInterface ").getMethod("methodName1", new Class[] { Class.forName("java.lang.String") });
    	m2 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      	m3 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      	m4 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  	}
	public final String methodName0(String paramString)return (String)this.h.invoke(this, m0, new Object[] { paramString });public final String methodName1(String paramString)return (String)this.h.invoke(this, m1, new Object[] { paramString });
	}
}

可以看到,ProxyXXX(代理类) 中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成proxyXXX(代理对象) 时传递的第三个参数。这里还有个关键就是methodName0方法(业务方法)跟调用invoke方法时传递的参数m0一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到ProxyXXX 在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

cglib动态代理
我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。
通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。
先看下代码

public class MyMethodInterceptor implements MethodInterceptor{
//第一个参数传递的是代理类对象,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法(代理类中,构建的名叫“CGLIB”+“$父类方法名$”的方法,方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用)
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable{
        // 这里可以增强
        //invokeSuper方法,这是因为cglib采用了fastclass机制,
        //不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用
        Object result = arg3.invokeSuper(arg0, arg2);
        // 这里可以增强
        return result ;
    } 
}

生成cglib代理对象

//Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非私有、非final的方法都带有回调钩子。
		Enhancer enhancer = new Enhancer();
        //Target.Class 目标类、委托类
        enhancer.setSuperclass(Target.Class);
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor ();
        enhancer.setCallback(myMethodInterceptor );
        Target proxyXXX = enhancer.create();

cglib的原理是这样,它生成一个继承委托类的类型CGLIBProxyXXX(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 代理类重写委托类中的方法(它调用MethodInterceptor的intercept方法),然后在CGLIBProxyXXX中,构建名叫“CGLIB”+“ 父类方法名 父类方法名 父类方法名”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

CGLIBProxyXXX(代理类)的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法增强,然后我们调用cglib方法间接调用父类方法完成整个方法链的调用,然后ntercept方法增强,完成后 返回结果。

这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

	@Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable{
        // 这里可以增强
        //invokeSuper方法,这是因为cglib采用了fastclass机制,
        //不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用
        Object result = arg3.invokeSuper(arg0, arg2);
        // 这里可以增强
        return result ;
    } 

因为如果我们通过反射 arg1.invoke(arg0, …)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, …)很明显会死循环。

所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。
————————————————
版权声明:本文为CSDN博主「赵举飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flyfeifei66/article/details/81481222

————————————————
版权声明:本文为CSDN博主「赵举飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flyfeifei66/article/details/81481222




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值