浅谈几种设计模式--代理模式

代理

代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。

1.静态代理

2.动态代理

3.cglib代理:如果目标对象没有实现接口,用Cglib代理

静态代理总结:

1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.

2.缺点:

因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

如何解决静态代理中的缺点呢?

答案是可以使用动态代理方式

spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。

静态代理

/**
 * 创建Person接口
 */
public interface Person {
        void giveMoney();//上交班费
}
/**
 * Student类实现Person接口。Student可以具体实施上交班费的动作
 */
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void giveMoney() {
       System.out.println(name + "上交班费50元");
    }
}
/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        stu.giveMoney();
    }
}
/**
 * 测试类
 */
public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
        Person zhangsan = new Student("张三");
        
        //生成代理对象,并将张三传给代理对象
        Person monitor = new StudentsProxy(zhangsan);
      
        //班长代理上交班费
        monitor.giveMoney();
    }
}
/**
 * 学生代理类
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        System.out.println("张三最近学习有进步!");
        stu.giveMoney();
    }
}

可以看到,只需要在代理类中帮张三上交班费之前,执行其他操作(方法)就可以了。

这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。

这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

动态代理

public void giveMoney() {
        beforeMethod();//调用被代理方法前加入处理方法
        stu.giveMoney();
}
/**
 * 创建Person接口
 */
public interface Person {
     void giveMoney(); //上交班费
}
public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    
    @Override
    public void giveMoney() {
        try {
          //假设数钱花了一秒时间
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       System.out.println(name + "上交班费50元");
    }
}
public class MonitorUtil {
    
    private static ThreadLocal<Long> tl = new ThreadLocal<>();
    
    public static void start() {
        tl.set(System.currentTimeMillis());
    }
    
    //结束时打印耗时
    public static void finish(String methodName) {
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
    }
}
public class StuInvocationHandler<T> implements InvocationHandler {
    T target; //invocationHandler持有的被代理对象
    public StuInvocationHandler(T target) {
       this.target = target;
    }
    /**
     * proxy:代表动态代理对象     method:代表正在执行的方法 args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
        //代理过程中插入监测方法,计算该方法耗时
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

测试类

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

        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");

       //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
        
        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

       //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

总结一下: 上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。

本文分享自微信公众号 - Java后端技术栈(t-j20120622)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转行程序员

2019最新14个国外知名开发者社区

端午节后第一天不是很忙,给自己和粉丝整理了一批国外优秀开发者社区,是时候给自己充充电顺便补补英语了,一定要多看看哦。

14730
来自专栏芋道源码1024

某团面试题:JVM 堆内存溢出后,其他线程是否可继续工作?

最近网上出现一个美团面试题:“一个线程OOM后,其他线程还能运行吗?”。我看网上出现了很多不靠谱的答案。这道题其实很有难度,涉及的知识点有jvm内存分配、作用域...

11630
来自专栏耕耘实录

在CentOS/RHEL 7.X安装 EPEL repo 的方法

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转...

13420
来自专栏耕耘实录

Docker中未指定挂载点容器间volume卷的数据共享

在实际使用过程中,我们可能会经常遇到容器间数据共享的情况,怎么处理呢?通过 docker 命令中的一些选项,我们即可完成容器间的数据共享。

13430
来自专栏软测小生

自动化测试上传文件之Sikuli图片识别代替AutoIT

这里介绍一下另外一种解决方案:使用Sikuli进行图片识别,来对Windows弹出框或者其他Windows窗口进行一系列的操作。

20660
来自专栏风吹杨柳

java内部类的作用(二)----隐藏作用

局部内部类有一个优势:即对外部世界完全可以隐藏起来,在这个方法类中的其它方法或者代码都不能调用这个内部类。更不用说其它的类了

12730
来自专栏猿GG编程

5步骤完成springboot 整合freemarker模板引擎

freemarker是个不错的模板引擎,在网页静态化的模板使用中口碑很好,今天就用springboot来整合这个模板。

30330
来自专栏林雍岷

网站内容重复影响SEO概率很小

网站内容,重复性的出现会影响SEO吗?在SEO圈子里面似乎有很多人对重复内容认知错误,我相信很多人都会听到,如果你的网站上有重复内容,将受到搜索引擎处罚。处罚就...

8130
来自专栏java架构1+1

「android」摆正姿势,dagger2,从精通到上瘾

dagger2的大名我想大家都已经很熟了,它是解决Android或java中依赖注入的一个类库(DI类库)。当我看到一些开源的项目在使用dagger2时,我也有...

22730
来自专栏软测小生

Jenkins可用环境变量以及使用方法

在这之前,没有自己配置过Jenkins,都是照猫画虎,Copy原来已经配好的项目过来修修改改,一直想不明白比如BUILD_NUMBER之类的东西是哪来的(其实是...

27230

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励