代理模式中的静态代理和动态代理细节

本文档为码歌老薛原创内容,转载请注明出处: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
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
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();
    }
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350