Java代理模式

来源:互联网 发布:易语言砍价源码 编辑:程序博客网 时间:2024/06/03 17:44

1. 代理模式

代理是一种设计模式,提供了通过代理对象访问目标对象的方式。这样做的好处是可以在目标对象的基础上增加额外的功能,扩展目标对象的功能。
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需要修改,可以通过代理或者其他方式来扩展该方法。
举个现实中的例子来说明代理模式:假如我们需要邀请一位明星唱歌,那么我们不会直接联系明星,而是直接联系明星的经纪人,由明星的经济负责接待洽谈等琐碎的工作,而明星只需要负责唱歌就行了。
在实际开发中,我们之所以需要产生代理对象,主要是用于拦截对真实业务对象的直接访问。
所以代理模式需要明确两个概念:
1. 代理对象存在的主要价值在于拦截对真实业务对象的直接访问。
2. 代理对象必须拥有和真实业务对象一样的方法。例如前面所举的例子,经纪人必须具有和明星相同的方法,会唱歌,但是不是真的懂得唱歌,真正懂得唱歌的是明星。所以我们联系明星的经纪人,把钱交给经纪人,然后由经纪人让明星去唱歌。
所以说,代理对象是目标对象的扩展,代理对象会调用目标对象的方法。

1.1 静态代理

静态代理在使用的时候,需要定义接口或者父类,被代理对象和代理对象需要实现相同的接口或者继承相同的父类。
下面示例:

定义一个接口IStar,该接口用于模拟明星唱歌。目标对象Star实现了IStar接口,而代理对象StarProxy则同样实现了IStar,通过调用了代理对象的方法来实际调用目标对象Star的方法。
IStar源代码如下:

public interface IStar {    public void sing();}

Star类代码如下:

public class Star implements IStar{    public void sing(){        System.out.println("Star is Singing!");    }}

StarProxy类源代码如下:

public class StarProxy implements IStar{    private IStar star;    public StarProxy(IStar star){        this.star=star;    }    public void sing(){        System.out.println("Get money!");        star.sing();        System.out.println("Sing finish");    }}

静态代理总结:
1. 可以做到在不修改目标对象的前提下,对目标对象功能进行拓展;
2. 因为代理对象和目标对象需要实现同样的接口,所以一旦接口添加方法,那么目标对象和代理对象都需要进行维护;

1.2 动态代理

动态代理有如下特点:
1. 代理对象不需要实现接口;
2. 代理对象的生成,是利用JDK API动态地在内存中构建代理对象;
3. 动态代理也叫做JDK代理、接口代理;
代理类所在的包为:java.lang.reflect.Proxy;
JDK实现动态代理只需要调用newProxyInstance方法,该方法在Proxy类中的定义如下:

public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException

由上可见,newProxyInstance方法是一个静态方法,该方法接收三个参数,参数的作用如下:

ClassLoader loader:用来指明生成代理对象应该使用哪个类加载器;
Class

public interface InvocationHandler {    public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable;}

下面来讲解invoke方法中的参数:
Object proxy:被调用的方法所在的代理实例;
Method method:代理实例所引用的接口方法相应的Method实例;
Object[] args:代理实例引用方法参数作为对象对象传递,或者为null当代理实例所引用的方法没有参数时;原生类型的参数如int等将会被自动装箱为Integer等;

代码示例:
在前面静态代理中例子的基础上,增加一个代理工厂类ProxyFactory, 该类的源代码如下:

