1、代理模式的理解:不修改A对象的代码的基础上,对A代码块进行拓展。通过创建ProxyA代理对象,拓展A对象并调用A对象的核心功能;
即:不修改对象的源码基础上,创建代理对象,进行功能的附加和增强;
2、代理的分类:1)静态代理;2)动态代理(jdk,cglib);
3、静态代理:在编译的过程中就已经将代理对象、被代理对象、接口确定下来了,class文件已经生成了;
实现步骤:
1、定义一个接口及其实现类;
2、创建一个代理类同样实现这个接口
3、将目标对象注注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。
3.1)定义一个接口及方法ParentPrint:
package com.example.demo.proxy; public interface ParentPrint { void print(String content); }
3.2)定义一个被代理的类,并实现接口ParentPrint,其中print方法就是我们需要代理的核心方法;
package com.example.demo.proxy; public class Printer implements ParentPrint { @Override public void print(String content) { System.out.println(content); } }
3.3)定义代理类,构造一个有参数的构造器,并重写print方法(其中通过传入的参数调用被代理对象的print方法)。
package com.example.demo.proxy; /* 1、首先需要代理类ProxyStaticPrinter和被代理类Printer同时实现一个共同的接口 ; 2、代理类ProxyStaticPrinter需要在构造方法中注入接口定义对象,并重写代理的方法print(前置后置处理),在其中加入注入对象的print方法; 3、实例化代理对象ProxyStaticPrinter,实例化被代理对象Printer; 4、将被代理对象通过参数注入到代理对象(ProxyStaticPrinter)有参构造器中; 5、代理对象调用处理过的方法; */ public class ProxyStaticPrinter implements ParentPrint { private ParentPrint parentPrint; public ProxyStaticPrinter(ParentPrint parentPrint) { this.parentPrint = parentPrint; } @Override public void print(String content) { System.out.println("前置操作"); parentPrint.print(content); System.out.println("后置操作"); } }
3.4)测试,定义一个代理对象,将代理对象通过参数传入被代理的对象中;
//测试 public static void main(String[] args) { ParentPrint parentPrint = new Printer(); ParentPrint ProxyParentPrint = new ProxyStaticPrinter(parentPrint); ProxyParentPrint.print("测试"); }
运行
3.5)总结:
将目标对象注注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问;
优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。
4、动态代理:动态代理包含了jdk动态代理和cglib动态代理;
4.1)jkd动态代理:
实现步骤:
1、定义一个接口及其实现类;
2、自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
3、通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;
4.1.1)定义一个接口
package com.example.demo.proxy; public interface ParentPrint { void print(String content); }
4.1.2)定义一个接口及方法ParentPrint:
package com.example.demo.proxy; public interface ParentPrint { void print(String content); }
4.1.3)定义一个被代理的类,并实现接口ParentPrint,其中print方法就是我们需要代理的核心方法;
package com.example.demo.proxy; public class Printer implements ParentPrint { @Override public void print(String content) { System.out.println(content); } }
4.1.4)定义一个代理类ProxyJdkPrinter并实现InvocationHandler,实现invoke方法,最终调用method.invoke(parentPrint,args);
注意该Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的。
Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型。
InvocationHandler:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
package com.example.demo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /*jdk动态代理:前提目标类必须有父接口(接口:ParentPrint,目标类:Printer) 1、创建ProxyJdkPrinter类,继承接口InvocationHandler创建代理类的调用处理程序; 2、同样引入代理类的参数对象private ParentPrint parentPrint; 3、实现invoke方法,最终调用method.invoke(parentPrint,args); 4、测试: 实例化代理类p1; 将p1放入代理类ProxyJdkPrinter; 通过Proxy类的WuDaInvocationHandler方法创建代理对象; 代理对象调用被代理的方法; * */ public class ProxyJdkPrinter implements InvocationHandler { private ParentPrint parentPrint; public ProxyJdkPrinter(ParentPrint parentPrint) { this.parentPrint = parentPrint; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if(name.equals("print")){ System.out.println("jdk增加操作"); } return method.invoke(parentPrint,args); } }
4.1.5)测试,invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。
public static void main(String[] args) { ParentPrint p1 = new Printer(); ProxyJdkPrinter proxyJdkPrinter = new ProxyJdkPrinter(p1); //newProxyInstance:创建代理实例对象 //三个参数: //1、 目标类的类加载器 2、目标类的父接口 3、handler ParentPrint proxyPrint = (ParentPrint)Proxy.newProxyInstance(p1.getClass().getClassLoader(), p1.getClass().getInterfaces(), proxyJdkPrinter); proxyPrint.print("测试"); }
运行
4.1.6)总结:
通过java提供的Proxy类帮我们创建代理对象,基于接口的动态代理需要利用JDK中的API,在JVM内存中动态的构建Proxy对象;
优点:可以生成所有实现接口的代理对象
缺点:JDK反射生成代理必须面向接口, 这是由Proxy的内部实现决定的。生成代理的方法中你必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。
4.2)cglib动态代理:当目标没有实现类的时候,可以使用;
实现步骤:
1、定义一个类;
2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
3、通过 Enhancer 类的 create()创建代理类;
4.2.1)定义一个被代理类Printer
package com.example.demo.proxy; public class Printer1 { public void print(String content) { System.out.println(content); } }
4.2.2)定义一个代理类ProxyCglibPrinter,并实现MethodInterceptor
package com.example.demo.proxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /*cglib动态代理:(目标对象不需要实现接口) 实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。 1.创建目标对象target; 2.创建interceptor对象 3.创建Enhancer对象,它以目标类和interceptor作为原料,生产出代理对象 4、enhancer设置参数setSuperclass,setCallback 5、创建代理类proxy,调用需要代理的方法; * */ public class ProxyCglibPrinter implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { String name = method.getName(); if(name.equals("print")){ System.out.println("cglib增加操作"); } methodProxy.invokeSuper(o,objects); return null; } }
4.2.3)测试
public static void main(String[] args) { Printer1 target = new Printer1(); ProxyCglibPrinter interceptor = new ProxyCglibPrinter(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(interceptor); Printer1 proxy = (Printer1)enhancer.create(); proxy.print("测试"); }
运行
4.2.4)总结:
无需代理类实现接口,使用Cblib中的Enhancer来生成代理对象子类,并实现MethodInterceptor中的intercept方法,在此方法中可以实现增强功能。
如果目标对象需要实现接口,则使用JDK代理。
如果目标对象不需要实现接口,则使用Cglib代理。
5、综上所述,再次总结:
静态代理:
jdk动态代理:
cglib动态代理: