Java动态代理

一、动态代理的背景
     之前我们应该学习过静态代理的使用,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口 服务,这样一来程序开发中必然产生过多的代理,较为麻烦。
所以我们需要一种不同于静态代理的代理机制来完成代理功能,即:最好可以通过一个代理类完成全部的代理功能。

二、什么是动态代理
    动态代理指的是客户通过代理类来调用其他对象(被代理类)的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

三、代理设计模式的原理(静态代理、动态代理)
使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始 对象上。

四、静态代理例子回顾讲解
在这里我们先用一个衣服制造厂的例子回顾一下静态代理的使用:
第一步:创建接口
//衣服制造工厂(接口)
interface ClothFactory {
	void productCloth();
}
第二步:创建被代理类
//耐克生产工厂(被代理类)
class NikeClothFactory implements ClothFactory {

	@Override
	public void productCloth() {
		System.out.println("Nike工厂生产一批衣服!");
	}
}
第三步:创建代理类
//(代理类)
class ProxyFactory implements ClothFactory {
	ClothFactory cf;

	//创建代理类的对象时,实际传入一个被代理类的对象
	public ProxyFactory(ClothFactory cf) {
		this.cf = cf;
	}

	@Override
	public void productCloth() {
		System.out.println("代理类开始执行,收代理费1000元!");
		cf.productCloth();
	}
}
第四步:测试
public class TestClothProduct {
	public static void main(String[] args) {
		//创建被代理类的对象
		NikeClothFactory nike = new NikeClothFactory();
		//创建代理类的对象
		ProxyFactory proxy = new ProxyFactory(nike);
		proxy.productCloth(); 
	}
}
测试结果:

通过分析上面的代码我们总结一下静态代理的弊端:
①我们需要在运行前手动创建代理类,这意味着如果有很多代理的话会很麻烦。
②代理类和被代理类必须实现相同的接口,如果接口发生变动,代理类和被代理类都要作出相应的修改,容易出问题。
所以我们需要使用一个更为灵活的代理机制——动态代理

五、动态代理
第一步:创建接口
//(接口)
interface Subject {
	void action();
}
第二:创建被代理类
//(被代理类)
class RealSubject implements Subject {

	@Override
	public void action() {
		System.out.println("我是被代理类,记得要执行我哦~~!");
	}

}
第三步:我们需要创建一个动态的代理类,要想创建动态代理类,我们要实现 InvocationHandler 接口
//动态代理都要用到InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
	Object object;//实现了接口的被代理类的对象的声明

	//①给被代理类的对象实例化
	//②返回一个代理类的对象
	public Object blind(Object object) {
		this.object = object;
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
	}

	//当通过代理类的对象发起对被 重写的方法的调用时,都会转化为对如下的invoke的调用
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//method方法的返回值时returnValue
		Object returnValue = method.invoke(object, args);
		return returnValue;
	}

}
这里我们来讲解一下 newProxyInstance() 方法中参数的含义
public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)
            		throws IllegalArgumentException{
		//此处方法内容略去
	}
①ClassLoader loader:被代理类的类加载器,用来创建代理类。
②Class<?>[] interfaces:被代理类实现的接口,创建的代理类会实现这个接口。
③InvocationHandler h:实现了 InvocationHandler 接口的类对象,它只有一个 invoke 方法,是代理类进行拦截操作的入口。
第四步:测试
public class TestProxy {
	public static void main(String[] args) {
		//1.被代理类的对象
		RealSubject real = new RealSubject();
		//2.创建一个实现了InvocationHandler接口的类的对象
		MyInvocationHandler handler = new MyInvocationHandler();
		//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口的代理类的对象
		Object obj = handler.blind(real);
		Subject sub = (Subject) obj;//此时的sub就是代理类的对象

		sub.action();//转到对InvocationHandler接口的实现类invoke()方法的调用

		//再举一个例子
		NikeClothFactory nike = new NikeClothFactory();
		ClothFactory proxyCloth = (ClothFactory) handler.blind(nike);//proxyCloth即为代理类的对象
		proxyCloth.productCloth();
	}
}
第五步:测试结果

这就是动态代理的使用,比较抽象,不是很容易接受,赶紧动手试试吧!

六、动态代理与AOP

    AOP即面向切面的编程,前面介绍的 Proxy 和 InvocationHandler 很难看出这种动态代理的优势,下面我们来简单介绍一下AOP这种更为实用的动态代理机制。


    在编程过程中,相同的代码多次出现会使代码冗余,我们一般都会采取将代码段放到一个方法中供我们多次调用来解决代码冗余问题,如图,如果代码段1、2、3和深色代码块分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代码段1、2、3既可以执行方法A,又无需在程序中以硬编码的方式直接调用深色代码的方法。

下面我们举一个例子来说明一下:

第一步:创建接口

interface Human {
	void info();

	void fly();
}

第二步:创建被代理类

//被代理类	
class SuperMan implements Human {

	@Override
	public void info() {
		System.out.println("我是超人!我怕谁!");
	}

	@Override
	public void fly() {
		System.out.println("I believe I can fly!");
	}
}
第三步:创建相同代码段组成的工具类,方便调用

class HumanUtil {
	public void method1() {
		System.out.println("=======方法一=======");
	}

	public void method2() {
		System.out.println("=======方法二=======");
	}
}

第四步:创建实现InvocationHandler接口的类

class InvocationHandlerTest implements InvocationHandler {
	Object obj;//被代理对象的声明

	public void setObject(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		HumanUtil hu = new HumanUtil();
		//调用工具类中的通用方法
		hu.method1();
		//回调目标对象的方法
		Object returnValue = method.invoke(obj, args);
		//调用工具类中的通用方法
		hu.method2();
		return returnValue;
	}
}

第五步:动态的创建一个代理类的对象

class MyProxy {
	//动态的创建一个代理类的对象
	public static Object getProxyInstance(Object obj) {
		InvocationHandlerTest handler = new InvocationHandlerTest();
		handler.setObject(obj);
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
	}
}
第六步:测试

public class TestAOP {
	public static void main(String[] args) {
		//创建了一个被代理类的对象
		SuperMan man = new SuperMan();
		//返回一个代理类的对象
		Human hu = (Human) MyProxy.getProxyInstance(man);
		//通过代理类的对象调用重写的方法
		hu.info();
		System.out.println();
		hu.fly();
	}
}
测试结果:


    面向切面的编程这一部分仅作了解,如果理解起来比较困难,可以通过下面这张图体会一下AOP的整个运转流程。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值