前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >静态代理和动态代理区别(是否有实现类)

静态代理和动态代理区别(是否有实现类)

作者头像
ha_lydms
发布于 2023-08-09 13:13:49
发布于 2023-08-09 13:13:49
24900
代码可运行
举报
文章被收录于专栏:学习内容学习内容
运行总次数:0
代码可运行

一、简介

Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。

Java中使用代理技术主要用于扩展原功能又不侵入(修改)源代码主要应用: 比如想在某个类的某个方法执行之前打印日志或者记录下开始时间,但是又不好将打印日志和时间的逻辑写入原来的方法里。这时就可以创建一个代理类实现和原方法相同的方法,通过让代理类持有真实对象,然后代码调用的时候直接调用代理类的方法,来达到增强业务逻辑的目的。

1、代理分类:

静态代理: 由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

​ Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一旦需要修改接口,代理类和委托类都需要修改。

动态代理: 在程序运行时运用反射机制动态创建而成。

​ Java中的动态代理依靠反射来实现,代理类和委托类不需要实现同一个接口。委托类需要实现接口,否则无法创建动态代理。代理类在JVM运行时动态生成,而不是编译期就能确定。

Java动态代理主要涉及到两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler。代理类需要实现InvocationHandler接口或者创建匿名内部类,而Proxy用于创建动态动态。

2、代码区别:

静态代理:

  1. 接口类。
  2. 具体用户管理实现类。
  3. 代理类(业务增强类)
  4. 客户端调用。

动态代理:(少用户自己实现类)

  1. 接口类。
  2. 代理类(业务增强类)
  3. 客户端调用。

二、静态代理

1、接口定义要做的事情
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IBuyHouse {

//    定义要做的事情
    public void buyHouse();
}
2、具体用户实现类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 用户实现类(也就是自己做,需要干的事情)
 */
public class HouseDelegation implements IBuyHouse{

    @Override
    public void buyHouse() {
        System.out.println("去选房,购房.");
    }
}
3、代理类(业务增强类)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 代理类(代替你完成工作,可以附加功能)
 */
public class HouseAgent implements IBuyHouse{

    private IBuyHouse iBuyHouse;

    public HouseAgent(IBuyHouse iBuyHouse) {
        this.iBuyHouse = iBuyHouse;
    }

    @Override
    public void buyHouse() {
//        1、代替你完成工作
        iBuyHouse.buyHouse();
//        2、代理类新增的功能
        System.out.println("倒卖你的信息");

    }
}
4、测试类
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        HouseDelegation delegation = new HouseDelegation();
//        1、将你要做的事情,传递给代理对象
        HouseAgent agent = new HouseAgent(delegation);
//        2、执行代理类方法
        agent.buyHouse();
    }
}

优点:

代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。

缺点:

1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

通俗点的解释:

优点:

1、实现解耦,不需要知道怎么做,只知道代理即可。

缺点:

1、大量的重复代码,改代码,需要维护的量大。

2、每个类都写代码,程序太大时,无法实现。

三、动态代理

​ 使用动态代理,我们最大的改变就是不需要定义一个个的代理类了。最重要的是获取到代理对象,有了代理对象,我们就可以直接调用代理对象了。

1、JDK动态代理类

JDK动态代理不仅可以代理有接口有实现类的情况,也可以代理只有接口没有实现类的情况。

使用JDK动态代理无需引入任何外部的jar包,JDK已经给我们提供了一种获取代理对象的API只需要我们传入相关信息,它就可以返回我们需要的代理对象。 java.lang.reflect.Proxy类的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//CLassLoader loader:类的加载器  
//Class<?> interfaces:得到全部的接口(代理的全部接口)
//InvocationHandler h:得到InvocationHandler接口的子类的实例(增强业务逻辑的,也就是说增加额外功能)
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException  

java.lang.reflect.InvocationHandler接口的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Object proxy:被代理的对象  
//Method method:要调用的方法  
//Object[] args:方法调用时所需要参数  
public interface InvocationHandler {  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
}  
1.1、有接口有委托类的情况

1、接口定义要做的事情

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IBuyHouse {

//    定义要做的事情
    public void buyHouse();
}

2、具体用户实现类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 用户实现类(也就是自己做,需要干的事情)
 */
public class HouseDelegation implements IBuyHouse{

    @Override
    public void buyHouse() {
        System.out.println("去选房,购房.");
    }
}

3、代理类(业务增强类)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lydms.testPro;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 功能增强类
 */
public class MyProxyPlus implements InvocationHandler {

    // 把委托对象传递进来进行增强
    private Object object;

    public MyProxyPlus(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1、执行原来的业务逻辑
        Object result = method.invoke(object, args);
//        2、执行增强逻辑
        System.out.println("对原有的功能进行增强");
        return result; // 如果原有业务逻辑有返回值别忘了返回
    }
}

