Java中的代理

代理模式

  1. 给某个对象提供一个代理对象,并有代理对象提供原对象的访问,可以理解成原有对象是代理对象的一个属性,由代理对象控制原有对象的访问,可以多加一些功能

  2. 按照代理对象的创建分期可以分为静态代理和动态代理

  3. 静态代理是在编译的时候就已经实现,编译完成后代理类是一个class字节码文件

  4. 动态代理是在运行的时候生成的,编译完成后没有实际的class字节码文件,是在运行的时候动态生成字节码文件,并加载jvm中

静态代理

  1. 比如一个人要卖车子,首先定义一个卖车子接口,由卖车子张三的实现这个接口,再用二手车代理类也实现这个接口,张三向代理类注册,然后通过代理类卖车子卖出更好的价钱

  2. package com.example.demo.proxy;
    
    /**
     * @Description TODO
     * @Author coderOx
     * @Date 2023/1/7 13:57
     */
    public class Proxy {
        public static void main(String\\\[\\\] args) {
            //新建张三的车
            Zhangsan zhangsanCar = new Zhangsan();
            //新建代理类你,并把张三的车交给代理
            SaleCarProxy proxy = new SaleCarProxy(zhangsanCar);
            //经过代理之后,代理卖车
            proxy.saleCar();
        }
    }
    
    interface SaleCar{
        void saleCar();
    }
    class Zhangsan implements SaleCar{
    
        @Override
        public void saleCar() {
            System.out.println("张三卖车要求最低价100w卖出");
        }
    }
    class SaleCarProxy implements SaleCar{
    
        private Zhangsan owner;
    
        //张三把自己的车交给代理类
        public SaleCarProxy(Zhangsan zhangsanCar) {
            this.owner = zhangsanCar;
        }
    
        @Override
        public void saleCar() {
            //代理类处理过后出售张三的车
            System.out.println("代理1000w卖出");
            owner.saleCar();
        }
    }
    
  3. 虽然张三实现了卖车的接口,但是张三需要自己卖,通过代理,可以实现更多操作,卖出更好的价钱

  4. 现在张三不但卖车现在还需要买房子,那么他可以找代理买房子的人买房子

  5. package com.example.demo.proxy;
    
    /**
     * @Description TODO
     * @Author coderOx
     * @Date 2023/1/7 13:57
     */
    public class Proxy {
        public static void main(String\\\[\\\] args) {
            //新建张三的车
            Zhangsan zhangsan = new Zhangsan();
            //新建代理类你,并把张三的车交给代理
            SaleCarProxy saleCarProxy = new SaleCarProxy(zhangsan);
            //经过卖车代理之后,代理卖车
            saleCarProxy.saleCar();
    
            BuyHouseProxy buyHouseProxy = new BuyHouseProxy(zhangsan);
            //经过买房代理之后,代理买房子给张三
            buyHouseProxy.buyHouse();
        }
    }
    
    interface SaleCar{
        void saleCar();
    }
    interface BuyHouse{
        void buyHouse();
    }
    class Zhangsan implements SaleCar,BuyHouse{
    
        @Override
        public void saleCar() {
            System.out.println("张三卖车要求最低价100w卖出车子");
        }
    
        @Override
        public void buyHouse() {
            System.out.println("张三要求最多200w买房子");
        }
    }
    //代理不光卖车还买房子
    class SaleCarProxy implements SaleCar{
    
        private Zhangsan owner;
    
        //张三把自己的车交给代理类
        public SaleCarProxy(Zhangsan zhangsan) {
            this.owner = zhangsan;
        }
    
        @Override
        public void saleCar() {
            //代理类处理过后出售张三的车
            System.out.println("代理1000w卖出车子");
            owner.saleCar();
        } 
    }
    class BuyHouseProxy implements BuyHouse{ 
        private Zhangsan zhangsan;
    
        public BuyHouseProxy(Zhangsan zhangsan) {
            this.zhangsan = zhangsan;
        } 
        @Override
        public void buyHouse() {
            //代理帮张三买房子
            System.out.println("代理200w买价值100w的房子");
            zhangsan.buyHouse();
        }
    }
    
  6. 这样就可以在不修改张三最低卖车钱和最高买房钱的前提下更改交易的金额

  7. 但是这样会产生很多的代理类,而且不容易维护如果接口增加目标对象和代理对象都要修改

  8. 当张三这样的人越来越多,代理的工作又差不多,就会多出很多代理类,最好能有个代理能代理一类像张三一样的人,那么久出现了动态代理

