大纲
代理
proxy
cglib
小结
一, 代理
为什么要用代理? 其实就是希望不修改对象的情况下, 增强对象.
静态代理:
静态代理模式, 需要代理类和目标类实现同一接口, 代理类的方法调用目标类的方法, 调用方法的前后实现需要增强的逻辑.
静态代理有一个问题就是, 每个代理类和目标类一一对应, 需要代理的类多的情况, 需要大量的代理类, 难以维护.
动态代理:
动态代理就是运行时动态生成的类, 并不是在编译时期.
动态代理有两种不同的方式, 一种是 jdk 反射包下的的 Prxoy, 一种是 cglib.
二, Proxy
Proxy 生成代理对象需要目标对象实现一至少一个接口.
Proxy 通过反射实现动态代理.
生成代理对象需要调用 Proxy 中 newProxyInstance 方法.
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
loader - 目标对象的 classloader
interfaces - 目标对象实现的接口
InvocationHandler - 处理器, 当目标对象接口中的方法被调用时处理器中 invoke 方法会被调用从而实现动态代理
在看下 InvocationHandler
- public interface InvocationHandler {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- }
proxy - 代理对象
method - 被调用的方法
args - 调用方法时传入的参数
invoke 返回值为代理方法的返回值
测试:
接口:
- public interface Speak {
- String say(String content);
- }
目标类:
- import lombok.Data;
- @Data
- public class Person implements Speak{
- private String name;
- private int age;
- public String say(String content){
- System.out.println("hi"+name+age+"content:"+content);
- return "say return";
- }
- }
代理工厂:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class ProxyFactory{
- // 维护一个目标对象
- private Object target;
- public ProxyFactory(Object target) {
- this.target = target;
- }
- // 生成代理对象
- public Object getProxyInstance() {
- return Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("before method:"+method.getName());
- // 执行目标对象方法, 返回 return 值.
- Object returnValue = method.invoke(target, args);
- System.out.println("end method:"+method.getName());
- return returnValue;
- }
- });
- }
- }
代理对象在代理方法调用前后打了一行字.
- public static void main(String[] args) {
- Person p = new Person();
- p.setAge(11);
- p.setName("xx");
- ProxyFactory factory = new ProxyFactory(p);
- Object proxyInstance = factory.getProxyInstance();
- Speak speak = (Speak) proxyInstance;
- String returnValue = speak.say("haha");
- System.out.println("returnValue:"+returnValue);
- }
三, cglib
静态代理和都必须实现接口, 而 cglib 没有这个限制, cglib 通过字节码操作动态生成子类, 因此目标类不能被 final 修饰.
与 proxy 类似的我们也需要复写一个处理器
- public interface MethodInterceptor extends Callback {
- Object (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
- }
proxy - 代理对象
method - 被调用的方法
methodProxy - 代理方法 (具体本人不是特别清楚)
intercept 返回值为代理方法的返回值
测试:
重写 Person 不需要实现接口
- import lombok.Data;
- @Data
- public class Person{
- private String name;
- private int age;
- public String say(String content){
- System.out.println("hi"+name+age+"content:"+content);
- return "say return";
- }
- }
代理工厂:
- import org.assertj.core.internal.cglib.proxy.Enhancer;
- import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
- import org.assertj.core.internal.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- public class CgProxyFactory<T> {
- // 维护目标对象
- private T target;
- public CgProxyFactory(T target) {
- this.target = target;
- }
- // 获取代理
- public T getProxyInstance() {
- Enhancer en = new Enhancer();
- en.setSuperclass(this.target.getClass());
- // 设置拦截器
- en.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("before method:"+method.getName());
- Object returnValue = method.invoke(target, objects);
- System.out.println("end method:"+method.getName());
- return returnValue;
- }
- });
- return (T) en.create();
- }
- }
和之前的代理对象一样, 在代理方法调用前后打了一行字.
- public static void main(String[] args) {
- Person p = new Person();
- p.setAge(11);
- p.setName("xx");
- CgProxyFactory<Person> factory = new CgProxyFactory(p);
- Person proxyInstance = factory.getProxyInstance();
- String returnValue = proxyInstance.say("cg");
- System.out.println("returnValue:" + returnValue);
- }
四, 小结:
Proxy 需要代理类实现接口, 底层为反射.
Cglib 代理对象不能被 final 修饰, 底层是字节码操作.
spring 会根据目标类是否实现接口的情况, 切换动态代理的模式, 也可以通过配置强制使用 cglib.
来源: http://www.bubuko.com/infodetail-3119508.html