• 详解 动态代理


    (请观看本人博文 —— 《详解 反射机制》
    (请观看本人博文 —— 《详解 代理模式》

    Youzg Logo


    动态代理

    概述

    • 代理:本来应该自己做的事情,却请了别人来做被请的人就是代理对象
      举例:春季回家买票让人代买
    • 动态代理:在程序运行过程中产生的这个对象
      而程序运行过程中产生对象,其实就是本人刚才反射讲解的内容,
      所以,动态代理其实就是通过反射来生成一个代理

    特点

    字节码 随用随创建随用随加载

    作用

    不修改源码的基础上对方法增强

    分类

    • 基于接口的动态代理 —— Proxy代理模式
    • 基于子类的动态代理 —— CGLib代理模式

    注意:JDK给我们提供的动态代理,只能对接口进行代理
    (即:Proxy代理模式)


    那么,首先,本人来讲解下由JDK提供Proxy代理模式

    Proxy代理模式:

    如何创建代理对象

    使用Proxy类中的newProxyInstance方法

    创建代理对象的要求

    被代理类最少实现一个接口,如果没有则不能使用

    其实,Proxy代理模式 的 动态代理,主要应用了两个方法:

    1. Proxy类创建动态代理类对象 的方法:
      public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

    参数

    • loader: 类加载器
    • interfaces: 接口对应的一个Class数组
    • InvocationHandler: 这个其实就是要代理对象所做的事情的一个类的封装

    1. 而这个方法,最终会调用InvocationHandler接口的方法:
      InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
      作用:执行被代理对象的任何接口方法都会经过该方法

    参数

    • proxy:代理对象的引用
    • method:当前执行的方法
    • args:当前执行方法所需的参数

    返回值:和被代理对象方法有相同的返回值

    那么,现在,本人就来展示下通过这些知识点,来编写一个动态代理小工具,并做下使用展示:

    首先是 动态代理小工具

    package edu.youzg.about_reflact.core;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyUtil {
    
        //获取代理对象的方法
        //参数:就是被代理对象(目标对象)
        //返回值:代理对象
        public static IUserDao getProxy(IUserDao userDao) {
            IUserDao obj = (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //执行目标对象的任何接口方法都会经过该方法
                   Object result = null;
                    if (method.getName().equals("delete")) {	//这里仅仅对该接口的delete()方法做了增加了几个步骤
                        System.out.println("权限的校验 "); //增强的代码
                        result = method.invoke(userDao);//让目标对象中方法执行
                        System.out.println("记录日志"); //增强的代码
                    } else {
                        result = method.invoke(userDao);//让目标对象中方法执行
                    }
                    return result;
                }
            });
    
            return obj; //返回代理对象
        }
    
    }
    

    现在,本人来给出一个测试接口:

    package edu.youzg.about_reflact.core;
    
    public interface IUserDao {
        void insert();
        void delete();
        void update();
        void query();
    }
    

    本人再来给出一个测试接口的实现类:

    package edu.youzg.about_reflact.core;
    
    public class UserDaoImpl implements IUserDao{
    
        @Override
        public void insert() {
    
            System.out.println("添加一个用户");
    
        }
    
        @Override
        public void delete() {
    
            System.out.println("删除用户");
    
        }
    
        @Override
        public void update() {
            System.out.println("修改用户");
    
        }
        
        @Override
        public void query() {
            System.out.println("查询用户");
        }
    
    }
    

    现在,本人来给出一个测试类,来展示下这个小工具的使用:

    package edu.youzg.about_reflact.core;
    
    import com.sun.deploy.net.proxy.ProxyUtils;
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
            IUserDao userDao = new UserDaoImpl();
            //我们采用动态代理的模式,在不修改原有代码的情况下,对类中的方法进行增强(增加一些额外功能)
            //通过我们写的工具类,来获取一个代理对象
            IUserDao proxy = ProxyUtil.getProxy(userDao);
            proxy.insert();
            System.out.println("----------------------");
            proxy.delete();
            System.out.println("----------------------");
            proxy.update();
            System.out.println("----------------------");
            proxy.query();
        }
    
    }
    

    那么,本人来展示下运行结果
    在这里插入图片描述
    可以看到,只有删除操作,多了几个步骤。


    现在,本人来讲解下 CGLib代理模式

    CGLib代理模式:

    CGLib代理模式不是由JDK所提供的,
    所以,我们若是想要运用这种代理模式
    就需要导cglib-nodep的Jar包

    如何创建代理对象

    1. 直接创建一个Enhancer类的对象
    2. 被代理类字节码对象(.class对象)通过参数
      第一步所创建的Enhancer类对象setSuperclass()方法设置进去

    创建代理对象的要求

    被代理的类,必须实现接口

    CGLib代理的原理

    创建一个被代理类的子类对象
    所以:

    1. 被代理类本身是final类,则,不能被代理
    2. 被代理类中的final方法不能被代理的

    CGLib代理模式 的 动态代理 的 主要步骤

    主要步骤

    1. 直接创建(new)一个Enhancer类的对象
    2. 被代理类字节码对象(.class对象)通过参数
      第一步所创建的Enhancer类对象setSuperclass()方法设置进去
    3. 直接创建(new)一个MethodInterceptor()抽象类的对象,并实现其抽象方法:
      public Object intercept(Object object, Method method, Object[] args,MethodProxy proxy) throws Throwable

    参数

    • object: 目标对象的对象
    • method: 目标对象方法
    • args: 目标对象方法的参数
    • proxy:代理后自动调用的方法

    那么,现在本人来展示下 CGLib代理模式 的小工具:

    package com.mec.proxy.test;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class CGLibProxyUtil {
    
        public CGLibProxyUtil() {
        }
    
        public static <T> T getProxy(T target) {
            Class<?> klass = target.getClass();
    
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(klass);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object object, Method method, Object[] args,
                                        MethodProxy proxy) throws Throwable {
                    Object result = null;
                    if (method.getName().equals("update")) {
                        System.out.println("拦截前");
                        result = method.invoke(target, args);
                        System.out.println("拦截后");
                    } else {
                        result = method.invoke(target, args);//让目标对象中方法执行
                    }
    
                    return result;
                }
            });
            return (T) enhancer.create();
        }
    }
    

    本人还是用上面给出的接口和实现类 为根据,来给出一个测试类:

    package com.mec.proxy.test;
    
    public class Test {
        public static void main(String[] args) {
            UserDaoImpl target = new UserDaoImpl();
    
            UserDaoImpl targetProxy = CGLibProxyUtil.getProxy(target);
            targetProxy.insert();
            System.out.println("----------------------");
            targetProxy.delete();
            System.out.println("----------------------");
            targetProxy.update();
            System.out.println("----------------------");
            targetProxy.query();
        }
    }
    

    那么,现在本人来展示下运行结果:
    CGLib代理模式 展示

    可以看到,只有修改操作,多了几个步骤。


    那么,现在本人来讲解下这两种动态代理的区别:

    Proxy代理模式 与 CGLib代理模式 的区别:

    区别

    • Proxy代理模式:
      产生的代理对象,其类型是目标接口派生类类型对象
      代理对象只能调用接口中的方法
      (可以理解为:被代理对象兄弟对象)
    • CGLib代理模式
      产生的代理对象,是 被代理类子类对象
      代理对象可以调用 被代理类除了final修饰 的其它 所有方法
      (可以理解为:被代理对象子对象)

    (本人 反射机制 总集篇博文链接:https:////www.cnblogs.com/codderYouzg/p/12419061.html
    (本人 代理模式 总集篇博文链接:https://www.cnblogs.com/codderYouzg/p/12801349.html

  • 相关阅读:
    Swift
    Swift
    Swift
    Swift
    iOS 判断某一日期是否在一日期区间
    iOS 本地推送通知
    iOS json解析中包含“ ”等解析出错
    iOS UILabel两侧加阴影
    IOS 设置ios中DatePicker的日期为中文格式
    [分享] 关于App Store下载到一半发生错误的问题 [复制链接]
  • 原文地址:https://www.cnblogs.com/codderYouzg/p/12419087.html
Copyright © 2020-2023  润新知