动态代理

  1. 常见的有JDK的动态代理和CGLIB的动态代理

  2. JDK动态代理:利用拦截器(必须实现InvocationHandler接口)加上反射机制生成代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

  3. Cglib动态代理:通过ASM框架,对代理对象生成的class加载进来,修改字节码文件生成子类来进行代理

  4. 要实现JDK动态代理代理类必须要实现接口

  5. 要实现Cglib动态代理,代理类不能使用final修饰类和方法

  6. 动态代理的使用场景:AOP,RPC,Java注解对象获取,日志框架,全局异常等等

JDK动态代理

  1. 代理类必须要实现InvocationHandler接口,代理生成对象为接口类型

  2. package com.example.demo.proxy; 
    import org.aopalliance.intercept.Invocation; 
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy; 
    /**
     * @Description TODO
     * @Author coderOx
     * @Date 2023/1/7 14:45
     */
    public class JDKProxy {
        public static void main(String\\\[\\\] args) {
            //被代理对象
            UserServiceImpl userServiceImpl = new UserServiceImpl();
            //代理对象
            UserProxy userProxy = new UserProxy(userServiceImpl);
            /**
             * 代理对象实现了InvocationHandler接口,只需要吧被代理对象传入,动态生成代理
             * 这样就实现了传入 不同的被代理对象和 代理对象 实现一个代理对象代理多个不同的被代理对象
             * Proxy.newProxyInstance方法得到的也是 UserService 的实现类对象,那么其实这是一种基于接口的动态代理
             * 返回的必须是接口对象,代理实现类,返回接口类
             */
    
            UserService userService = (UserService) Proxy.newProxyInstance(userServiceImpl.getClass().getClassLoader(),
                    userServiceImpl.getClass().getInterfaces(),userProxy);
    
            userService.addUser();
    
            userService.updateUser("aaaa");
    
        }
    }
    
    interface UserService {
        void addUser();
        void updateUser(String str);
    }
    class UserServiceImpl implements UserService{
    
        @Override
        public void addUser() {
            System.out.println("添加用户");
        }
    
        @Override
        public void updateUser(String str) {
            System.out.println("更新用户信息" + str);
        }
    }
    
    //代理实现InvocationHandler
    class UserProxy implements InvocationHandler{
        private Object target;
    
        public UserProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object\\\[\\\] args) throws Throwable {
            System.out.println("执行"+method+"前");
            Object o = method.invoke(target,args);
            System.out.println("执行"+method+"后");
    
            return o;
        }
    }
    

Cglib动态代理

  1. CGLIB 动态代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ,也就是说 CGLIB 动态代理采用类继承 -> 方法重写的方式进行的

  2. 需要CGLIB需要导入jar包。spring-core中也包含

  3.  cglib    cglib    3.3.0

  4. 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

  5. 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.Method; 
    /**
     * @Description TODO
     * @Author coderOx
     * @Date 2023/1/7 16:11
     */
    public class CglibProxy {
        public static void main(String\\\[\\\] args) {
            UserServiceImpl userService = new UserServiceImpl();         UserServiceCGlib cGlib = new UserServiceCGlib(userService);         UserServiceImpl proxyInstance = (UserServiceImpl) cGlib.getProxyInstance(); 
            proxyInstance.addUser();
            proxyInstance.updateUser("aaa");
        }
    }
    class UserServiceImpl { 
        public void addUser() {
            System.out.println("添加用户");
        } 
        public void updateUser(String str) {
            System.out.println("更新用户信息" + str);
        }
    }
    class UserServiceCGlib implements MethodInterceptor { 
        private Object target; 
        public UserServiceCGlib() {
        } 
        public UserServiceCGlib(Object target) {
            this.target = target;
        } 
        //返回一个代理对象:    是 target对象的代理对象
        public Object getProxyInstance() {
            //1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2. 设置父类
            enhancer.setSuperclass(target.getClass());
            //3. 设置回调函数
            enhancer.setCallback(this);
            //4. 创建子类对象,即代理对象
            return enhancer.create();
        } 
        @Override
        public Object intercept(Object o, Method method, Object\\\[\\\] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("增强开始~~~");
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("增强结束~~~");
            return result;
        }
    }
    
  6. 如果目标对象有接口可以用jdk代理

  7. 如果目标对象没有实现接口,可以送cglib代理

  8. 如果实现了接口,可以强制使用cglib代理