您现在的位置是:首页 > 文章详情

细说代理模式

日期:2020-09-12点击:466

代理模式,大家应该都不陌生,很多框架底层都用了代理模式,像spring、mybatis等。虽然大家都听说过代理模式,但是可能也并不是那么地了解,本文将说一下常用的代理模式。

一、代理模式介绍

代理模式其实就是找替身,要去办一件事儿,自己不去,找人代替你去,这就是代理模式。在程序中就是,为对象提供一个替身,控制替身去访问目标对象,这样做的好处是,除了目标对象能提供的功能外,还可以让替身多做一些活,即可以扩展目标对象的功能。被代理的可以是远程对象、创建时开销很大的对象或者需要安全控制的对象。

代理模式主要分为以下三种:

  • 静态代理
  • 动态代理(又叫JDK代理、接口代理)
  • cglib代理(也属于动态代理的范畴)

二、静态代理

「1、静态代理介绍:」

使用静态代理的时候,需要定义接口或者父类,被代理的对象和代理对象需要一起实现相同的接口或者继承相同的父类。

「2、应用实例:」

  • 定义一个接口: TeacherDao
  • 定义被代理的对象: TeacherDaoImpl,需要实现 TeacherDao
  • 定义代理对象: TeacherDaoProxy,也需要实现 TeacherDao
  • 要调用 TeacherDaoImpl方法时,需要先创建 TeacherDaoProxy对象,然后创建 TeacherDaoImpl对象,将 TeacherDaoImpl对象交给 TeacherDaoProxy对象,再调相关方法

代码实现:

  • TeacherDao.java:
public interface TeacherDao {
    void teach();
}
  • TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
    @Override
    public void teach() {
        System.out.println("今天又是没妹子的一天(ノへ ̄、)");
    }
}
  • TeacherDaoProxy.java:
public class TeacherDaoProxy implements TeacherDao {
    
    private TeacherDao target; // 被代理的对象
    
    public TeacherDaoProxy(TeacherDao target){
        this.target = target;
    }
    
    @Override
    public void teach() {
        System.out.println("代理开始");
        // 这里可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的前置通知
        target.teach();
        // 这里也可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的后置通知
        System.out.println("代理结束");
    }
}
  • Client.java:调用代理对象
public class Client {

    public static void main(String[] args){
        // 创建被代理的对象
        TeacherDao target = new TeacherDaoImpl();
        // 创建代理对象
        TeacherDaoProxy proxy = new TeacherDaoProxy(target);
        // 通过代理对象调用方法
        proxy.teach();
    }
}

「3、静态代理的优缺点:」

  • 优点:可以在不修改被代理对象的前提下扩展被代理的对象,做一些增强
  • 缺点:需要实现相同的接口或者继承相同的父类,所以代理类会很多,而且如果接口或者父类有改动,代理对象和被代理对象都需要维护

三、动态代理(JDK代理)

「1、动态代理介绍:」

代理对象不要实现接口,但是被代理对象还是需要实现接口的。动态代理对象的生成,利用的是JDK的API,反射包下的Proxy类,动态地在内存中构建代理对象。

「2、java.lang.reflect.Proxy:」

这个类有一个newProxyInstance方法,该方法接收三个参数,如下:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

「3、应用实例:」

  • 定义一个接口:TeacherDao
  • 定义被代理的对象:TeacherDaoImpl,需要实现TeacherDao
  • 定义一个代理工厂ProxyFactory,有一个getProxyInstance方法,需要传入被代理的对象,然后返回代理对象实例,通过代理对象调用被代理对象的方法

代码实现:

  • TeacherDao.java:
public interface TeacherDao {
    void teach();
}
  • TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
    @Override
    public void teach() {
        System.out.println("今天又是没妹子的一天(ノへ ̄、)");
    }
}
  • ProxyFactory.java
public class ProxyFactory {

    private Object target; // 被代理的对象

    public ProxyFactory(Object target){
        this.target = target;
    }

    // 给被代理的对象生成一个代理对象
    public Object getProxyInstance(){
        // 参数1:指定被代理对象的类加载器
        // 参数2:被代理对象实现的接口类型
        // 参数3:事件处理,执行被代理对象的方法
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("JDK代理开始");
                // 调用方法,args的方法的参数
                Object returnValue = method.invoke(target, args);
                System.out.println("JDK代理结束");
                // 将执行结果return
                return returnValue;
            }
        });
    }
}
  • Client.java:通过代理调用方法
public class Client {

    public static void main(String[] args){
        // 创建被代理的对象
        TeacherDao target = new TeacherDaoImpl();
        // 创建代理对象
        TeacherDao proxy = (TeacherDao) new ProxyFactory(target).getProxyInstance();
        // 通过代理对象调用被代理对象的方法
        proxy.teach();
    }
}

四、cglib代理

「1、cglib代理介绍:」

静态代理和动态代理,被代理的对象,都需要实现接口,如果一个类没实现任何接口的,那就要用cglib代理了。cglib代理也叫子类代理,它会在内存中构建一个子类对象,从而实现对被代理对象的扩展。cglib代理底层是通过一个叫ASM的字节码处理框架来转换字节码并生成新的类从而实现代理的。被代理的类不能为final,否则会报错。被代理对象的方法如果是final/static,就不会被拦截,即不会执行被代理对象额外的业务方法。

「2、应用实例:」

  • 首先要添加cglib相关依赖:
<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.3.0</version>
</dependency>
  • TeacherDaoImpl.java:
public class TeacherDaoImpl implements TeacherDao {
    @Override
    public void teach() {
        System.out.println("今天又是没妹子的一天(ノへ ̄、)");
    }
}
  • CglibProxyFactory.java:
// 需要实现MethodInterceptor并重写其方法
public class CglibProxyFactory implements MethodInterceptor {

    private Object target;

    public CglibProxyFactory(Object target){
        this.target = target;
    }

    /**
     * 返回target的代理对象
     * @return
     */
    public Object getProxyInstance(){
        // 1. 创建工具类
        Enhancer enhancer = new Enhancer();
        // 2. 设置父类
        enhancer.setSuperclass(target.getClass());
        // 3. 设置回调函数
        enhancer.setCallback(this);
        // 4. 创建子类对象,即代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("CGLIB代理开始");
        Object returnValue = method.invoke(target, args);
        System.out.println("CGLIB代理结束");
        return returnValue;
    }
}
  • Client.java:通过代理调用方法
public class Client {

    public static void main(String[] args){
        // 创建被代理的对象
        TeacherDaoImpl target = new TeacherDaoImpl();
        // 获取代理对象,并将被代理对象传给代理对象
        TeacherDaoImpl proxy = (TeacherDaoImpl) new CglibProxyFactory(target).getProxyInstance();
        // 执行方法,触发intecept方法,从而实现执行被代理对象的方法
        proxy.teach();
    }
}



-java开发那些事-
长按扫描二维码
关注我 获取更多内容
分享、点赞、在看三连


本文分享自微信公众号 - java开发那些事(javawebkf)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

原文链接:https://my.oschina.net/u/4602675/blog/4560230
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章