spring相关之代理模式(proxy)

it2025-03-30  13

一.什么是代理模式?

    客户端不直接和目标对象(实际对象)发生交互,或不直接调用目标对象,而是通过调用代理对象来间接的调用目标对象的设计模式。

二.作用(为什么要使用代理模式)

当客户端不想直接访问目标对象或访问目标对象有困难时,需要通过代理对象进行间接访问在不改变目标对象源代码的情况下,通过代理对目标对象的功能进行增强。如添加日志、事务控制,权限控制,鉴权登录等

三.代理模式分类

1.静态代理(了解)

静态代理,代理类和i能代理某一特定的目标对象,并且对目标对象进行增强。

public interface Ticket { void buyTicket(); } /** * 12306 直接购买 */ public class By12306TicketImpl implements Ticket{ @Override public void buyTicket() { System.out.println("12306 买票"); } } /** * 通过黄牛购买 */ public class HuangNiuTicketImpl implements Ticket { private Ticket ticket; public void setTicket(Ticket ticket) { this.ticket = ticket; } @Override public void buyTicket() { System.out.println("黄牛开始买票"); if (ticket!=null){ ticket.buyTicket(); } System.out.println("黄牛买票成功"); } }

测试类

public class Test { public static void main(String[] args) { // 自己买票 Ticket ticket = new By12306TicketImpl(); ticket.buyTicket(); // 通过黄牛买票,最终票的来源还是12306 HuangNiuTicketImpl huangNiu = new HuangNiuTicketImpl(); huangNiu.setTicket(ticket); huangNiu.buyTicket(); } }

2.动态代理(重要)

动态代理,代理对象可以代理很多类型的目标对象,并且对目标对象进行增强

java中动态代理有JDK和CGLIB两种方式

四、基于JDK接口的动态代理(面试必会)

1.JDK动态代理步骤:

实现InvocationHandler接口创造代理对象通过反射执行目标对象方法

2.注意点

应用场景:基于java jdk实现只能代理实现接口的目标对象,如果没有接口,则报错

搞清楚Proxy.newProxyInstance三个参数的意思返回的代理对象是接口,目标对象实现的那个接口当代理对象调用对应的接口方法时 会回调传递第三个参数 InvocationHandler 对象内的 invoke 方法,通过反射执行 public class JdkProxyTest1 { public static void main(String[] args) { //12306 目标对象 因为作为了匿名内部类参数的缘故,所以用final修饰 final TicketBy12306Impl t12306 = new TicketBy12306Impl(); //创建一个代理对象 //ClassLoader loader, 类加载器 //Class<?>[] interfaces, 目标对象实现的接口,代理目标对象必须实现接口 // t12306.getClass().getInterfaces() 获取目标对象实现的接口 // InvocationHandler 当代理对象执行接口的方法时 会回调InvocationHandler的invoke Ticket proxyTicket = (Ticket) Proxy.newProxyInstance(t12306.getClass().getClassLoader(), t12306.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理对象开始调用 目标对象"); //让目标对象执行方法 并且获取返回结果 Object result = method.invoke(t12306, args); System.out.println("代理对象结束调用 目标对象"); return result; } }); String str = proxyTicket.bubTicket(); System.out.println("str:" + str); } }

 3.封装工具类

/** * 专门用于产生JDK代理对象 */ public class JdkProxy implements InvocationHandler { //目标对象 private Object target; public Object createProxy(final Object target){ this.target = target; //ClassLoader loader,指定当前目标对象使用类加载器,我们知道获取加载器的写法是固定的 //Class<?>[ ] interfaces,目标对象实现的接口的类型,使用泛型方式确认类型,我们知道要想知道一个类是否实现某个接口,可以使用    Object.getClass().getInterfaces() ,这个方法是获取类是否实现接口,如果此对象表示一个类,则返回值是一个数组,它包含了表示该类所实现的所有接口的对象。 //InvocationHandler h,事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理对象开始调用 目标对象"); // 让目标对象执行方法 并且获取返回结果 Object result = method.invoke(target,args); System.out.println("代理对象结束调用 目标对象"); return result; } }

五、cglib基于类的动态代理

cglib基于asm字节码生成框架,用于动态生成代理类。

重点:

应用场景:cglib可以代理类和接口,返回的代理对象是其子类cglib 直接调用生成的类的方法,而jdkproxy是通过反射,返回效率差 

1.cglib与jdk的区别(重点)?

         1. jdk实现代理,目标对象必须实现接口

             cglib是根据目标对象/目标类生成代理类的字节码文件,不要求实现接口

         2.在生产代理类的效率上,cglib效率差 因为要生成字节码文件   jdk效率高

         3.在代理方法执行效率上来说。 cglib直接执行class文件的方法,效率高。jdk通过反射实现,效率低

2.实现

public class CglibProxyTest1 { public static void main(String[] args) { //创建cglib代理对象需要工具类 //1.引入jar包 Enhancer enhancer = new Enhancer(); //传递目标类 cglib的原理:生成的代理类是目标类的子类 enhancer.setSuperclass(TicketBy12306Impl.class); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib 代理开始调用 目标对象"); //让目标对象执行 //o 目标对象 //object 目标对象执行方法需要的参数 Object result = methodProxy.invokeSuper(o, objects); System.out.println("cglib-result:"+result); System.out.println("cglib 代理结束调用 目标对象"); return null; } }); //创建代理对象 目标对象可以不创建 cglib会自己创建目标 TicketBy12306Impl ticket = (TicketBy12306Impl) enhancer.create(); ticket.bubTicket(); } }

3.封装工具类

/** * cglib 创建代理对象的工具类 */ public class CglibProxy implements MethodInterceptor { public Object createProxy(Class superClass){ Enhancer enhancer = new Enhancer(); // 传递目标类 cglib 的原理是生成的代理类是 目标类的子类 enhancer.setSuperclass(superClass); enhancer.setCallback(this); //创建代理对象 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib 代理开始调用 目标对象"); // 让目标 对象执行 // o 目标对象 // objects 目标对象执行方法需要的惨呼 Object result = methodProxy.invokeSuper(o,objects); System.out.println("cglib-result:"+result); System.out.println("cglib 代理结束调用 目标对象"); return result; } } public class CglibProxyTest2 { public static void main(String[] args) { //生成动态代理的工具类 CglibProxy cglibProxy = new CglibProxy(); //12306代理对象 TicketBy12306Impl ticketBy12306Proxy = (TicketBy12306Impl) cglibProxy.createProxy(TicketBy12306Impl.class); ticketBy12306Proxy.bubTicket(); System.out.println("*********************"); //第二个代理对象 RealSubject realSubjectProxy = (RealSubject) cglibProxy.createProxy(RealSubject.class); realSubjectProxy.action(); } }

 

最新回复(0)