public class ProxyFactory {    //维护一个目标对象    private Object target;    public ProxyFactory(Object target){        this.target=target;    }    public Object getProxyFactory(){        return Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                new InvocationHandler(){                    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{                        System.out.println("Get money!");                        Object returnValue=method.invoke(target, args);                        System.out.println("Sing finish!");                        return returnValue;                    }                }            );    }}

对例子进行测试,测试类App的源代码如下:

public class App {    public static void main(String args[]){        IStar star=new Star();        IStar proxy=(IStar) new ProxyFactory(star).getProxyFactory();        System.out.println(proxy.getClass());        proxy.sing();    }}

输出结果为:

总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理;

1.3 Cglib代理

在前面提到的动态代理或者静态代理中,目标对象都必须是一个实现接口的对象。但是,有时候目标对象并没有实现接口,这时候就需要采用目标对象的子类来实现代理,这种方式叫做:Cglib代理;
Cglib代理也叫做子类代理,通过在内存中构建一个目标对象的子类来扩展目标对象的功能;
Cglib是一个高性能的代理生成包,它能够在运行期动态地扩展目标对象或者实现接口,因此被许多AOP框架所使用,例如Spring AOP;为它们提供方法的拦截;
Cglib子类代理实现的方法:
1. 需要引入Cglib的jar包,但是spring的核心包已经包含了Cglib的功能,所以直接引入spring-core-xxx.jar包即可
2. 引入功能包之后,就可以在内存中动态地构建子类;
3. 代理的类不能为final类;
4. 如果目标对象的方法为final/static,那么目标对象的方法将不会被执行;

示例如下:
目标类UserDao:

public class UserDao {    public void save(){        System.out.println("save data successfully!");    }}

代理类工厂ProxyFactory:

import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ProxyFactory implements MethodInterceptor{    private Object target;    public ProxyFactory(Object target){        this.target=target;    }    public Object getProxyInstance(){        Enhancer en=new Enhancer();        en.setSuperclass(target.getClass());        en.setCallback(this);        return en.create();    }    public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{        System.out.println("start transaction!");        Object returnValue=method.invoke(target, args);        System.out.println("submit transaction!");        return returnValue;    }}

测试类App:

public class App {    public static void main(String args[]){        UserDao user=new UserDao();        UserDao userP=(UserDao) new ProxyFactory(user).getProxyInstance();        userP.save();    }}

在Spring AOP编程中:
如果加入容器的目标对象实现了接口,那么采用JDK代理;
如果加入容器的目标对象没有实现接口,那么将会采用Cglib代理;

参考博文:http://www.cnblogs.com/cenyu/p/6289209.html

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ipad需要激活锁忘了id怎么办 小米非系统储存空间满了怎么办的 学生打暑假工被代理拖欠工资怎么办 华为畅享7plus进水了怎么办 华为荣耀6plus喇叭坏了怎么办 华为荣耀7x听筒声音小怎么办 华为智能手表开不了机了怎么办 华为手机重启忘记解锁密码怎么办 华为荣耀8手机开不开机怎么办 苹果5c手机开机密码忘了怎么办 魅蓝note6打王者掉帧怎么办 王者荣耀6月26日活动怎么办 荣耀v9玩刺激战场掉帧怎么办 华为荣耀v9进水开不开机怎么办 魅蓝2玩游戏超卡怎么办 华为手机突然黑屏开不了机怎么办 三星a9开机混合密码忘了怎么办 三星a9锁屏密码忘了怎么办 华为6x手机电源键失灵怎么办 华为手机更换主题后图标不变怎么办 手机屏幕上的拨打电话键没了怎么办 小米手机电用完了充不进去怎么办 华为微信分身版本过低怎么办 手机连接不上4g网络怎么办办 手机通话时老返回锁屏怎么办 小米手机指纹解锁按键不灵了怎么办 小米手机4s黑屏开不了机怎么办 手机老年机突然黑屏开不了机怎么办 苹果7P遮挡传感器就黑屏怎么办 手机被偷支付宝的钱被偷怎么办 oppo手机微信视频黑屏了怎么办 小米账号锁死插卡不显示卡怎么办 红米手机充电时不黑屏怎么办 酷派手机开机出现系统错误怎么办 乐视1s手机开不了机怎么办 酷派手机的信息文件夹不见了怎么办 酷派手机开机卡在开机界面怎么办 魅蓝拨号键老是闪退怎么办 手机上浏览器搜索时字在右边怎么办 酷比魔方平板开不开机怎么办 红米手机摔了有一角黑屏了怎么办