设计模式——代理模式

代理模式(Proxy Pattern)分为静态代理,动态代理。在Spring AOP,Mybatis中都得到了广泛的应用。

如图所示,房产中介就是一个典型的代理,先写一个简单的demo。

1 静态代理

  • 抽象角色:用接口或者抽象类,抽象出代理角色和真实角色的相同功能。

    public interface Rent {
        public void rent();
    }
    
  • 真实角色:被代理的角色

    public class Host implements Rent{
        public void rent(){
            System.out.println("我要出租房子!");
        }
    }
    
  • 代理角色:代理真实角色,还会做一些额外的操作。

    public class Proxy implements Rent{
        private Host host;
    
        public Proxy() {}
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            showHouse();
            signContract();
            fare();
            host.rent();
        }
    
        public void showHouse(){
            System.out.println("带客户看房子!");
        }
    
        public void fare(){
            System.out.println("收取中介费用!");
        }
        public void signContract(){
            System.out.println("签租赁合同!");
        }
    }
    
  • 访问的人:客户,访问代理角色。

    public class Client {
        public static void main(String[] args) {
            Host host=new Host();
            Proxy proxy=new Proxy(host);
            proxy.rent();
        }
    }
    

优点:

  • 真实角色功能纯粹,不用关心公共规则
  • 公共规则交给代理去做,实现了业务的分工
  • 公共规则发生变化,只需在代理中集中管理。

2 动态代理

  1. 动态代理角色跟静态代理一样。
  • 抽象角色:用接口或者抽象类,抽象出代理角色和真实角色的相同功能。

    public interface Rent {
        public void rent();
    }
    
  • 真实的角色:被代理的角色

    public class Host implements Rent{
        public void rent(){
            System.out.println("我要出租房子!");
        }
    }
    
  • 代理角色,在动态代理中,是指jvm运行时动态生成的一个对象,类型为com.sun.proxy.$Proxy0仅数字作为标志号会变化

    $Proxy0实现了真实角色实现的所有接口,可以强转为其中任意一个接口的类型(此处为Rent)。

    基于reflect包下面的Proxy类和InvocationHandler接口实现。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvocationHandler  implements InvocationHandler {
        //真实的角色
        public Rent  target;
    
        public void setTarget(Rent target) {
            this.target = target;
        }
        //通过反射获取真实角色实现的接口,相当于代理的角色
        public Object getProxy(){
            Object result= Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
            return result;
        }
        //通过反射调用真实角色的接口实现方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log(method.getName());//此处可增加定制化操作
            Object result=method.invoke(target,args);//调用目标接口指定方法
            fare();   //此处可增加定制化操作
            return result;
        }
        //增加打印日志特殊操作
        public void log(String msg){
            System.out.println("执行了"+msg+"方法");
        }
        //增加执行完成后特殊操作
        public void fare(){
            System.out.println("执行成功!");
        }
    }
    }
    
  • 访问的人:客户,访问代理角色。

    public class Client {
        public static void main(String[] args) {
            //真实的角色
           Host host=new Host();
           //获取代理的真实角色的方法处理类
           ProxyInvocationHandler proxyInvocationHandler=new ProxyInvocationHandler();
           //设置要代理的真实角色
           proxyInvocationHandler.setTarget(host);
           //获取真实角色实现的接口类,相当于代理的角色
           Rent rent= (Rent)proxyInvocationHandler.getProxy();
           //调用com.sun.proxy.$Proxy0的接口实现方法
           rent.rent();
        }
    

通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它不是InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行时动态生成的一个对象com.sun.proxy.$Proxy0,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

  1. 动态代理分为两类:基于接口的动态代理,基于类的动态代理。
  • 基于接口的动态代理:JDK
  • 基于类的动态代理:cglib
  • JAVA字节码实现:javasist
  1. 优点:

​ 在静态代理基础上,还拥有如下优点:

  • 一个动态代理类代理的是一个接口,一般对应的是一类业务。
  • 一个动态代理类可以代理多个类,只要这些类实现同一个接口即可。
posted @ 2021-02-08 15:52  每天向前一步  阅读(70)  评论(0编辑  收藏  举报