4、测试类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.lang.reflect.Proxy;
/**
 * 测试类
 */
public class test {

    public static void main(String[] args) {
//        获取IBuyHouse的代理对象
        HouseDelegation houseDelegation = new HouseDelegation();
//        1、将你要做的事情,传递给代理对象(并做功能增强MyProxyPlus)
        IBuyHouse iBuyHouse = (IBuyHouse) Proxy.newProxyInstance(houseDelegation.getClass().getClassLoader(),
                houseDelegation.getClass().getInterfaces(), new MyProxyPlus(houseDelegation));
//        2、执行代理类方法
        iBuyHouse.buyHouse();
    }
}

总结: 同样进行了增强,是不是代理类不见了呢!!!!这就是动态代理的好处,不需要你定义代理类了,你只需要能拿到代理对象就可以

1.2、仅有接口的情况

 假如说上面我们只定义了IBuyCar接口和IBuyHouse接口,没有委托类(实现类),也是可以玩的。定义一个InvocationHandler接口的实现,用于写业务逻辑,你把所有的业务逻辑写在invoke方法中就行了

1、接口定义要做的事情

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IBuyHouse {

//    定义要做的事情
    public void buyHouse();
}

2、代理类(业务增强类)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 功能增强类
 */
public class MyProxyPlus implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("对原有的功能进行增强");
        return null;
    }
}

3、测试类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.lang.reflect.Proxy;

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {

//        1、将你要做的事情,传递给代理对象(并做功能增强MyProxyPlus)
        IBuyHouse iBuyHouse = (IBuyHouse) Proxy.newProxyInstance(Test.class.getClassLoader(),
                new Class[]{IBuyHouse.class}, new MyProxyPlus());

//        2、执行代理类方法
        iBuyHouse.buyHouse();
    }
}

总结:

 业务逻辑从无到有不也是一种增强嘛!是不是代理类不见了而且连实现类都不需要了呢!!!!这就是我们Mapper动态代理的底层原理(只要定义接口,不需要写实现类)

2、CGLIB动态代理

 Java中的动态代理包括JDK动态代理和CGLIB动态代理。使用这两种代理方式我们都可以不用定义代理类,区别在于使用JDK动态代理必须有一个接口类,使用CGLIB动态代理不需要接口类。

 所以如果你要对一个实现了接口的类进行业务增强就用JDK动态代理,如果就对一个普通类进行业务增强就用CGLIB动态代理。如下

1、cglib是第三方jar,因此需要引入jar包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

2、具体用户实现类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Book {  
    public void addBook() {  
        System.out.println("新增图书...");  
    }  
}  

3、测试类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 Test {
    public static void main(String[] args) {
        Book book = new Book();

        // 1、获取book对象的代理对象,
        Book bookProxy = (Book) Enhancer.create(book.getClass(), new MethodInterceptor() {
            Object obj = null;

            // 2、都是对业务逻辑的增强
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("日志开始........................");
                obj = method.invoke(book, objects);
                System.out.println("日志结束........................");
                return obj;
            }
        });
//        3、执行方法
        bookProxy.addBook();
    }
}

结果:

总结:

cglib动态代理其实就是把原有对象传进去进行方法拦截,拦截到之后进行逻辑增强

三、总结

  1. 使用代理技术就是为了帮我们在不入侵原有代码的情况下增强业务逻辑。
  2. 你完全可以使用静态代理一个一个去定义代理类,但是这样的话太过于繁琐,而且有些情况下你不知道未来会有什么接口(比如咱们的Mybatis,你现在有个UserMapper.java,以后还可能有更多其他的Mapper接口,这些都是不确定的),所以最好采用动态代理生成代理对象吧。
  3. 有接口就用JDK动态代理。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Oracle 分析函数 ROW_NUMBER() 使用
在使用 row_number() over()函数时候,over()里头的分组以及排序的执行晚于 where group by  order by 的执行。
星哥玩云
2022/08/13
9740
oracle最强大函数之一decode函数的使用[通俗易懂]
大家好,又见面了,我是你们的朋友全栈君。 decode的几种用法 1:使用decode判断字符串是否一样 DECODE(value,if1,then1,if2,then2,if3,then3,…,else) 含义为 IF 条件=值1 THEN     RETURN(value 1) ELSIF 条件=值2 THEN     RETURN(value 2)     …… ELSIF 条件=值n THEN     RETURN(value 3) ELSE     RETURN(default) END IF
全栈程序员站长
2022/09/16
5820
Oracle 数据库入门之----------------------多表查询
  SUM(SAL)                                                                                                                                                                                              
