【码歌】细致入微,代理模式中的静态代理和动态代理

Source

本文档为码歌老薛原创内容,转载请注明出处:https://www.jianshu.com/p/ed4cb94cefe9
本次主要分享代理模式当中的静态代理动态代理内容。如果有问题请联系老薛,老薛qq:1811112688

本文是为《基于微服务千万级流量项目实战》主题课做的辅助课,帮助各位夯实基础,快速上手。

想了解课程请找码歌老薛。

1-7:代理设计模式

i:静态代理

ii:动态代理

1-7-1:解决问题

i:安全性考虑:可以避免真实角色直接被引用,提高真实角色的安全性。
ii:功能增强:在真实角色执行之前和执行之后都可一增加真实角色的功能。
iii:简化开发,和代理角色打交道,不和真实角色有关,简化开发
iv:解耦合,将核心业务逻辑和辅助业务逻辑解耦

1-7-2:实际应用场景

i:Spring AOP(Aspect Oriented Programming) 面向切面编程的直接应用
ii:Struts 拦截器实现机制
iii:Thread和Runnable采用静态代理机制
iv:MyBatis 的面向接口编程

1-7-3:实现方案

i:jdk自带的动态代理
ii:CGLib

1-7-4:静态代理

1-7-4-1:编写方式

1:将业务抽象为接口

2:代理角色和真实角色都实现该接口

3:代理角色持有真实角色的引用,在执行具体核心业务时交由真实角色完成。

1-7-4-2:测试代码

抽象接口

/**
 * 真实角色和代理角色统一需要实现的接口
 *
 */
public interface Lease {
    /**
     * 带看
     */
    void takeLook();
    /**
     * 议价
     */
    void bargaining();
    /**
     * 签合同
     */
    void signContract();
    /**
     * 物业交割
     */
    void propertyDeliveries();
}

真实角色

/**
 * 真实角色:
 *  房东  不属于房东的业务我们可以采用空实现的方式
 */
public class Landlord implements Lease{
    @Override
    public void takeLook() {
    }
    @Override
    public void bargaining() {
    }
    @Override
    public void signContract() {
        System.out.println("老薛签字,这房子租你了");
    }
    @Override
    public void propertyDeliveries() {
    }
}

代理角色

public class LettingAgent implements Lease{
    //虽然我不能签合同,关键时刻我可以叫房东过来
    private Landlord landlord;
    public LettingAgent(Landlord landlord) {
        this.landlord = landlord;
    }
    @Override
    public void takeLook() {
        System.out.println("我带你看看房被");
    }
    @Override
    public void bargaining() {
        System.out.println("我说5000,你说3000,这样,200不二价");
    }
    @Override
    public void signContract() {
        System.out.println("我没有房本签不了合同,但是");
        landlord.signContract();//真实角色做事
    }
    @Override
    public void propertyDeliveries() {
        System.out.println("看看有没有少东西");
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        //创建真实角色
        Landlord landlord = new Landlord();
        //创建代理角色 代理角色持有真实角色的引用
        LettingAgent lettingAgent = new LettingAgent(landlord);
        //调用代理角色功能
        lettingAgent.takeLook();
        lettingAgent.bargaining();
        lettingAgent.signContract();//核心业务代码
        lettingAgent.propertyDeliveries();
    }
}

结果

我带你看看房被
我说5000,你说3000,这样,200不二价
我没有房本签不了合同,但是
老薛签字,这房子租你了 //**重要**这是真实角色做的事情
看看有没有少东西

类图

image

image

1-7-4-3:Thread和Runnable关系

源码分析

自定义实现类

class MyRun implements Runnable{
    @Override
    public void run() {
        System.out.println("实现类中的run方法。。。");
    }
}

Thread类

public class Thread implements Runnable {
    /* What will be run. */
    private Runnable target;//四:
    public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);//一:
    }
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
        this(group, target, name, stackSize, null, true);//二:
    }
    private Thread(ThreadGroup g, Runnable target, String name,
                   long stackSize, AccessControlContext acc,
                   boolean inheritThreadLocals) {
         if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();

        this.target = target;//重要 三:

    }
    //重要 五:
    public void run() {
        if (target != null) {
            target.run();//六:
        }
    }
}

Runnable接口

public interface Runnable {
     public abstract void run();
}

测试类

public class Test02 {
    public static void main(String[] args) {
        Runnable run = new MyRun();//创建实现类对象
        Thread thread = new Thread(run);//创建Thread对象实现类对象传入
        thread.start();//启动线程
    }
}

​ PS:其实这里的真实角色就是我们自定义的实现类对象,代理角色就是Thread,真正执行的是实现类中的run();方法。

类图

image

image

1-7-2:结论

1:可以在增强真实角色的功能,比如第一个案例中除了签合同之外就属于代理觉得对于真实角色的功能增强。

2:将核心业务代码屏蔽掉,用户只需要和代理角色打交道

1-7-5:动态代理

PS:

1-7-5-1:动态代理的实现方式

常见的动态代理的实现方法:

i:通过java提供的类完成动态代理

ii:CGLIB 实现动态代理

1-7-5-2:代理的核心本质

1:动态代理采用的技术是反射

2:动态代理核心内容就是监听何时需要代理对象,监听InvocationHadnler接口

1-7-5-3:需求内容

通过动态代理完成出租房屋等功能。

根据之前的代码,我们在整个出租房屋过程中分为:看房 面议 签合同 交钥匙这4个步骤,这里的核心业务应该是签合同,辅助业务为看房 面议 交钥匙。

1-7-5-4:测试用例1

编写方式

1: 创建通用的接口

2:创建真实角色类

3:创建实现处理器类,注意该类不是代理类,虽然写的内容和代理内容很像

