黑马程序员 学习日记(十)

来源:互联网 发布:清朝人口知乎 编辑:程序博客网 时间:2024/06/04 19:18
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

动态代理

代理是一种设计模式,与现实生活中的代理意思接近。一个类的运行由另一个类去代理运行,这样就可以在目标类的方法运行前后都加入额外的内容去执行,也可以对目标方法的参数进行过滤,委托类与代理类通常存在一个关联。代理设计模式非常有利于程序的管理维护和测试。

1.静态代理

shop.java
package com.itheima.proxy;/** * 定义一个商店接口 * 无论是原商场还是代理商都要实现这个接口 * @author cloud * */public interface Shop {public void sale() ;   //卖东西public void stoke() ;  //进货}

ShopImpl.java
package com.itheima.proxy;/** * 原商店 * 有出售和进货能力 * @author cloud * */public class ShopImpl implements Shop {@Overridepublic void sale() {System.out.println("卖出一件货物。");}@Overridepublic void stoke() {System.out.println("采购一批货物");}}

ShopProxy.java
package com.itheima.proxy;/** * 代理类 * 代理出售和进货 * @author cloud * */public class ShopProxy implements Shop {private ShopImpl shopImpl ;public ShopProxy(ShopImpl shopImpl){this.shopImpl = shopImpl ;}@Overridepublic void sale() {System.out.println("出售货物之前");shopImpl.sale();System.out.println("出售货物之后");}@Overridepublic void stoke() {System.out.println("进货之前");shopImpl.stoke();System.out.println("进货之后");}}

ShopTest.java
package com.itheima.proxy;/** * 测试 * @author cloud * */public class ShopTest {public static void main(String[] args){ShopImpl shopImpl = new ShopImpl() ;ShopProxy shopProxy = new ShopProxy(shopImpl) ;  //把对象交给代理对象shopProxy.sale();     //代理对象在卖东西shopProxy.stoke();  //代理对象 在进货}}

简单的一个静态代理就实现了。重点在于他们都实现了同一个接口,这样才有相同方法,才能在委托类把对象交给代理类的对象的时候确保执行相同方法,实现代理。但在这之余代理可以更自由的去做其他的事情,这就是代理的好处。你随时可以加一个打折促销的广告,夏季冬季随便换也不影响原出售环节。

2.动态代理

但在实际开发中,如果只使用程序员预订编辑好的代理类,每一个接口每一个委托类都要去设计一个代理类,这样做徒然增加工作量,维护起来也很费时费力。最好就是一个代理类能接受所有委托。利用Java的反射机制设计一个代理类使其接受任意代理。
要做这样一个代理,那我们就要仔细思考。首先肯定还是要接收一个对象参数,然后要实现此对象的哪个方法,执行方法可能需要参数。这样就一共需要这三个参数。JDK正好为我们提供了一个接口,
InvocationHandler接口: 
public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 

接口解决了,那么最关键的类呢?关键就在于这个类必须是动态的,如果写死它最终还是一个静态代理。所以这个类必须要在运行时动态创建。实现动态创建类要使用Proxy类。
把上面的代理类稍微改动一下,接口和委托类不用动。
package com.itheima.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 代理类 * 代理出售和进货 * @author cloud * */public class ShopProxy implements InvocationHandler {private Object target ;public Object bind(ShopImpl target){this.target = target ;return Proxy.newProxyInstance(target.getClass().getClassLoader(),  //通过反射创建一个类target.getClass().getInterfaces(), this) ;}        //代理对象执行任意方法都会自动来执行一次这个方法。@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {Object result = null ;System.out.println("start");result = method.invoke(target, args) ;    //执行委托方法System.out.println("end");return result;}}
main方法中也稍微改改,使用方式有所变化
package com.itheima.proxy;/** * 测试 * @author cloud * */public class ShopTest {public static void main(String[] args){ShopProxy proxy = new ShopProxy() ;  //获得代理类Shop shop = (Shop)proxy.bind(new ShopImpl()) ;   通过接口把委托对象与代理对象绑定shop.sale();}}
所以,bind()方法里传什么对象都可以了,当然前提还是实现了相同接口才行。如果你不了解反射是很难理解动态的代理过程的,不懂的同学请补习一下。。。

总结

可以看出JDK的代理方式必须依赖接口,如果一个类没有实现相应接口是不可以被代理的。
如果你想既不实现接口又要代理,建议去了解一下CGLIBCGLIB是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
关于
CGLIB我了解的也不是太多,这里就不多说了,感兴趣的朋友请自行搜索相关资料吧。( ̄▽ ̄)~*
大家互相学习互相进步
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
0 0