设计模式——代理模式

代理模式

1.生活中:

代理就是一个人或者一个组织代表其他人去做一件事。在一些情况下,一个对象不行或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

2.官方:

代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并有代理对象控制对原对象的引用。


一,静态代理

在代理模式中的角色
●抽象主题角色:声明了目标对象和代理对象的共同接口,这样一来来任何可以使用目标对象的地方都可以使用代理对象
●真实主题角色:定义了代理对象所代表的目标对象
●代理主题角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象:代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象
代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是淡出的将调用传递给目标对象它可以增加一些真实主题里没有的功能


生活中的例子:过年加班比较忙,没空去买火车票,这时可以打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费。但要清楚票务中心自己并不卖票,只有火车站才真正卖票,票务中心卖给你的票其实是 通过火车站实现的。这点很重要! 
上面这个例子,你就是“客户”,票务中心就是“代理角色”,火车站是“真实角色”,卖票称为“抽象角色”!


代码


//抽象主题角色
//抽象角色声明真实对象和代理对象的共同接口
//抽象角色声明真实对象和代理对象的共同接口
 public interface TicketManager { 
     / **
             *售票
      * /
 void soldTicket(); 
    / **
             *改签
      * /
 void changeTicket(); 
    / **
             *退票
      * /
 void returnTicket();
}            
}


//真实主题角色
public class TicketManagerImpl implements TicketManager {
    @Override
    public void soldTicket() {
        System.out.println("售票");
    }

    @Override
    public void changeTicket() {
        System.out.println("改签");
    }