4:编写测试类

测试代码

接口:接口内容和原来内容一致

真实角色:真实角色内容和原来内容一致

处理器类

public class LettingAgent implements InvocationHandler {
    //持有的真实角色引用
     //持有的真实角色引用
    private Lease landlord;
    public LettingAgent(Lease landlord) {
        this.landlord = landlord;
    }
    /**
     * @param proxy   代理对象,一般没啥用,经常使用的方式就是作为返回对象,让proxy进行多次调用。因为你在该方法返回的this并不是代理对象
     * @param method  需要执行的真实角色的 具体方法
     * @param args    真实角色方法的参数  实参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //判定是否要签合同
        if("signContract".equalsIgnoreCase(method.getName())){
            takeLook();
            bargaining();
            //执行具体的核心业务方法
            method.invoke(landlord,args);
            propertyDeliveries();
        }else{
            System.out.println("你在看看,房东就不出面了");
            takeLook();
        }

        return null;
    }
    public void takeLook() {
        System.out.println("我带你看看房被");
    }
    public void bargaining() {
        System.out.println("我说5000,你说3000,这样,200不二价");
    }
    public void propertyDeliveries() {
        System.out.println("看看有没有少东西");
    }
}

测试类

public class Test01 {
    public static void main(String[] args) {

        //1:创建真实角色对象
        Landlord landlord = new Landlord();

        //2:创建处理器对象
        LettingAgent lettingAgent = new LettingAgent(landlord);

        //3:通过java动态创建代理对象
        Lease proxy = (Lease) Proxy.newProxyInstance(landlord.getClass().getClassLoader(),new Class[]{Lease.class},lettingAgent);

        //4:执行具体方法
        proxy.takeLook();
    }
}

问题:在实际企业开发中不会这样编写代码,在原来的代码基础上,我们应该将获取代理对象的方法进行封装,通过ProxyFactory获取到。

1-7-5-5:测试用例2

编写方式

1: 创建通用的接口

2:创建真实角色类

3:创建实现处理器类,注意该类不是代理类,虽然写的内容和代理内容很像

4:编写代理工厂,代理对象通过工厂创建出来

5:编写测试类

测试代码

接口:

public interface Lease {
    /**
     * 签合同
     */
    void signContract();
}

真实角色:

public class Landlord implements Lease {
    @Override
    public void signContract() {
        System.out.println("老薛签字,这房子租你了,租金你看着给,老薛不差钱");
    }
}

处理器类:和原来一样

代理工厂:

public class ProxyFactory {
    /**
     * 获取代理对象
     * @return
     */
    public static Lease getInstance(){

        //创建真实角色对象
        Landlord landlord = new Landlord();

        //获取代理实现类对象
        InvocationHandler agent = new LettingAgent(landlord);

        //申请一个代理对象
        /**
         * classLoader: 代表那个接口或者类中的行为被执行时 需要我们去动态创建代理对象
         * interfaces:代表那个类实现的接口中的行为需要被执行时,创建代理对象
         */
        Class [] classArrays = {Lease.class};
        Lease proxy = (Lease) Proxy.newProxyInstance(Lease.class.getClassLoader(),classArrays,agent);
        //返回对象
        return proxy;
    }
}

测试类

public class Test01 {
    public static void main(String[] args) {
        //获取代理对象
        Lease proxy = ProxyFactory.getInstance();
        //调用方法
        proxy.signContract();
    }
}

好处:开发人员主要关注核心业务逻辑,代理角色的创建以及对于核心业务的功能增强都与开发人员无关。简化了开发

1-7-5-6:测试用例3

编写方式

1: 创建通用的接口

2:创建真实角色类

3:创建实现处理器类,注意该类不是代理类,虽然写的内容和代理内容很像

4:编写代理工厂,代理对象通过工厂创建出来,

增加重载方法,根据传入的真实角色对象的Class,创建对应的代理对象

5:编写测试类

测试代码

接口:

public interface Lease {
    /**
     * 签合同
     */
    void signContract();
}

真实角色1:

public class LandlordX implements Lease {
    @Override
    public void signContract() {
        System.out.println("老薛签字,这房子租你了,租金你看着给,老薛不差钱");
    }
}

真实角色2:

public class LandlordY implements Lease {
    @Override
    public void signContract() {
        System.out.println("老薛签字,这房子租你了,租金你看着给,老薛不差钱");
    }
}

处理器类:和原来一样

代理工厂:

public class ProxyFactory {
    /**
     * 获取代理对象
     * @return
     */
    public static Lease getInstance(Class<?> clz)throws Exception{
        //创建真实角色对象
        Lease lease = (Lease) clz.getConstructor(null).newInstance(null);

        //获取代理实现类对象
        InvocationHandler agent = new LettingAgent(lease);

        //申请一个代理对象
        /**
         * classLoader: 代表那个接口或者类中的行为被执行时 需要我们去动态创建代理对象
         * interfaces:代表那个类实现的接口中的行为需要被执行时,创建代理对象
         */
        Class [] classArrays = {Lease.class};
        Lease proxy = (Lease) Proxy.newProxyInstance(lease.getClass().getClassLoader(),classArrays,agent);
        //返回对象
        return proxy;
    }
}

测试类

public class Test01 {
    public static void main(String[] args) throws Exception {
        //获取代理对象
        Lease proxy1 = ProxyFactory.getInstance(LandlordMrX.class);
        //调用方法
        proxy1.signContract();

        //获取代理对象
        Lease proxy2 = ProxyFactory.getInstance(LandlordMrY.class);
        //调用方法
        proxy2.signContract();
    }
}

好处:核心业务和增强业务解耦,相当于代理角色和真实角色解耦。