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

posted @ 2015-11-07 11:57  黎明露珠  阅读(296)  评论(0编辑  收藏  举报