互联网CEO
2018/12/11
6240
Oracle 数据库入门之----------------------基本查询
TNAME                          TABTYPE  CLUSTERID                               
互联网CEO
2018/12/11
5850
Oracle学习(二):过滤和排序
1.知识点:可以对照下面的录屏进行阅读 SQL> --字符串大小写敏感 SQL> --查询名叫KING的员工信息 SQL> select * 2 from emp 3 where ename = 'KING'; SQL> --日期格式敏感 SQL> --查询入职日期为17-11月-81的员工 SQL> select * 2 from emp 3 where hiredate='17-11月-81'; --正确例子 SQL> ed 已写入 file afiedt.buf 1
Java架构师必看
2021/05/17
7590
SQL基础-->过滤和排序
--=======================================
Leshami
2018/08/07
6910
Oracle学习(五):多表查询
1.知识点:可以对照下面的录屏进行阅读 SQL> --等值连接 SQL> --查询员工信息: 员工号 姓名 月薪 部门名称 SQL> select empno,ename,sal,dname 2 from emp,dept 3 where emp.deptno=dept.deptno; SQL> --不等值连接 SQL> --查询员工信息: 姓名 月薪 工资级别 SQL> select ename,sal,grade 2 from emp e,salgrade s 3 wh
Java架构师必看
2021/05/17
4240
MySQL去重distinct
在MySQL中需要查询表中不重复的记录时,可以使用distinct关键字过滤重复记录。
Python之道
2020/04/10
3.9K0
Oracle学习(一):基本操作和基本查询语句
文中以"--"开头的语句为注释,即为绿色部分 1.知识点:可以对照下面的录屏进行阅读 SQL> --录屏工具spool,开始录制,并指定保存路径为c:\基本查询.txt SQL>spool c:\基本查询.txt SQL> --清屏 SQL> host cls SQL> --查看当前用户 SQL> show user USER 为 "SCOTT" SQL> --查询当前用户下的表 SQL> select * from tab; SQL> -- tab数据字典(管理员提供的表) SQL> des
Java架构师必看
2021/05/17
5280
Oracle 数据库入门之----------------------,多行函数
  SUM(SAL)                                                                                                                                                                                        
互联网CEO
2018/12/11
6020
sql系列(基础)-第二章 限制和排序数据
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/117647.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/05
1970
day42_Oracle学习笔记_01
虚拟机上的orcl数据库,所在位置:C:\app\Training\oradata\orcl
黑泽君
2018/10/11
9410
day42_Oracle学习笔记_01
SQL 基础--> 集合运算(UNION 与UNION ALL)
--=============================================
Leshami
2018/08/07
6970
day43_Oracle学习笔记_02
八、子查询 示例代码如下: 子查询.txt SQL> --rownum 行号 SQL> select rownum,empno,ename,sal from emp;     ROWNUM      EMPNO ENAME             SAL                                                                                                                                
黑泽君
2018/10/11
7850
day43_Oracle学习笔记_02
02 . Mysql基础操作及增删改查
SQL(Structured Query Language 即结构化查询语言) SQL语言主要用于存取数据、查询数据、更新数据和管理关系数据库系统,SQL语言由IBM开发。
iginkgo18
2020/09/27
1.9K0
Oracle 11g LogMiner解析redo日志
SQL> alter system set nls_date_format='yyyy-mm-dd hh24:mi:ss' scope=spfile;
星哥玩云
2022/08/17
8600
Oracle 11g LogMiner解析redo日志
CHECKSUM 分析函数
本文概述了Oracle database 21c中引入的 CHECKSUM 分析函数。可以用于检查表的内容是否已变更。
Yunjie Ge
2022/04/24
9980
Oracle基础教程之手动创建 emp 表 与 dept 表
说明: 有时候我们需要通用的实验数据,emp表 与 dept表  但是Oracle数据库中有没有。 这时,我们可以手动创建。
星哥玩云
2022/08/16
1.8K0
Oracle数据库常见的误操作恢复方法(上)
闪回回来的表如果之前有主键和索引,对应的名字会变成一串字符,虽然不影响使用,但为了规范管理,建议将它们重命名为之前的名字
Alfred Zhao
2022/05/06
4000
Oracle学习(三):单行函数
1.知识点:可以对照下面的录屏进行阅读 SQL> --字符函数 SQL> --字符串的转换 SQL> select lower('hellO WORld') 转小写,upper('hellO WORld') 转大写,initcap('hello world') 首字母大写 2 from dual; SQL> --substr(a,b) 从a中,第b位开始取,取右边所有的字符 SQL> select substr('Hello World',4) from dual; SQL> --subst
Java架构师必看
2021/05/17
1.1K0
相关推荐
Oracle 分析函数 ROW_NUMBER() 使用
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 一、简介
    • 1、代理分类:
    • 2、代码区别:
  • 二、静态代理
    • 1、接口定义要做的事情
    • 2、具体用户实现类
    • 3、代理类(业务增强类)
    • 4、测试类
  • 三、动态代理
    • 1、JDK动态代理类
      • 1.1、有接口有委托类的情况
      • 1.2、仅有接口的情况
    • 2、CGLIB动态代理
  • 三、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档