java代理模式介绍

1、代理模式

代理模式是一种比较简单易懂的设计模式,通俗讲就是通过代理对象来代替真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能,扩展目标对象的功能。

代理模式的作用主要是扩展目标对象的功能,可以在调用目标对象的方法前后增加一些自定义的操作。

2、静态代理

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。

上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。

静态代理实现步骤:

  1. 定义一个接口及其实现类;
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自定义的操作。

下面通过代码展示:

  1. 定义接口
    1
    2
    3
    4
    package com.dodo.proxy;
    public interface SendService {
        String sendMsg(String message);
    }
  2. 创建原目标类并实现上面的接口
    1
    2
    3
    4
    5
    6
    7
    8
    package com.dodo.proxy;
    public class SendServiceImpl implements SendService {
        @Override
        public String sendMsg(String message) {
            System.out.println("发送短信:" + message);
            return message;
        }
    }
  3. 创建代理类并实现上面的接口,并将目标类注入到代理类种
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.dodo.proxy;
    public class SendServiceProxy implements SendService {
        private final SendService sendService;
        public SendServiceProxy(SendService sendService) {
            this.sendService = sendService;
        }
        @Override
        public String sendMsg(String message) {
            System.out.println("代理前操作……");
            sendService.sendMsg(message);
            System.out.println("代理后操作……");
            return message;
        }
        public static void main(String[] args) {
            SendService sendService = new SendServiceImpl();
            SendServiceProxy proxy = new SendServiceProxy(sendService);
            proxy.sendMsg("信息内容");
        }
    }
  4. 打印结果展示
    1
    2
    3
    代理前操作……
    发送短信:信息内容
    代理后操作……

     

3、动态代理

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。从JVM角度来说,动态代理是在运行过程中动态生成类字节码,然后加载到JVM中。

就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理CGLIB 动态代理等等。

3.1、JDK动态代理

在JDK动态代理中InvocationHandler接口和Proxy类是最重要的两个核心。

jdk动态代理实现步骤:

  1. 定义一个接口及其实现类
  2. 自定义实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中调用目标对象的方法并做一些额外的操作。
  3. 定义一个类通过Proxy.newProxyInstance来创建代理对象。

下面通过代码展示:

  1. 定义一个接口及其实现类
    1
    2
    3
    4
    package com.dodo.proxy;
    public interface SendService {
        String sendMsg(String message);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    package com.dodo.proxy;
    public class SendServiceImpl implements SendService {
        @Override
        public String sendMsg(String message) {
            System.out.println("发送短信:" + message);
            return message;
        }
    }
  2. 自定义实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中调用目标对象的方法并做一些额外的操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.dodo.proxy.jdkProxy;
     
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
     
    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理前操作……");
            Object result = method.invoke(target, args);
            System.out.println("代理后操作……");
            return result;
        }
    }//invoke()
  3. 定义一个类通过Proxy.newProxyInstance来创建代理对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.dodo.proxy.jdkProxy;
     
    import com.dodo.proxy.SendService;
    import com.dodo.proxy.SendServiceImpl;
     
    import java.lang.reflect.Proxy;
     
    public class JdkProxyfactory {
        /**
         * 主要通过Proxy.newProxyInstance()方法获取某个类的代理对象
         *
         * @param target
         * @return
         */
        public static Object getProxy(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new MyInvocationHandler(target));
        }
     
        public static void main(String[] args) {
            SendService sendService = (SendService) JdkProxyfactory.getProxy(new SendServiceImpl());
            sendService.sendMsg("Hello World");
        }
    }
  4. 打印结果展示
    1
    2
    3
    代理前操作……
    发送短信:Hello World
    代理后操作……

缺点:只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

3.2、CGLIB动态代理

在CGLIB动态代理中MethodInterceptor接口和Enhancer类是两个重要的核心。

CGLIB动态代理实现步骤:

  1. 定义一个类
  2. 自定义实现MethodInterceptor接口的类,并重写intercept方法,intercept用于拦截增强被代理类的方法;和JDK动态代理中的invoke方法类似;
  3. 通过Enhancer类的create()方法创建代理类

下面通过代码展示:

  1. 定义一个类
    1
    2
    3
    4
    5
    6
    7
    package com.dodo.proxy.cglibProxy;
    public class CglibSendMsgService {
        public String sendMsg(String message) {
            System.out.println("发送短信:" + message);
            return message;
        }
    }
  2. 自定义实现MethodInterceptor接口的类,并重写intercept方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.dodo.proxy.cglibProxy;
     
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
     
    public class MyInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("代理前操作……");
            Object object = methodProxy.invokeSuper(o, args);
            System.out.println("代理后操作……");
            return object;
        }
    }
  3. 通过Enhancer类的create()方法创建代理类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.dodo.proxy.cglibProxy;
     
    import net.sf.cglib.proxy.Enhancer;
     
    public class CglibProxyFactory {
        public static Object getProxy(Class clazz) {
            Enhancer enhancer = new Enhancer();
            enhancer.setClassLoader(clazz.getClassLoader());
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(new MyInterceptor());
            return enhancer.create();
        }
     
        public static void main(String[] args) {
            CglibSendMsgService aliSendMsgService = (CglibSendMsgService) CglibProxyFactory.getProxy(CglibSendMsgService.class);
            aliSendMsgService.sendMsg("Hello World");
        }
    }
  4. 打印结果展示
    1
    2
    3
    代理前操作……
    发送短信:Hello World
    代理后操作……

3.3、JDK动态代理和CGLIB动态代理对比

JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。

另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。

 

posted @   胡桃里等你  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示