代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式的结构
所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式类图如下:
在代理模式中的角色:
● 抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
● 目标对象角色:定义了代理对象所代表的目标对象。
● 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。
源代码
抽象对象角色
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
参考文章:
2. CGlib简单介绍