• java动态代理--proxy&cglib


    大纲

    1. 代理
    2. proxy
    3. cglib
    4. 小结

    一、代理

    为什么要用代理?其实就是希望不修改对象的情况下,增强对象。

    静态代理:

    • 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法,调用方法的前后实现需要增强的逻辑。
    • 静态代理有一个问题就是,每个代理类和目标类一一对应,需要代理的类多的情况,需要大量的代理类,难以维护。

    动态代理:

    • 动态代理就是运行时动态生成的类,并不是在编译时期。
    • 动态代理有两种不同的方式,一种是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);
        }

    四、小结:

    1. Proxy需要代理类实现接口,底层为反射。
    2. Cglib代理对象不能被final修饰,底层是字节码操作。
    3. spring会根据目标类是否实现接口的情况,切换动态代理的模式,也可以通过配置强制使用cglib。
  • 相关阅读:
    MyBatis+MySQL 返回插入的主键ID
    微信被动回复用户消息-文本消息-springmvc环境下自动生成xml
    微信自动回复消息示例
    微信自定义菜单
    微信获取二维码
    微信被动回复用户消息-文本消息-填坑
    微信获得access-token
    设置ckeditor的高度
    Java三行代码搞定MD5加密
    Highchart
  • 原文地址:https://www.cnblogs.com/liuboyuan/p/11157378.html
Copyright © 2020-2023  润新知