java 代理模式
1.概述
Java中最常见的模式之一,简单来说,即在不改变目标对象源码的情况下,实现对目标对象的功能扩展。
举个栗子:码农在看P站
public class CodeFamer{ public void watchPSite(){ System.out.println("code famer is watching PSite"); } }
如果码农在看P站之前要准备卫生纸;看P站之后要洗手。
那么应该是这样的:
public class CodeFamer{ public void watchPSite(){ System.out.println("prepare toilet paper"); System.out.println("code famer is watching PSite"); System.out.println("handwashing"); } }
这样当然没问题,但是很显然无关紧要的准备纸巾/洗手这些操作影响到看片了......
也就是说改动了源码了,开闭原则(扩展开放,修改关闭)了解一下?
这个时候我们的代理模式闪亮登场,总共有三种:静态代理,jdk动态代理,cglib动态代理
2.静态代理
//统一接口 public interface Famer{ void watchPSite(); } //目标类 public class CodeFamer implement Famer{ @Override public void watchPSite(){ System.out.println("code famer is watching PSite"); } } //代理类 public class CodeFamerProxy implement Famer{ private Famer codeFamer; public CodeFamerProxy(Famer codeFamer){ this.codeFamer = codeFamer; } @Override public void watchPSite(){ System.out.println("prepare toilet paper"); codeFamer.watchPSite(); System.out.println("handwashing"); } } //使用 public class Test{ public static void main(String[] args){ Famer codeFamer = new CodeFamer(); Famer proxy = new CodeFamerProxy(codeFamer); poxy.watchPSite(); } }
如代码所示:创建一个代理类来做扩展,不修改源码,完全符合开闭原则。
也就是说给码农看片提供一个服务员,在他看片前给他递纸巾,在他看片完帮他洗手,毕竟看片才是码农的主要事项。
静态代理虽好,但也有缺点:如果接口层有变动呢?譬如多了一个方法:void code(); 那么代理类也得跟着改动,这又增加了维护的难度。
也就是说给码农提供了一个看片的服务员,但是码农不但看片,还要写代码的呀。写代码的时候这个服务员也得跟着码农,众所周知,码农写代码时是不能让别人看到的,不然复制粘贴大法岂不是不好意思用?
3.jdk动态代理
//接口 public interface Famer{ void watchPSite(); } //目标类 public class CodeFamer implement Famer{ @Override public void watchPSite(){ System.out.println("code famer is watching PSite"); } } //jdk动态代理类 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class Dynamicproxy implements InvocationHandler { private final Object target; public Dynamicproxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("prepare toilet paper"); method.invoke(target, args); System.out.println("handwashing"); return null; } public static Object getDynamicproxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标类的类加载 target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个 new Dynamicproxy(target) // 代理对象对应的自定义 InvocationHandler ); } } //使用 public class Test{ public static void main(String[] args){ Famer proxy = (Famer)Dynamicproxy.getDynamicproxy(new CodeFamer()); proxy.watchPSite(); } }
以上代码虽然相对复杂,但是主要解决了静态代理的问题,无需为每个目标类创建一个代理类,接口有什么变动,也不会影响代理实现。只有在使用时通过反射获取目标类的方法动态做代理。
也就是说,码农不用预先请一个服务员,而是只有在看片的时候再请服务员过来做事,做完了服务员也就走了。做其他事譬如写代码的时候不用服务员那不叫服务员过来就行了。
动态代理虽好,可缺点也不少,首先是代码复杂度大大提高,其次和静态代理一样都必须要通过接口的方式实现。
4.cglib动态代理
//目标类 public class CodeFamer{ public void watchPSite(){ System.out.println("code famer is watching PSite"); } } //cglib动态代理类 import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private Object target; public CglibProxy(Object target) { this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("prepare toilet paper"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("handwashing"); return result; } public static <T> T createProxy(T target) { CglibProxy cglibProxy = new CglibProxy(target); Enhancer enhancer = new Enhancer(); enhancer.setCallback(cglibProxy); enhancer.setSuperclass(target.getClass()); return (T) enhancer.create(); } } //使用 public class Test { public static void main(String[] args) { CodeFamer proxy = CglibProxy.createProxy(new CodeFamer()); proxy.watchPSite(); } }
cglib代理和jdk代理代码复杂度差不多,都需要按照规则实现功能。
直观上的不同就是jdk动态代理需要目标类实现接口,而cglib代理无需目标类实现接口。
5.jdk代理和cglib代理的区别
JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;
CGLIB代理使用字节码处理框架ASM,对代理对象类的class文件加载进来,通过修改字节码生成子类。
DK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。
JDK创建代理对象效率较高,执行效率较低;
CGLIB创建代理对象效率较低,执行效率高。
JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
1.实现InvocationHandler接口,重写invoke()
2.使用Proxy.newProxyInstance()产生代理对象
3.被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
1.实现MethodInterceptor接口,重写intercept()
2.使用Enhancer对象.create()产生代理对象
6.SpringAOP
源码如下:
@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理
2、如果目标对象实现了接口,也可以强制使用CGLIB
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换