    @Override
    public void returnTicket() {
        System.out.println("退票");
    }
    /**
     *身份验证
      * /
 public void checkIdentity (){
 System 出去println “身份验证” );
    }
 }            



//代理主题角色(添加了身份验证功能)
public class StaticProxyTicketManager implements TicketManager{
    private TicketManager ticketManager;//目标对象的引用

    public StaticProxyTicketManager(TicketManager ticketManager) {
        this.ticketManager = ticketManager;
    }

    @Override
    public void soldTicket() {
        checkIdentity();
        ticketManager.soldTicket();
    }

    @Override
    public void changeTicket() {
        ticketManager.changeTicket ();
    }
 @覆盖
 公共无效returnTicket (){
 ticketManager returnTicket ();
    }
 / **
      *身份验证
      * /
 public void checkIdentity (){
 System 出去println “身份验证--------------” );
    }
 }
                
                




//第二个代理主题角色(添加了日志功能)
//代理类  实现同一个接口
public class LogProxy implements TicketManager{
    private TicketManager ticketManager;

    public LogProxy(TicketManager ticketManager) {
        this.ticketManager = ticketManager;
    }

    @Override
    public void soldTicket() {
        ticketManager.soldTicket();
        log();
    }

    @Override
    public void changeTicket() {
        ticketManager.changeTicket();
        log ();
    }
 @覆盖
 公共无效returnTicket (){
 ticketManager returnTicket ();
log ();
    }
 //增强
 private void log (){
 System 出去println “日志...” );
    }
 }

                        
                



//客户端
public class Test {
    public static void main(String[] args) {
        TicketManager tm = new LogProxy(new StaticProxyTicketManager(new TicketManagerImpl()));
        tm.soldTicket();
        tm.changeTicket();
        tm.returnTicket();
    }
}



结果:
身份验证----- 
售票 
日志... 
身份验证----- 
改签 
日志... 
身份验证----- 
改签 
日志... 


从上面例子可以看出客户端通过代理来购票而代理实际上不能卖票给客户,他实际上是通过目标对象卖票给客户的,也就是说他是通过真实主题的目标对象实现给客户端卖票的功能,他只是一个中介,但我们可以在它。里面增加一些功能,身份比如验证或者宣传打广告等其他的功能


静态代理类:在程序运行前,代理类的的.class文件就已经存在了,确定已被代理的对象
静态代理:
优点:
1。对真实对象进行封装,不会修改目标类的代码确定

1,多个不同类型的目标对象需要代理时,就需要建立多个代理类,造成类
的膨胀2,代码的冗余
3,编译期加入,不够灵活








二,动态代理

动态代理(Dynamic Proxy):相比静态代理,动态代理具有更强的灵活性,
他不用在设计实现时就指定某个代理类来代理哪一个被代理对象,
可把这种指定延迟到程序运行时由JVM实现。

所谓代理,就是需要代理类和被代理类有相同的对外接口或者说服务
所以代理类一般都必须实现了所有被代理类已实现的接口,因为接口就是指定了一系列对外服务的标准。


1.JDK实现动态代理

        正因为动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时,不用显示地让它实现与真实主题类(RealSubject)相同的接口(接口),从而把这种实现推迟到运行时。
        为了能让DynamicProxy类能够在运行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口,接口中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。 

        按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有接口都加载到JVM中,不然JVM怎么能够找到这些方法呢?明白了这个道理,那么我们就可以创建一个被代理类的实例,获 该实例的类加载器类加载器。 
所谓的类加载器类加载器,就是具有某个类的类定义,即类的内部相关结构(包括继承树,方法区等等)。 

        更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展,增强其功能,并且可以达到控制被代理对象的行为的目的。请详见下面代码中的DynamicProxy类,其中必须实现的调用()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作这是动态代理最明显的优点




代码:
public class DynamicProxyTicketManager implements InvocationHandler {

    private Object targetObject;
    /**
     * 反射,这样你可以在不知道具体的类的情况下,根据配置的参数去调用一个类的方法。
     * 在灵活编程的时候非常有用。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检查
        checkIdentity();
        Object ret = null;
        try {
            // 调用目标方法
            ret = method.invoke(targetObject, args);
            // 执行成功,打印成功信息
            log();
        } catch (Exception e) {
            e.printStackTrace();
            // 失败时,打印失败信息
            System.out.println("error-->>" + method.getName());
            throw e;
        }
        return ret;
    }

    /**
     * 目标的初始化方法,根据目标生成代理类
     * @param targetObject
     */
    public Object newProxyInstance(Object targetObject){
        this.targetObject=targetObject;
        /**
         * 第一个参数:目标对象的类加载器
         * 第二个参数:目标接口已实现的所有接口,而这些是动态代理类要实现的接口列表
         * 第三个参数:调用实现了InvocationHandler的对象生成动态代理实例,当你一调用代理,
         *              代理就会调用InvocationHandler的invoke方法
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObjectgetClass ()。getInterfaces (),
 this );
    }
 / **
      *身份验证
      * /
 public void checkIdentity (){
 System 出去println “身份验证--------------” );
    }
 public void log (){
 System 出去println “日志...” );
    }
 }                
                            
//客户端
public class Test {
    public static void main(String[] args) {
        DynamicProxyTicketManager dynamicProxyTicketManager=new DynamicProxyTicketManager();
        TicketManager tm=(TicketManager) dynamicProxyTicketManager.newProxyInstance(new TicketManagerImpl());

        tm.soldTicket();
        tm.changeTicket();
        tm.returnTicket();
    }
}


优点:

1.一个动态代理类更加简单了,可以解决创建多个静态代理的麻烦,避免不断的重复多余的代码
2.调用目标代码时,会在方法运行时动态的加入,决定你是什么类型,才调动,灵活。
缺点:
1.系统灵活了,但是相比而言,效率降低了,比静态代理慢一旦
2.动态代理比静态代理在代码的可读性上差了一点,不太容易理解
3.JDK动态代理只能对实现了接口的类进行代理

总结 

各有各的好,具体情况具体讨论


2.Cglib实现动态代理

        AOP的源码中使用了两种动态代理来实现拦截切入功能,jdk动态代理和cglib动态代理。
两种方法同时存在,各有优劣。jdk
        动态代理是由java内部的反射机制实现的.cglib动态代理代理底层是借助asm实现的。
        总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。(可以通过将asm生成的类进行缓存,这样解决ASM生成类过程低效的问题)。
        还有一点要注意,JDK动态代理的前提,必须是目标类基于统一的接口。否则JDK动态代理不能应用。
由此可以看出JDK动态代理使用有一定的局部性.cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。jdk
动态代理机制只能代理实现了接口的类,否则不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增 ,但因为采用的是继承,所以不能对最终修饰的类进行代理。







猜你喜欢

转载自blog.csdn.net/qq_41570691/article/details/80205184