java设计模式-代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
静态代理
静态代理其实挺简单的,我们把真正要实现功能的对象称作目标对象,实现代理功能的对象称作代理对象。在我们这个例子中,要实现的功能是在userDao中保存数据。
首先有一个接口userDao,目标对象UserDaoImpl和代理对象UserDaoProxy都实现了这个接口。这样也保证了两个类都会有同名的方法(这里姑且叫做代理方法好了)。既然是代理,那么代理对象首先要和目标对象有联系,所以代理类里面引用了目标对象。当我们要调用目标对象的代理方法时,实际上是通过调用代理对象的代理方法,代理对象的代理方法中再去调用目标对象的代理方法,这种间接调用的方法实现的。下面来看看代码:
UserDao.java 接口
public interface UserDao {
void save();
}
UserDaoImpl.java 目标对象
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("----保存数据成功------");
}
}
UserDaoProxy.java 代理对象
public class UserDaoProxy implements UserDao {
UserDao userDao = null;
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("----开始事务----");
userDao.save();
System.out.println("----结束事务----");
}
}
调用
public static void main(String[] args) {
UserDaoImpl userDaoImpl = new UserDaoImpl();
//将目标对象传给代理对象
UserDaoProxy userDaoProxy = new UserDaoProxy(userDaoImpl);
userDaoProxy.save();
}
打印结果
----开始事务----
----保存数据成功------
----结束事务----
可以看到,代理模式可以做到控制对一个对象的访问,在访问的前后可以做一些其他事项。静态方法的不足就是都是代码都是写死的,如果要增加一个代理方法,那么很多地方都要改变,所以就有了动态代理。
动态代理
动态代理主要来看看Jdk动态代理和Cglib动态代理,Spring和AspectJ的动态代理是基于前面两种来实现的。
jdk代理
jdk代理用到了java.lang.reflect.Proxy
这个类,当中有一个方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
这个方法返回了一个代理对象,这是一个什么代理对象呢?你可以指定它的类加载器,指定它实现的接口,指定它的调用处理器(分别是传入的三个参数)。
我们的目标对象还是实现了一个接口,这个没有变化,我们的代理类就不需要自己去实现接口了,因为前面提到的newProxyInstance方法已经帮我们指定它的接口了。所以我们调用newProxyInstance()这个方法,就可以返回代理对象了。
我们来一个代理工厂,负责生产代理对象。
ProxyFactory.java
public class ProxyFactory{
//这里维护目标对象
private UserDao userDao = null;
public ProxyFactory(UserDao userDao) {
this.userDao = userDao;
}
//获得代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务");
Object invoke = method.invoke(userDao, args);
System.out.println("结束事务");
return invoke;
}
});
}
}
调用
public static void main(String[] args) {
UserDaoImpl userDaoImpl = new UserDaoImpl();
UserDao proxyInstance = (UserDao) new ProxyFactory(userDaoImpl).getProxyInstance();
proxyInstance.save();
}
打印
开始事务
----保存数据成功------
结束事务
cglib代理
jdk动态代理要求被代理的对象必须要实现一个接口,如果没有实现接口就无法使用jdk动态代理。而cglib就不需要实现接口,cglib代理也叫子类代理,是通过被代理对象的子类,覆盖其中的方法实现增强,由于用的是继承, 所以不能对final修饰的对象进行代理。
UserDao.java–现在的目标对象,不需要继承接口
public class UserDao {
public void save() {
System.out.println("----保存数据成功------");
}
}
ProxyFactory.java
public class ProxyFactory{
private UserDao userDao = null;
public ProxyFactory(UserDao userDao) {
this.userDao = userDao;
}
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(userDao.getClass());
//3.设置回调函数
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("开始事务");
Object invoke = method.invoke(userDao, arg2);
System.out.println("关闭事务");
return invoke;
}
});
//4.创建子类(代理对象)
return en.create();
}
}
调用
public static void main(String[] args) {
UserDao userDao = new UserDao();;
UserDao proxyInstance = (UserDao) new ProxyFactory(userDao).getProxyInstance();
proxyInstance.save();
if( proxyInstance instanceof UserDao) {
System.out.println("yes");
}
}
这里我判断了一下代理对象是不是目标对象的子类。
打印结果
开始事务
----保存数据成功------
关闭事务
yes
我们来看看ProxyFactory.java中的 en.setCallback()这个方法。这里面的MethodInterceptor是一个接口,根据名字可以知道这是一个方法拦截器,可以看到这个接口里的intercept方法和前面jdk代理里面的invoke方法有点类似。
所以如果有接口可以使用jdk代理,没有接口则使用cglib代理,这也是spring中的策略。