Java设计模式-结构型模式-代理模式

代理模式

代理模式

创建一个代理对象来控制对原始对象的访问,可以用来扩展原始对象的功能,同时保护原始对象

一般使用代理模式的目的有两个:

  1. 保护目标对象
  2. 增强目标对象

代理模式有两种实现方案:静态代理 和 动态代理

下面以Dao层执行sql语句为例,讲述代理模式的 应用

静态代理

静态代理就是通过组合/继承的方式,代理类关联原始类,通过调用代理类的方法来实现 原始类的方法增强

这里有一个UserDaoImpl,作为原始类,提供一个模拟插入用户的接口

java 复制代码
public interface UserDao {
    void insert();
}
/*****************************************************/

public class UserDaoImpl implements UserDao{

    public void insert(){
        System.out.println("插入一个用户");
    }
}

创建一个静态代理类,StaticProxyUserDao,完成对userDao的增强

java 复制代码
public class StaticProxyUserDao implements UserDao{

    private UserDao userDao;

    public StaticProxyUserDao(UserDao userDao) {
        this.userDao = userDao;
    }


    @Override
    public void insert(){
        //事务开启...
        System.out.println("事务开启");
        userDao.insert();
        //方法增强...
        //事务提交...
        System.out.println("事务提交");
    }
}

测试代码如下,很容易理解,就不解释了

java 复制代码
@Test
public void staticProxyTest(){
    //直接使用
    UserDao userDao = new UserDaoImpl();
    userDao.insert();

    System.out.println("---------------------------------------------------");
    //通过静态代理的方式,增强insert()方法
    StaticProxyUserDao staticProxyUserDao = new StaticProxyUserDao(userDao);
    staticProxyUserDao.insert();
}

输出:
插入一个用户
---------------------------------------------------
事务开启
插入一个用户
事务提交

动态代理

通过静态代理的方式可以实现方法的增强,但对于每个增强的类,都需要再写一个代理类,这样显得代码比较臃肿,所以就有了动态代理,可以在程序执行过程中,动态的代理目标类。

动态代理有两种实现方式:JDK动态代理CGlib动态代理

JDK动态代理

JDK动态代理是Java标准库中提供的一种代理方式

JDK动态代理是依赖接口的。

实现方式:通过Proxy类中的 静态方法 newProxyInstance(...)实现

java 复制代码
@Test
public void JdkProxyTest(){
    UserDao userDao = new UserDaoImpl();
    /*
    * 参数1:指定一个类加载器
    * 参数2:指定接口 getInterfaces()
    * 参数3:指定对应的代理方法
    * */
    System.out.println();
    UserDao proxyUserDao = (UserDao)Proxy.newProxyInstance(UserDao.class.getClassLoader(), UserDaoImpl.class.getInterfaces(), (proxy, method, args) -> {
        System.out.println("事务开启");
        Object invoke = method.invoke(userDao,args);
        System.out.println("事务关闭");
        return invoke;
    });
    proxyUserDao.insert();
}

可以看出来,JDK动态代理的方法 必须在接口中有定义,所以,如果想要代理没有在接口中定义的方法,这就需要使用CGlib来实现

CGlib动态代理

CGlib 是基于 类继承实现的,通过继承目标类 来实现类的代理,所以目标类必须是可继承的

CGlib支持更细粒度的代理------针对某个方法进行代理

CGLIB是第三方提供的包,需要引入Jar包

java 复制代码
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.1</version>
</dependency>

spring-core jar包里包含cglib ,所以使用spring框架的工程可以直接使用。

实现方式:通过Enhancer类实现,如下:

java 复制代码
@Test
public void CGlibProxyTest(){
    // 创建Enhancer对象 类似于 Proxy类
    Enhancer enhancer = new Enhancer();
    // 设置父类的字节码对象 即需要代理的类
    enhancer.setSuperclass(UserDaoImpl.class);
    //设置回调函数,增强方法
    /**
         * o 代理对象,即生成的代理类的实例,这里是生成的代理对象,不是被代理对象
         * method 被代理方法的Method对象
         * args 方法调用时传递的参数数组
         * methodProxy 方法代理对象
         */
    enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
        System.out.println("事务开启");
        //这里注意使用的是 invokeSuper() 调用的是父类的接口
        Object invoke = methodProxy.invokeSuper(o,objects);
        System.out.println("事务关闭");
        return invoke;
    });
    // 生成代理对象
    UserDaoImpl proxy = (UserDaoImpl) enhancer.create();
    proxy.insert();
}

这里提一下注意的点:

JDK动态代理,依赖接口完成代理,没有实例对象,所以需要传入实例对象,

即 method.invoke(userDao,args); 中的userDao,所以在代理之前,需要先有个实例化的对象

而CGlib动态代理,依赖类继承实现,本身是知道代理对象的所有结构的,所以不需要传入实例对象,所以:

enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {

System.out.println("事务开启");

Object invoke = methodProxy.invokeSuper(o,objects);

System.out.println("事务关闭");

return invoke;

});
第一个参数是 代理对象,而不是被代理对象,调用方法使用的是 invokeSuper() 不是invoke(),调用的是父类的接口

关于CGlib和JDK代理的详细代理过程,可以看下这篇文章
https://blog.csdn.net/ren9436/article/details/125602288

相关推荐
我只会发热几秒前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
是老余2 分钟前
本地可运行,jar包运行错误【解决实例】:通过IDEA的maven package打包多模块项目
java·maven·intellij-idea·jar
crazy_wsp2 分钟前
IDEA怎么定位java类所用maven依赖版本及引用位置
java·maven·intellij-idea
.Ayang5 分钟前
tomcat 后台部署 war 包 getshell
java·计算机网络·安全·web安全·网络安全·tomcat·网络攻击模型
一直学习永不止步10 分钟前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
博风19 分钟前
设计模式:6、装饰模式(包装器)
设计模式
A_cot20 分钟前
理解设计模式与 UML 类图:构建稳健软件架构的基石
microsoft·设计模式·简单工厂模式·工厂方法模式·uml
君败红颜22 分钟前
设计模式之创建模式篇
设计模式
hummhumm24 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
chusheng184028 分钟前
Java项目-基于SpringBoot+vue的租房网站设计与实现
java·vue.js·spring boot·租房·租房网站