先举个例子,来说明为什么要用代理模式。
假如有类A,有类的实例方法,test()。现在,有一个场景是,我想要在test原来的方法前执行一些任务,在原来方法完成后再执行一些任务。那么,按照我们原来的思路,我们需要做的是,修改test()方法。这个时候还不算复杂。如果test方法被两个位置调用。一个位置需要新的test方法,另一个位置需要旧的test方法。那么,修改方案无疑将变得更加复杂。
于是,代理模式被发现了。这种模式就是为了解决以上问题的。代理模式可以在不修改原方法的情况下,在原方法执行前后,分别添加新的任务。
接下来,我们分享三种代理模式的实现方法。
先假设有接口如下:
package com.syxsoa.dao;
publicinterface TestDao {
void test();
}
接口有一个实现类如下:
package com.syxsoa.dao;
publicclass TestDaoImpl implements TestDao{
publicvoid test() {
System.out.println("执行接口实现类");
}
}
我们要做的是,在不改变TestDaoImpl类的方法的情况下,在test方法前后分别执行一些任务。
第一种,称之为静态代理。代码如下:
package com.syxsoa.dao;
publicclass TestDaoProxy implements TestDao{
private TestDao testDao;
publicvoid test() {
System.out.println("执行静态代理方法1");
testDao = new TestDaoImpl();
testDao.test();
System.out.println("执行静态代理方法2");
}
}
调用方式如下:
TestDao testDao = new TestDaoProxy();
testDao.test();
静态代理方法非常容易理解。分析代码也可知,静态代理确实能达到我们想要的效果。
第二种,称之为动态代理。代码如下:
package com.syxsoa.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
publicclass ProxyFactory {
private Object target;
private Class c;
public ProxyFactory(Object target){
this.target = target;
this.c = target.getClass();
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(
c.getClassLoader(), c.getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行动态代理方法1");
Object re = method.invoke(target, args);
System.out.println("执行动态代理方法2");
returnre;
}
}
);
}
}
调用方法如下:
TestDao testDao1 = (TestDao) new ProxyFactory(new TestDaoImpl()).getProxyInstance();
testDao1.test();
动态代理的核心方法就是Proxy类的newProxyInstance方法。Proxy是Java反射库中的一个类。通过观察该类的原代码,很容易就会发现,动态代理可以成功的关键是利用了反射。如果对这部分的具体实现原理感兴趣,可以关注本博客的后续文章。
对比静态代理和动态代理可以发现,静态代理类需要和被代理类实现同一个接口,动态代理类则不需要。这就意味着,如果有某些同类型的类,我们要在这些类的前后执行相同的任务,那么,无疑用动态代理类更加明智。虽然动态代理类由于使用反射,有效率上的牺牲,但是,这种牺牲带来的价值是远大于牺牲的。
通过观察我们还发现,静态代理和动态代理能够实现的前提是,被代理类继承了一个接口。那么,如果某个类没有继承接口,或者说,不依赖其接口,能否实现代理模式呢?
当然可以,接下来,我们介绍一种Cglib模式。这种模式是Spring中采用的。因此,我们实现这种模式也需要依赖于spring-core包。
第三种,Cglib模式。代码如下:
package com.syxsoa.dao;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
publicclass SpringTestProxy implements MethodInterceptor{
private Object target;
public SpringTestProxy(Object target){
this.target = target;
}
public Object getProxyInstance(){
Enhancer en = new Enhancer();
en.setSuperclass(target.getClass());
en.setCallback(this);
returnen.create();
}
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("执行Cglib动态代理方法1");
Object re = arg1.invoke(target, arg2);
System.out.println("执行Cglib动态代理方法2");
returnre;
}
}
调用方法如下:
TestDaoImpl testDaoImpl = (TestDaoImpl)new SpringTestProxy(new TestDaoImpl()).getProxyInstance();
testDaoImpl.test();
观察发现,Cglib无论是在代理类的实现还是调用时,都只使用了TestDaoImpl类,而没有使用其接口TestDao。这也就是说,Cglib这种方法,对于任何类都可以轻易的写出其代理类,无论目标类是否继承了某个接口。这种方式无疑更加灵活一些。
理解一种设计模式的核心在于为什么要使用这种设计模式。