代理是设计模式中一种模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。
代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性
代理分为两种,一种是静态代理,另一种是动态代理。下面分别介绍一些是如何实现的。
1、静态代理
静态代理,创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
静态代理类与委托类,需要实现同一个接口。
例如,有接口UserManager
public interface UserManager{
public void addUser(User user);
}
有个具体的实现类,UserManagerImpl:
public class UserManagerImpl implements UserManager{
public void addUser(User user){
System.out.println("add user");
}
}
代理类代码如下:
public UserManagerProxy implements UserManager{
private UserManager userManager;
public UserManagerProxy(UserManager userManager){
this.userManager = userManager;
}
@Override
public void addUser(User user){
System.out.println(" start do some other thing.....");
userManager.addUser(user);
System.out.println(" finish do other thing.......");
}
}
具体使用:
public void test{
User user = new User();
UserManager userManager = new UserManagerProxy(new UserManagerImpl());
userManager.addUser(user);
}
print:
start do some other thing.....
add user
finish do other thing.......
静态代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类。并且只能代理一种类型。
2、动态代理
动态代理是在程序运行时运用反射机制动态创建而成。
动态代理有两种实现方式,一种是java自带的invocationhandler和第三方cglib,cglib不做详细介绍。
还是以静态代理的代码为例进行说明,UserManager、UserManagerImpl代码参考静态代理中的代码。
Proxy有个静态方法:getProxyClass(ClassLoader,interfaces),我们只要出入类(接口)加载器和其对应的接口(可以是多个),proxy代理类就会返回对应的一个class。
获取到代理的class后,可以调用getConstructor获取对应的Constructor对象。然后再调用该对象的newInstance方法返回对应的对象,该方法需要传递一个InvocationHandler的实现类,在这个实现类中,我们可以做一些我们想做的事情,例如日志输出。
首先看下获取代理类的方法
public Object getProxy(Object target) throws Exception{
Class proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader,target.getClass().getInerfaces());
Constructor c = proxyClass.getConstructor(InvocationHander.class);
Object proxy = c.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy,Mehtod m,Object[] args) throws Exception{
System.out.println("proxy:start to do some other things.......");
Object rst = method.invoke(target,args);
System.out.println("proxy:finish do some other things.......");
return rst;
}
});
return proxy;
}
//另外,获取对应的代理类也可以使用,Proxy.getInstance(ClassLoader,interface[],new InvocationHandler(){});来实现
调用方式:
public void static{
UserManager uerManager = new UserManagerImpl();
User user = new User();
UserManager proxy = (UserManager)getProxy(userManager);
proxy.addUser(user);
}
print:
proxy:start to do some other things.......
add user
proxy:finish do some other things.......
如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。