Java代理模式学习
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
无代理模式的代码
比如说这里有一个接口Hello
public interface Hello{
void say(String name);
}
别人基于这个接口实现了它的方法
public class HelloImpl implements Hello{
@Override
public void say(String name){
System.out.println("Hello " + name);
}
}
突然有一天要求你要在println方法的前后处理一些逻辑,怎么做呢?可能最直接的方法就是把这些逻辑直接写在say()里面了,比如
public class HelloImpl implements Hello{
@Override
public void say(String name){
before();
System.out.println("Hello! " + name);
after();
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
但是这样直接修改别人代码的方式肯定不够优雅,这样可以通过代理的方式来扩展该方法。
静态代理
现在写一个HelloProxy类,让它去调用HelloImpl的say()方法,在调用前后分别进行逻辑处理不就可以了吗
public class HelloProxy implements Hello{
private Hello hello;
public HelloProxy(){
hello = new HelloImpl();
}
@Override
public void say(String name){
before();
System.out.println("Hello! " + name);
after();
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
写一个main方法测试一下
public static void main(String[] args){
Hello helloProxy = new HelloProxy();
helloProxy.say("Jack");
}
运行一下结果为
before
Hello! Jack
after
原来HelloProxy就是所谓的“代理模式”,但这仅仅是最初级的代理模式,一旦接口增加方法,目标对象与代理对象都要维护
JDK动态代理
动态代理有以下特点:
- 代理对象,不需要实现接口
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
- 动态代理也叫做:JDK代理,接口代理
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable{
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
DynamicProxy 类中,定义了一个Object类型的target变量,他就是被代理的目标对象,通过构造函数来初始化(“注入”),DynamicProxy实现了InvocationHandler,它必须实现接口的invoke方法。
写一个mian方法看看实际使用:
public static void main(String[] args){
Hello hello = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloProxy = (hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxy
);
helloProxy.say("Jack");
}
打印结果和前面一样
before
Hello! Jack
after
但是Proxy.newProxyInstance这个写的感觉有点复杂了,于是重构一下DynamicProxy代码
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable{
before();
Object result = method.invoke(target, args);
after();
return result;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(){
return (T) Proxy.Proxy.newProxyInstance(
target .getClass().getClassLoader(),
target .getClass().getInterfaces(),
this
);
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
现在main方法可以写得简单一点了
public static void main(String[] args){
Hello hello = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloProxy = dynamicProxy.getProxy();
helloProxy.say("Jack");
}
CGlib动态代理
JDK动态代理并不是万能的,比如要代理一个没有任何接口的类,它就没有用武之地了,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理。比如Spring和Hibernate都用上了这种代理
有了上面的基础,实现它也不难
public class CGLibProxy implements MethodInterceptor{
private static CGLibProxy instance = new CGLibProxy();//这里使用到了单例模式
private CGLibProxy(){//限制外界去new CGLibProxy
}
public static CGLibProxy getInstance(){
return instance;
}
public <T> T getProxy(Class<T> cls){
return (T) Enhancer.create(cls, this);
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
//执行目标对象的方法
Object result = proxy.invokeSuper(obj, args);
after();
return result;
}
private void before(){
System.out.println("before");
}
private void after(){
System.out.println("after");
}
}
现在main方法可以更简单了,当然结果也是一样的
public static void main(String[] args){
Hello helloProxy = CGLibProxy.getInstance().getProxy(HelloImpl.class);
helloProxy.say("Jack");
}