• java框架基础(3)


    代理

    静态代理:

    接口a定义了方法aa,实现了接口a的实现类b和实现类c;如何用c来代理b;

    其实就是把b类实例化后作为参数通过c类的构造函数注入到c类中,这样就可以在c类里调用b类里自己重写的aa方法了;

    JDK动态代理:

    这里涉及两个类:Proxy类和InvocationHandler类;

    Proxy类用得最多的就是newProxyInstance 这个方法:

     这个方法的作用就是得到一个动态的代理对象,其接收三个参数

    loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

    interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

    h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

    package com.lzj;
    
    /**
     * @Description: TODO
     * @author: lzj
     * @date: 2021年04月27日 16:53
     */
    
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理举例
     *
     *
     * **/
    
    interface Human{
        void getBelief();
        void eat(String food);
    }
    //被代理类
    class SuperMan implements Human{
    
        @Override
        public void getBelief() {
            System.out.println("I believe I can fly!");
        }
    
        @Override
        public void eat(String food) {
            System.out.println("我喜欢吃"+food);
        }
    }
    
    
    class ProxyFactory{
        //调用此方法,返回一个代理类的对象,解决了问题1
        public static Object getProxychInstance(Object obj){//obj:被代理类的对象
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
        }
    }
    class MyInvocationHandler implements InvocationHandler {
        private Object obj;
        public void bind (Object obj){
            this.obj = obj;
        }
    
        //当我们通过代理类的对象,调用方法a,就会自动调用如下方法:invoke()
        //将被代理类要执行的方法a的功能就声明在invoke()中
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method:就是代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
            if(method.getName().equals("eat")){
            System.out.println("before rent house");
    
            System.out.println("Method:" + method);
    
            // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
            method.invoke(obj, args);
    
            //   在代理真实对象后我们也可以添加一些自己的操作
            System.out.println("after rent house");}
            return null;
    
        }
    
    }
    public class proxy {
        public static void main(String[] args) {
            SuperMan superMan = new SuperMan();
            Human proxyInstance = (Human) ProxyFactory.getProxychInstance(superMan);
            proxyInstance.getBelief();
            proxyInstance.eat("麻辣烫");
    
        }
    }

    运行结果:

    before rent house
    Method:public abstract void com.lzj.Human.eat(java.lang.String)
    我喜欢吃麻辣烫
    after rent house

    定义好InvocationHandler,这里可以做各种自定义业务;之后生成代理类后便可调用所传入参数的接口interfaces里所有的实现方法了;

    之所以需要传入类加载器,是因为在生成代理类的时候,其实内部的原理是通过反射,把我们传入接口里的所有方法都遍历出来,再根据我们自己写好的业务再次编译一个class文件,再通过类加载器加载生成代理类到内存里,这里就很好地反映动态这个词了

    CGLIB动态代理:

     CGLIB动态代理其实和jdk动态代理很是相似,依旧是在程序运行时生成新的字节码文件,生成被代理类的子类作为代理类,在调用被代理类方法时进行拦截,从而执行我们拦截器里自定义的方法;

    同样涉及两个主要类:MethodInterceptor(拦截器接口)、Enhancer(字节码加强器)

    package com.lzj;
    
    import org.springframework.cglib.proxy.*;
    
    import java.lang.reflect.Method;
    import java.util.Calendar;
    
    /**
     * @Description: TODO
     * @author: lzj
     * @date: 2021年04月28日 9:32
     */
    //目标类
    class CglibDao {
    
        public CglibDao() {
            select();
        }
    
        public void update() {
            System.out.println("CglibDao.update()");
        }
    
        public void select() {
            System.out.println("CglibDao.select()");
        }
    
    }
    //拦截器1
    class CglibDaoProxy implements MethodInterceptor {
    
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("before method invoke");
            methodProxy.invokeSuper(object, args);
            System.out.println("after method invoke");
            return object;
        }
    
    }
    //拦截器2
    
    class CglibDaoTwoProxy implements MethodInterceptor {
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("startTime : " + Calendar.getInstance().getTimeInMillis());
            methodProxy.invokeSuper(object, args);
            System.out.println("endTime : " + Calendar.getInstance().getTimeInMillis());
            return object;
        }
    }
    //回调器
    
    class CglibFilter implements CallbackFilter {
        public int accept(Method method) {
            if ("select".equals(method.getName())) {
                return 1;
            }
            return 0;
        }
    
    }
    
    public class cglib {
        public static void main(String[] args) {
            cglibTest2();
        }
    
    
        public static void cglibTest2() {
            CglibDaoTwoProxy twoProxy = new CglibDaoTwoProxy();
            CglibDaoProxy proxy = new CglibDaoProxy();
    
            CglibFilter filter = new CglibFilter();
            Enhancer enhancer = new Enhancer();
            //设置父类
            enhancer.setSuperclass(CglibDao.class);
            //设置拦截器替代调用方法
            enhancer.setCallbacks(new Callback[]{proxy, twoProxy});
            //配置回调器,在配置多拦截器时才会用到,根据返回的int作为拦截器数组的下标,采用不同的拦截器
            enhancer.setCallbackFilter(filter);
            //生成代理类
            CglibDao dao = (CglibDao) enhancer.create();
            dao.update();
            dao.select();
        }
    
    }

    执行之后返回的结果:

    startTime : 1619592608771
    CglibDao.select()
    endTime : 1619592608800
    before method invoke
    CglibDao.update()
    after method invoke
    startTime : 1619592608800
    CglibDao.select()
    endTime : 1619592608800

    可以看到delect方法对应的是拦截器2;而update方法对应的是拦截器1;

    JDK动态代理和cglib代理的区别:

    • cglib无需实现接口,达到代理类无侵入,但是被代理类不能是final,因为所创建的代理类其实是被代理类的子类;
    • 使用动态代理必须实现接口
  • 相关阅读:
    学习笔记10.28
    学习目标
    ajax传值修改数据
    php 4种传值方式
    01-17权限管理
    01-16作业:文件管理
    01-15文件操作
    01-12文件上传
    1-6 号 详情
    ajax登陆提示
  • 原文地址:https://www.cnblogs.com/lzj-learn/p/14679774.html
Copyright © 2020-2023  润新知