设计模式 之 代理模式- 2020-10-26 12:16- 设计模式: java,编程,设计模式,代理模式,CGLB,动态代理
代理模式分为两种 静态代理,动态代理。
代理中的角色: 代理类,目标方法,目标类
静态代理
原理
静态代理是由我们自己编写的代理类,代理类中指定引用的真实对象,使用真实对象去执行接口,在编译时期就确定了。
优点和缺点
优点:
1.因为是我们手动编写的代理类,可控性好,可针对指定接口做扩展,例如增加日志或是数据统计等。
2.避免了生成大对象,造成资源浪费
缺点:
1.手动编写 维护等成本增加,如果有多个类需要代理,则维护成本更增加。
2.代码耦合度高,不利于通用性。
3.违反 开闭原则 (开闭原则:增加扩展,避免更新)
因为代理类中耦合了被代理类,如果被代理的方法更新或者删除了,那么代理类中相应的也需要更新或删除,从而违反了开闭原则。
dome
/*** 接口类** @Author Bai* @Date 2020-10-26 14:25*/public interface HelloService {void say(String msg);}
/*** 真实对象:被代理对象** @Author Bai* @Date 2020-10-26 14:25*/public class HelloServiceImpl implements HelloService {@Overridepublic void say(String msg) {System.out.println("向大家say" + msg);}}
/*** 代理类** @Author Bai* @Date 2020-10-26 14:26*/public class HelloServiceProxy {/*** 真实引用对象*/private HelloService target;public HelloServiceProxy(HelloService target) {this.target = target;}@Overridepublic void say(String msg) {System.out.println("say proxy 开始");target.say(msg);System.out.println("say proxy 结束");}}
@Testpublic void test1() {HelloService helloService = new HelloServiceImpl();HelloServiceProxy proxy = new HelloServiceProxy(helloService);proxy.say("hello world");}
动态代理
动态代理有种实现方式,JDK动态代理和CGLB。JDK动态代理 主要是java.lang.reflect包下的类,实现jdk动态代理需要实现InvocationHandler。CGLB是第三方字节码处理库,它的底层是基于ASM框架来处理字节码实现代理的。
JDK动态代理
原理
主要基于java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler来实现动态代理,所有生成的代理类都继承于Proxy,InvocationHandler是一个接口,类似于调用处理器,我们自己在实现动态代理时都必须要实现java.lang.reflect.InvocationHandler。jdk底层其实也是通过asm框架来实现对字节码的操作,读字节码并生生成对应的class文件。
1.获取被代理类的引用,并获取引用类的所有接口,通过反射获取。2.Proxy基于被代理类的接口生成新的类,拥有被代理类的所有接口。3.动态生成java代码,把增强逻辑加入到新代码中。4.重新编译代理类生成新的class文件5.加载代理类的class文件并重新运
优点和缺点
优点:
1.不需要手动编写代理类,运行时会自动生成代理类
缺点:
1.被代理的类 必须要实现接口,没有实现接口或是抽象类的 类 无法被代理
dome
1.实现InvocationHandler
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/*** JDK动态代理handler** @Author Bai* @Date 2020-10-26 14:52*/public class HelloInvocationHandler implements InvocationHandler {/*** 真实对象:被代理的对象引用*/private Object target;public HelloInvocationHandler(Object target) {super();this.target = target;}/*** @param proxy 代理类* @param method 代理类的方法* @param args 入参* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("HelloInvocationHandler#invoke start");//两者执行有何区别?target or proxy//proxy 会造成死循环,猜测可能是因为invoke内部实现 需要生成proxy的实例,去调用proxy实例的invoke,就会造成循环调用// Object invoke = method.invoke(proxy, args);//这里要传入目标类Object invoke = method.invoke(target, args);System.out.println("HelloInvocationHandler#invoke end");return invoke;}/*** 获取代理对象** @return*/public Object getProxy() {//第一个入参 使用哪个loader将proxy对象加载到内存中,一般是跟被代理对象同个loader就可以//第二个入参 proxy要代理的是哪些接口Class<?> aClass = target.getClass();saveProxyClass0();return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);}private void saveProxyClass0() {//通过反编译工具可以查看源代码byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{HelloService.class});try {FileOutputStream os = new FileOutputStream("E://$Proxy0.class");os.write(bytes);os.close();} catch (IOException e) {e.printStackTrace();}}}
2.测试
/*** jdk动态代理*/@Testpublic void jdkProxyTest() throws Throwable {//真实对象HelloService helloService = new HelloServiceImpl();//代理handlerHelloInvocationHandler handler = new HelloInvocationHandler(helloService);// 代理对象HelloService helloProxyService = (HelloService) handler.getProxy();//使用代理对象执行System.out.println("--- helloProxyService.result ---");helloProxyService.say("helloProxyService.jdk动态代理");//使用handler执行String[] args = new String[]{"helloProxyService.invoke"};//对应 Object invoke = method.invoke(proxy, args)// Object say = handler.invoke(helloProxyService, helloProxyService.getClass().getMethod("say", String.class), args);//对应 Object invoke = method.invoke(target, args)//入参: 代理对象,被调用的目标方法,入参Object say = handler.invoke(helloProxyService, helloService.getClass().getMethod("say", String.class), args);System.out.println("--- HelloProxyService.result ---" + say);}
3.JDK动态生成的代理类源码
import com.xy.blog.test.poxy.HelloService;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements HelloService {private static Method m1;private static Method m2;private static Method m3;private static Method m4;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}public final boolean equals(Object var1) throws {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void say(String var1) throws {try {super.h.invoke(this, m3, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void eat(String var1) throws {try {super.h.invoke(this, m4, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final int hashCode() throws {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.xy.blog.test.poxy.HelloService").getMethod("say", Class.forName("java.lang.String"));m4 = Class.forName("com.xy.blog.test.poxy.HelloService").getMethod("eat", Class.forName("java.lang.String"));m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}}
CGLB代理
原理 todo
第三方字节码处理库,底层是asm框架,如果要了解原理就需要了解asm框架,以及它的一些设计模式
cglb是通过生成被代理类的子类来实现代理。
优点和缺点todo
优点:
- 被代理对象无须实现接口,对类的入侵没有那么深
dome
import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** cglb动态代理实现** @Author Bai* @Date 2020-10-26 16:00*/public class CglibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();/*** 获取代理类** @param clazz 真实类* @return*/public Object getProxy(Class clazz) {//设置需要创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);//通过字节码技术动态创建子类实例return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("前置代理");//通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.println("后置代理");return result;}}
/*** jdk动态代理*/@Testpublic void cglbProxyTest() throws Throwable {//真实对象CglibProxy cglibProxy = new CglibProxy();HelloService helloService = (HelloService) cglibProxy.getProxy(HelloServiceImpl.class);helloService.say("cglb动态代理");}
执行结果:前置代理向大家saycglb动态代理后置代理
两者区别:JDK动态代理/CGLB动态代理
应用
aop
aop由JDK动态代理和CGLB动态代理实现,当需要代理的类实现了接口,既使用jdk动态代理,如果需要代理的类没有实现接口则自动切换为CGLB代理。<br /> jdk动态代理是基于反射来获取被代理的类,要去被代理的类必须要实现接口。<br /> cjlb是代码生成的类库,它是在运行过程中动态的生成类的子类来实现代理的,cglb是基于继承来实现的,所以如果一个类被final修饰,那么这个类将不能被cglb代理。
源码分析todo
问题整理
1.invoke接口的第一个入参proxy不是很经常用,为什么还要传过来呢?
2.静态代理为啥要构造参数里给出了来
3.动态代理为什么要实现接口?
参考资料:
http://hollischuang.gitee.io/tobetopjavaer/#/basics/java-basic/static-proxy
**
