静态代理,一个代理类只能代理一个接口,好处的简单、好实现、好理解,坏处呢,举个例子,我们知道Mybatis逆向工程会生成N多个Mapper接口文件,如果每个Mapper接口都造一个代理类出来,然后呢,每个代理类做的事情又都是一样的——获取session、解析crud语句、执行crud、处理结果、结束session。
难道我们对此就束手无策了吗?不,使用动态代理,我们可以只造一个轮子就能代理everything。
动态代理分两种:Java动态代理 和 CGLib,其中Java动态代理只能代理实现了接口的类,而CGLib则可以代理一个没有实现任何接口的类。
1 JDK动态代理
实现InvocationHandler并重写invoke方法。
public interface Hello {
void say(String name);
}
public class HelloImpl implements Hello {
@Override
public void say(String name) {
System.out.println("Hello! " + name);
}
}
public class DynamicProxy implements InvocationHandler {
//能代理任意一个类
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
/**
* 能接收任意数量和类型的参数、任意的返回值、任意的异常抛出
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@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");
}
测试一下:
public class Test {
public static void main(String[] args) {
Hello hello = new HelloImpl();
DynamicProxy dynamicProxyHandler = new DynamicProxy(hello);
Hello helloProxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxyHandler
);
helloProxy.say("biglong");
}
}
打印出来是:
Before
Hello! biglong
After
——————————————— 割以咏志 ———————————————————————
以上Proxy.newProxyInstance的方法还是有点复杂,需要传入3个参数:
参数1:ClassLoader
参数2:该实现类的所有接口
参数3:动态代理对象
我们可以简化一下:
public class DynamicProxy2 implements InvocationHandler {
...
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
...
}
测试下:
public class Test2 {
public static void main(String[] args) {
Hello hello = new HelloImpl();
DynamicProxy2 dynamicProxy2 = new DynamicProxy2(hello);
Hello helloProxy = dynamicProxy2.getProxy();
helloProxy.say("biglong");
}
}
输出跟上面一样。
2 CGLib动态代理
如果一个类没有实现任何接口,以上JDK动态代理就成了废物,此时CGLib粉墨登场,CGLib可以代理没有接口的类——我们的Spring框架的AOP就用了这个。
public class CGLibProxy implements MethodInterceptor {
//单例模式(懒汉)
private static CGLibProxy instance = new CGLibProxy();
public static CGLibProxy getInstance() {
return instance;
}
private CGLibProxy() {
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> cls) {
return (T) Enhancer.create(cls, this);
}
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
before();
Object result = proxy.invokeSuper(target, args);
after();
return result;
}
private void before() {
System.out.println("Before");
}
private void after() {
System.out.println("After");
}
}
测试一下:
public class Test {
public static void main(String[] args) {
HelloImpl helloProxy = CGLibProxy.getInstance().getProxy(HelloImpl.class);
helloProxy.say("biglong");
}
}