zoukankan      html  css  js  c++  java
  • JAVA中的代理模式

    代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

    代理模式的结构

      所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

      代理模式类图如下:

      

      在代理模式中的角色:

      ●  抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。

      ●  目标对象角色:定义了代理对象所代表的目标对象。

      ●  代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。

    源代码

      抽象对象角色

    public abstract class AbstractObject {
        //操作
        public abstract void operation();
    }

       目标对象角色

    public class RealObject extends AbstractObject {
        @Override
        public void operation() {
            //一些操作
            System.out.println("一些操作");
        }
    }

      代理对象角色

    public class ProxyObject extends AbstractObject{
        RealObject realObject = new RealObject();
        @Override
        public void operation() {
            //调用目标对象之前可以做相关操作
            System.out.println("before");        
            realObject.operation();        
            //调用目标对象之后可以做相关操作
            System.out.println("after");
        }
    }

      客户端

    public class Client {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            AbstractObject obj = new ProxyObject();
            obj.operation();
        }
    
    }

        从上面的例子可以看出代理对象将客户端的调用委派给目标对象,在调用目标对象的方法之前跟之后都可以执行特定的操作。

    上面介绍了代理模式相关的概念和基础代码结构,下面详细介绍代理模式的实现。

    这里给出一个实际的业务需求:在对数据表USER进行操作时,需要记录下用户的操作日志,这里简单的进行add、query操作。

    1. 静态代理

    静态代理的代码结构和上面给出的结构相同,通过继承同一个接口,代理类调用执行被代理类,执行方法。

    /**用户Vo
     * Created by litao on 15/11/6.
     */
    public class UserVo {
        private String name;
        private int age;
        private int id;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    }

    DBMock

    import java.util.HashMap;
    
    /**
     * Created by litao on 15/11/6.
     */
    public class UserDBMock {
    
        private static HashMap<Integer,UserVo> users=new HashMap<Integer, UserVo>();
    
        public static HashMap<Integer,UserVo> getUserDB()
        {
            return users;
        }
    }

    UserService

    /**
     * Created by litao on 15/11/6.
     */
    public interface UserService {
    
        public void add(UserVo userVo);
    
        public UserVo query(int id);
    
    }

    UserSreviceImpl

    import java.util.Map;
    
    /**
     * Created by litao on 15/11/6.
     */
    public class UserServiceImpl implements UserService {
    
    
        public void add(UserVo userVo) {
            Map users = UserDBMock.getUserDB();
            users.put(userVo.getId(), userVo);
    
        }
    
        public UserVo query(int id) {
            Map users = UserDBMock.getUserDB();
            Object obj = users.get(id);
            if (obj != null) {
                return (UserVo) obj;
            } else {
                return null;
            }
        }
    }

    这里可以看出,对db的操作,并没有记录相关的日志。

    Client

    /**
     * Created by litao on 15/11/6.
     */
    public class Client {
    
        public static void main(String[] args)
        {
            Client client =new Client();
            client.normalOp();
    
        }
    
        public void normalOp()
        {
            UserVo userVo=new UserVo();
            userVo.setId(1);
            userVo.setAge(10);
            userVo.setName("zhangsan");
    
            UserService userService=new UserServiceImpl();
    
            userService.add(userVo);
            userVo=userService.query(1);
    
            System.out.println(userVo.getName());
        }
    
    }

    这里,只打印了"zhangsan",没有记录对用户的操作日志。

     为了记录用户的操作日志,增加proxy类 :UserServiceImplProxy

    /**
     * Created by litao on 15/11/6.
     */
    public class UserServiceImplProxy implements UserService {
    
        private UserService userService=new UserServiceImpl();
    
        public void add(UserVo userVo) {
            System.out.println("add user,userName:"+userVo.getName());
            userService.add(userVo);
        }
    
        public UserVo query(int id) {
            System.out.println("query user,id:"+id);
            return userService.query(1);
        }
    }

    修改Client代码:

    /**
     * Created by litao on 15/11/6.
     */
    public class Client {
    
        public static void main(String[] args)
        {
            Client client =new Client();
            client.proxyOp();
    
        }
    
        public void proxyOp()
        {
            UserVo userVo=new UserVo();
            userVo.setId(1);
            userVo.setAge(10);
            userVo.setName("zhangsan");
    
            UserService userService=new UserServiceImplProxy();
    
            userService.add(userVo);
            userVo=userService.query(1);
    
            System.out.println(userVo.getName());
        }
    
    }

    程序执行结果:

    add user,userName:zhangsan
    query user,id:1
    zhangsan

    可以看出在增加用户和查询用户时,记录了相关的操作日志。

    2. 动态代理

    在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

    InvocationHandler:

    InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 
    
    Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

    每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
    proxy:  指代我们所代理的那个真实对象
    method:  指代的是我们所要调用真实对象的某个方法的Method对象
    args:  指代的是调用真实对象某个方法时接受的参数

    接下来我们来看看Proxy这个类:

    Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

    Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

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

    Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

    这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    
    loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    
    interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
    
    h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

    下面来介绍,怎么使用动态代理来实现上面的需求。

    InvocationHandler类:
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * Created by litao on 15/11/7.
     */
    public class UserServiceInvokeHandle implements InvocationHandler {
    
    
        private UserService userService;
    
    
        public UserServiceInvokeHandle(UserService userService)
        {
            this.userService=userService;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if("query".equals(method.getName()))
            {
                System.out.println("query user,id:"+args[0]);
            }
            else if("add".equals(method.getName()))
            {
                System.out.println("add user,userName:"+args[0]);
            }
            return method.invoke(userService,args);
        }
    }

    Client:

    import java.lang.reflect.Proxy;
    
    /**
     * Created by litao on 15/11/6.
     */
    public class Client {
    
        public static void main(String[] args)
        {
            Client client =new Client();
            client.proxyDynamicOp();
    
        }
    
        public void proxyDynamicOp()
        {
            UserVo userVo=new UserVo();
            userVo.setId(1);
            userVo.setAge(10);
            userVo.setName("zhangsan");
    
            UserService userService=new UserServiceImpl();
    
            UserService userServiceProxy=(UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserServiceInvokeHandle(userService));
    
    
            userServiceProxy.add(userVo);
            userVo=userServiceProxy.query(1);
    
            System.out.println(userVo.getName());
        }
    
    }

    执行结果:

    add user,userName:UserVo@4617c264
    query user,id:1
    zhangsan

    这里 Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserServiceInvokeHandle(userService)); 负责来生成了代理类,需要注意的是,这里要求被代理类一定有一个接口。

    3. cglib实现代理类

    上面说了,使用Proxy.newProxyInstance()来生成代理类,要求代理类一定有一个接口,使用cglib可以解决代理类没有接口的问题。 

    cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。cglib封装了asm,可以在运行期动态生成新的class。简单的说cglib是通过在运行期改变类的字节码结构,来实现代理的。 当然实现代理类,也只是cglib的一个小功能。

    这里为了验证被代理类没有接口的情况下也可以实现代理,将ServiceUserImpl 的接口去掉。

    ServiceUserImpl

    import java.util.Map;
    
    /**
     * Created by litao on 15/11/6.
     */
    public class UserServiceImpl {
        public void add(UserVo userVo) {
            Map users = UserDBMock.getUserDB();
            users.put(userVo.getId(), userVo);
        }
    
        public UserVo query(int id) {
            Map users = UserDBMock.getUserDB();
            Object obj = users.get(id);
            if (obj != null) {
                return (UserVo) obj;
            } else {
                return null;
            }
        }
    }

    代理类:UserServiceCglibProxy

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by litao on 15/11/7.
     */
    public class UserServiceCglibProxy implements MethodInterceptor {
    
        public UserServiceImpl getInstance(Class target) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target);
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return (UserServiceImpl)enhancer.create();
        }
    
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if("query".equals(method.getName()))
            {
                System.out.println("query user,id:"+args[0]);
            }
            else if("add".equals(method.getName()))
            {
                System.out.println("add user,userName:"+args[0]);
            }
            return methodProxy.invokeSuper(o, args);
        }
    }

    Client:

    import java.lang.reflect.Proxy;
    
    /**
     * Created by litao on 15/11/6.
     */
    public class Client {
    
        public static void main(String[] args)
        {
            Client client =new Client();
            client.proxyDynamicCglibOp();
    
        }
    
        public void proxyDynamicCglibOp()
        {
            UserVo userVo=new UserVo();
            userVo.setId(1);
            userVo.setAge(10);
            userVo.setName("zhangsan");
    
            UserServiceImpl userService=new UserServiceCglibProxy().getInstance(UserServiceImpl.class);
    
            userService.add(userVo);
            userVo=userService.query(1);
    
            System.out.println(userVo.getName());
        }
    }

    执行结果:

    add user,userName:UserVo@4459eb14
    query user,id:1
    zhangsan

    需求增加: 如果只希望对用户的add操作才需要记录日志,怎么通过代理类的方式实现。

    1. 静态代理类的实现方式: 只在代理类中的add方法中,增加日子记录。 

    2. 动态代理类的实现方法 : 

        使用InvocationHandler: 在invoke方法处,先判断Method方法名称,然后进行日子记录。

        使用MethodInterceptor:a. 在intercept方法处,先判断Method方法名称,然后在add方法类进行日子记录。

                                           b.使用CallbackFilter类进行身份验证。

    这里给出使用CallbackFilter来实现上面的功能。 

    增加OpRoleFilter类

    import net.sf.cglib.proxy.CallbackFilter;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by litao on 15/11/7.
     */
    public class OpRoleFilter implements CallbackFilter {
        private static final int LOG_NEED     = 0;
        private static final int LOG_NOT_NEED = 1;
    
        public int accept(Method method) {
            if("query".equals(method.getName()))
            {
                return LOG_NOT_NEED;
            }
            else
            {
                return LOG_NEED;
            }
    
        }
    }

    修改UserServiceCglibProxy类

    import net.sf.cglib.proxy.*;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by litao on 15/11/7.
     */
    public class UserServiceCglibProxy implements MethodInterceptor {
    
        public UserServiceImpl getInstance(Class target) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target);
            // 回调方法
            enhancer.setCallbacks(new Callback[]{this, NoOp.INSTANCE});
            enhancer.setCallbackFilter(new OpRoleFilter());
            // 创建代理对象
            return (UserServiceImpl) enhancer.create();
        }
    
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if ("query".equals(method.getName())) {
                System.out.println("query user,id:" + args[0]);
            } else if ("add".equals(method.getName())) {
                System.out.println("add user,userName:" + args[0]);
            }
            return methodProxy.invokeSuper(o, args);
        }
    }

    执行结果:

    add user,userName:UserVo@5a2e4553
    zhangsan

    这里Enhancer类中增加了OpRoleFilter类,method名称为query的accept()返回1,其他的方法返回0,

    Enhancer增加了两个Callback:this, NoOp.INSTANCE,其中NoOp.INSTANCE表示在调用代理类的时候,直接走被代理类的代码,不走代理类“修改”的代码。accept()的返回值对应了callback。这里0对应this,1对应NoOp.INSTANCE

    参考文章:

    1.  java的动态代理机制详解  

    2. CGlib简单介绍 

    3. java动态代理(JDK和cglib

  • 相关阅读:
    51nod1042
    51nod1009
    分库分表Mycat总结
    RocketMQ事务消息实现分析
    RocketMQ消费模式
    mysql中的隐式转换总结
    EXPLAIN用法和结果分析
    MySQL日期时间处理函数总结
    RocketMQ在windows环境下的安装
    深入分析Synchronized原理
  • 原文地址:https://www.cnblogs.com/limingluzhu/p/4942169.html
Copyright © 2011-2022 走看看