代理模式是什么?
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。举个例子:比如我们平常相亲前会找媒婆帮忙,此时媒婆的作用就是代理。
为什么要用代理模式?
因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
如何用代理模式?
举个例子,我有一个接口类和一个实现类
public interface HelloWorld {
public void sayHelloWorld(String name);
}
public class HelloWorldImpl1 implements HelloWorld {
public void sayHelloWorld(String name) {
System.out.println("hello,world," + name);
}
}
此时我想在调用HelloWordImpl的sayHelloWorld方法前输出:before,方法后输出:after,要怎么写?简单粗暴的写法为:
public class HelloWorldImpl1 implements HelloWorld {
public void sayHelloWorld(String name) {
before();
System.out.println("hello,world" + name);
after();
}
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
直接在实现类HelloWorldImpl1 中加入两个方法,分别输出before和after。但是我就是不想在HelloWorldImpl1 里面写呢?那我是不是要找个代理类帮我写?好,代理类来了:
public class HelloWorldProxy implements HelloWorld {
private HelloWorldImpl1 helloWorldImpl1;
public HelloWorldProxy(HelloWorldImpl1 helloWorldImpl1) {
this.helloWorldImpl1 = helloWorldImpl1;
}
public void sayHelloWorld(String name) {
before();
System.out.println("hello,world," + name);
after();
}
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
//写个manin方法验证下
HelloWorldImpl1 helloWordImpl1 = new HelloWorldImpl1();
HelloWorldProxy helloWorldProxy = new HelloWorldProxy(helloWordImpl1);
helloWorldProxy.sayHelloWorld("Jerry");
//结果输出:
before
hello,world,Jerry
after
此时HelloWorldProxy 就是我们的“媒婆”,也就是代理类。但是如果我们有另外一个实现类比如HelloWorldImpl2,要实现同样的功能那不是又要写另外一个代理类吗?
没错,这样写的话就不够通用,这种代理写法属于“静态代理”,我们需要改造一下,改成“动态代理”,如下:
1、使用JDK动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CommonProxy implements InvocationHandler {
private Object target;
public CommonProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invokeMethodResult = method.invoke(target, args);
after();
return invokeMethodResult;
}
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
我们新建一个CommonProxy,作为通用的代理包装类,这个类实现InvocationHandler接口,同时在构造方法中传入指定接口的实现类,在invoke方法中插入我们自己所需的before和after方法,有了这个代理包装类后,要怎么调用呢?如下:
//写个main方法测试
HelloWorldImpl1 helloWorld = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld);
// 创建代理实现类
HelloWorldImpl1 helloWorldProxy = (HelloWorldImpl1) Proxy.newProxyInstance(helloWorld.getClass()
.getClassLoader(), helloWorld.getClass().getInterfaces(), commonProxy);
helloWorldProxy.sayHelloWorld("Puma");
//执行结果:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.test.HelloWorldImpl1
at com.test.HelloWorldTest.main(HelloWorldTest.java:22)
。。好像报错了,查看报错信息是该代理类不能够转为HelloWorldImpl1 ,原因是因为JDK动态代理有个缺点:必须代理有接口的类,在Proxy.newProxyInstance调用后返回的必须是以接口来接收,我们修改下:
HelloWorldImpl1 helloWorld = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld);
// 创建代理类
HelloWorld helloWorldProxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
helloWorld.getClass().getInterfaces(), commonProxy);
helloWorldProxy.sayHelloWorld("Puma");
//执行结果:
before
hello,world,Puma
after
对于有点强迫症的人来说,在创建代理类的时候才不想一直写Proxy.newProxyInstance。。。这种代码,我们把它提取出来下放在CommonProxy 里
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CommonProxy implements InvocationHandler {
private Object target;
public CommonProxy(Object target) {
this.target = target;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invokeMethodResult = method.invoke(target, args);
after();
return invokeMethodResult;
}
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
//写个main方法测试
HelloWorldImpl1 helloWorld1 = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld1);
HelloWorld helloWorldProxy = commonProxy.getProxy();
helloWorldProxy.sayHelloWorld("Puma");
//执行结果:
before
hello,world,Puma
after
由上可见,CommonProxy 实现了通用的代理功能,下次如果HelloWorld 接口有新的实现类比如HelloWorldImpl2,则代理类不用改,只改接口实现类就行。
但是如我们刚才所说,JDK动态代理的缺点是必须代理有接口的类,如果我有一个类没有实现接口呢?那就需要另一种代理模式
2、使用CGlib动态代理
假设我有个新的类,没有实现任何接口 ,如下:
public class HelloWorldImpl3 {
public void sayHelloWorld(String name) {
System.out.println("hello,world," + name);
}
}
既然要使用代理,那就建个使用CGlib的代理类,如下:
package com.test;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CGlibProxy implements MethodInterceptor {
private Object target;
public CGlibProxy(Object target) {
this.target = target;
}
public static <T> T getProxy(T t) {
Enhancer enhancer = new Enhancer();
// 设置被代理的类
enhancer.setSuperclass(t.getClass());
// 设置回调方法,待会执行intercept方法
enhancer.setCallback(new CGlibProxy(t));
// 创建代理对象
return (T) enhancer.create();
}
public Object intercept(Object target, Method method, Object[] params, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, params);
after();
return result;
}
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
新建一个CGlib代理类CGlibProxy,实现MethodInterceptor 接口,在intercept方法里面,调用代理对象proxy调用被代理类(即HelloWorldImpl3 )的方法,并在方法执行前后插入我们所需要的信息,其中如何生成代理对象在getProxy方法里,通过创建Enhancer 对象来生成,我们来验证下:
//写个main方法
HelloWorldImpl3 helloWorldImpl3 = new HelloWorldImpl3();
HelloWorldImpl3 helloWorldImpl3Proxy = CGlibProxy.getProxy(helloWorldImpl3);
helloWorldImpl3Proxy.sayHelloWorld("Jerry");
//查看执行结果
before
hello,world,Jerry
after
总结:以上我们已经通过使用JDK动态代理和使用CGlib动态代理两种方式来创建代理对象,通过代理对象来帮助我们实现被代理类不能或者不好实现的功能,这个“媒婆”作用 不小,对于一些通用的功能,比如记录日志等等,使用非常方便,spring的aop主要也是用代理模式来实现,下一次我们聊聊:听说你知道spring aop的实现方式?