详解 动态代理

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

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

原文地址:https://www.cnblogs.com/codderYouzg/p/12419087.html