代理模式(Proxy Pattern)

概述

代理是一种结构型设计模式,让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。

分类

  • 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
  • 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

应用实例

  1. Windows 里面的快捷方式
  2. 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类
  3. 买火车票不一定在火车站买,也可以去代售点
  4. 代购
  5. 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制
  6. 信用卡是银行账户的代理,银行账户则是一大捆现金的代理。它们都实现了同样的接口,均可用于进行支付。消费者会非常满意,因为不必随身携带大量现金;商店老板同样会十分高兴,因为交易收入能以电子化的方式进入商店的银行账户中,无需担心存款时出现现金丢失或被抢劫的情况
  7. Spring AOP

优点

  • 职责清晰

    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。

  • 高扩展性

    具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。

  • 智能化

基本结构

  1. 服务接口(Service Interface)声明了服务接口。代理必须遵循该接口才能伪装成服务对象。
  2. 服务(Service)类提供了一些实用的业务逻辑。
  3. 代理(Proxy)类包含一个指向服务对象的引用成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期进行管理。
  4. 客户端(Client) 能通过同一接口与服务或代理进行交互,所以你可在一切需要服务对象的代码中使用代理。

静态代理【重点】

概述

静态代理是指预先确定了代理与被代理者的关系,即代理类与被代理类的依赖关系在编译期间就确定了。

静态代理又分为强制代理和普通代理。普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色;强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。

案例一

描述

永强的公司老板突然在发工资的前一天带着小姨子跑路了,可怜的永强一身房贷,被迫提起劳动仲裁,劳动局就会为其指派一位代理律师全权负责永强的仲裁事宜。那这里面就是使用了代理模式,因为在劳动仲裁这个活动中,代理律师会全权代理永强。

案例代码

Interface

// 诉讼接口
public interface LawSuit {
    // 提起诉讼
    void submit();

    // 法庭辩护
    void defend();
}

Service

// 诉讼当事人(原告)
public class Parties implements LawSuit {
    @Override
    public void submit() {
        System.out.println("老板欠薪跑路,证据确凿!!!");
    }

    @Override
    public void defend() {
        System.out.println("铁证如山,欠债还钱");
    }
}

Proxy

// 代理律师类,全权负责案件的代理
public class Lawyer implements LawSuit {
    // 包含一个被代理的对象
    private final LawSuit lawSuit;

    // 通过构造方法传递被代理对象
    public Lawyer(LawSuit lawSuit) {
        this.lawSuit = lawSuit;
    }

    @Override
    public void submit() {
        // 搜集证据
        this.gatherEvidence();

        // 提起诉讼
        this.lawSuit.submit();
    }

    @Override
    public void defend() {
        // 法庭辩护
        this.lawSuit.defend();

        // 交换意见
        this.compareNotes();
    }

    // 搜集证据(属于增强的方法)
    private void gatherEvidence() {
        System.out.println("搜集证据");
    }

    // 交换意见(属于增强的方法)
    private void compareNotes() {
        System.out.println("与审判长、审判员交换意见,被告如果不服,代其上诉或申诉");
    }
}

Client

// 开庭
public class Court {
    @Test
    public void testLawSuit() {
        // 当事人安排上
        Parties parties = new Parties();

        // 当事人的代理律师安排上
        Lawyer lawyer = new Lawyer(parties);

        // 代理律师进行提起诉讼以及法庭辩护
        lawyer.submit();
        lawyer.defend();
    }
}

案例二

描述

玉田是个游戏死忠粉,每天最大的乐趣就是在游戏里大杀四方,但是游戏打时间长了,腰酸背痛、眼睛干涩、手臂酸麻,等等,也就是网络成瘾综合症都出来了。玉田想玩游戏,但又不想碰触到游戏中的烦恼,如何解决呢?有办法,现在游戏代练的公司非常多,于是玉田把自己的账号交给代练人员,由他们去帮着升级,去打怪,非常好的想法。游戏代练者,它也不能有作弊的方法(开挂),也只能手动打怪,这个时候,玉田的代练者就上着他的号,扮演着他的角色在游戏里继续大杀四方。

案例代码

Interface

// 游戏接口
public interface GamePlay {
    // 登录
    void login();

    // 打怪
    void killBoss();

    // 升级
    void upgrade();
}

Service

// 游戏玩家(被代理对象)
public class GamePlayer implements GamePlay {
    // 用户名
    private final String username;

    // 通过构造方法进行传递参数
    public GamePlayer(String username) {
        this.username = username;
    }

    // 登录
    @Override
    public void login() {
        System.out.println(username + "登录成功>_<");
    }

    // 打怪
    @Override
    public void killBoss() {
        System.out.println(username + "正在打怪...");
    }

    // 升级
    @Override
    public void upgrade() {
        System.out.println(username + "已升级!!!");
    }
}

Proxy

// 代练,也要实现游戏接口
public class GamePlayProxy implements GamePlay {
    // 包含了一个游戏玩家
    private final GamePlay gamePlayer;

    // 通过构造方法对其进行赋值
    public GamePlayProxy(GamePlayer gamePlay) {
        this.gamePlayer = gamePlay;
    }

    // 登录
    @Override
    public void login() {
        this.gamePlayer.login();
    }

    // 打怪
    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    // 升级
    @Override
    public void upgrade() {
        this.gamePlayer.killBoss();
        // 计算时间
        this.count();
    }

    // 计算时间
    public void count() {
        System.out.println("本次共花费了150金币");
    }
}

Client

// 游戏开始
public class Client {
    @Test
    public void playByProxy() {
        // 网瘾少年上号
        GamePlayer player = new GamePlayer("易烊千玺");

        // 代练上号
        GamePlayProxy proxy = new GamePlayProxy(player);

        // 执行方法
        proxy.login();
        proxy.killBoss();
        proxy.upgrade();
    }
}

案例三

描述

刘英是个刚毕业的大学生,找到一份离家较远的工作,没办法回家住,于是她决定在单位附近15分钟路程的小区租房住,但是苦于人生地不熟,马上就要上班,自己在附近并没有熟人可以给她提供房源,于是她打开了手机上的58同城...

案例代码

Interface

// 租房接口
public interface Rent {
    // 租房
    void rent();
}

Service

// 房东(被代理对象)
public class Lord implements Rent {
    // 租房
    @Override
    public void rent() {
        System.out.println("房东租房");
    }
}

Proxy

// 中介
public class Agent implements Rent {
    // 包含一个房东对象
    private final Lord lord;

    // 通过构造方法进行传参
    public Agent(Lord lord) {
        this.lord = lord;
    }

    // 租房
    @Override
    public void rent() {
        lord.rent();
    }
}

Client

// 租客
public class Renter {
    @Test
    public void rent() {
        // 房东安排上
        Lord lord = new Lord();

        // 中介安排上
        Agent agent = new Agent(lord);

        // 中介出租了方法
        agent.rent();
    }
}

动态代理

动态代理本质上仍然是代理,只是代理与被代理人的关系是动态确定的。映射到编程领域为这个关系是在运行时确定的。

JDK 动态代理

概述

自 Java 1.3 以后,Java 提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了 Spring 的很多地方。JDK 动态代理主要涉及 java.lang.reflect 包下边的两个类:Proxy 和 InvocationHandler。其中,InvocationHandler 是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

【注意】要实现JDK动态代理的首要条件是:被代理类必须实现一个接口

原理

JDK 动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个 InvocationHandler,该 InvocationHandler 包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。

核心 API

// 由代理对象的调用处理程序实现的接口,在代理对象上调用方法时,该方法调用将被编码并分派到其调用处理程序的invoke方法。
interface InvocationHandler {
    // 处理代理对象上的方法调用并返回结果
    // 参数:
    //      proxy ——> 代理对象
    //		method --> 被代理类要执行的方法
    //		args --> 被代理类要执行的方法的参数
    // 返回值:被代理类要执行的方法的返回值
    public Object invoke(Object proxy, Method method, Object[] args);
}

// 提供了用于创建动态代理类和实例的静态方法
class Proxy {
    // 返回指定接口的代理类对象,该对象将方法调用分派到指定的调用处理程序
    // 参数:
    //      loader ——> 被代理类的类加载器
    //		interfaces --> 被代理类要实现的所有接口
    //		h --> 将方法调用分派给指定的执行处理器
    // 返回值:代理对象
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
}

案例一

Interface

// 诉讼接口
public interface LawSuit {
    // 提起诉讼
    void submit();

    // 法庭辩护
    void defend();
}

Service

// 诉讼当事人(原告)
public class Parties implements LawSuit {
    @Override
    public void submit() {
        System.out.println("老板欠薪跑路,证据确凿!!!");
    }

    @Override
    public void defend() {
        System.out.println("铁证如山,欠债还钱");
    }
}

InvocationHandler

// 律师事务所,实现JDK动态代理的核心接口InvocationHandler
public class LawOffice implements InvocationHandler {
    // 包含一个动态的被代理对象(当事人)
    private final Object target;

    // 通过构造方法进行传参
    public LawOffice(Object obj) {
        this.target = obj;
    }

    /**
     * 此方法用于代理对象调用被代理对象的操作
     *
     * @param proxy 代理对象(律师)
     * @param method 被代理对象的方法
     * @param args 被代理对象的方法所需要的参数
     * @return 被代理对象的方法执行后的返回值
     * @throws Throwable 非法参数异常、安全异常、空指针异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取被代理对象方法的方法名
        System.out.println("案情进展:" + method.getName());

        // 执行被代理对象的方法
        return method.invoke(target, args);
    }
}

Client

// 开庭
public class Court {
    @Test
    public void testLawSuit() {
        // 安排一个当事人(被代理对象)
        Parties parties = new Parties();

        // 安排一个律师(代理对象)
        InvocationHandler handler = new LawOffice(parties);

        // 根据被代理对象的类加载器、类的所有对象以及InvocationHandler接口实现类来获取代理对象(代理律师)
        LawSuit lawSuit = (LawSuit) Proxy.newProxyInstance(parties.getClass().getClassLoader(), parties.getClass().getInterfaces(), handler);

        // 代理对象执行方法
        lawSuit.submit();
        lawSuit.defend();
    }
}

案例二

Interface

// 游戏接口
public interface GamePlay {
    // 登录
    void login();

    // 打怪
    void killBoss();

    // 升级
    void upgrade();
}

Service

// 游戏玩家(被代理对象)
public class GamePlayer implements GamePlay {
    // 用户名
    private final String username;

    // 通过构造方法进行传递参数
    public GamePlayer(String username) {
        this.username = username;
    }

    // 登录
    @Override
    public void login() {
        System.out.println(username + "登录成功>_<");
    }

    // 打怪
    @Override
    public void killBoss() {
        System.out.println(username + "正在打怪...");
    }

    // 升级
    @Override
    public void upgrade() {
        System.out.println(username + "已升级!!!");
    }
}

InvocationHandler

// 游戏工作室
public class GameOffice implements InvocationHandler {
    // 声明一个被代理对象
    private final Object object;

    // 通过构造方法进行传递参数
    public GameOffice(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 被代理对象的方法之前执行
        if (method.getName().equals("login")) {
            System.out.println("当前用户正在登录");
        }

        // 执行被代理对象的方法
        Object result = method.invoke(object, args);

        // 被代理对象的方法之后执行
        if (method.getName().equals("upgrade")) {
            System.out.println("耗时3天3夜,花费300金币升了一级");
        }

        return result;
    }
}

Client

// 游戏开始
public class Client {
    @Test
    public void play() {
        // 网瘾少年出场
        GamePlayer gamePlayer = new GamePlayer("易烊千玺");

        // 安排一个游戏工作室
        InvocationHandler handler = new GameOffice(gamePlayer);

        // 获取一个游戏代练
        GamePlay gamePlay = (GamePlay) Proxy.newProxyInstance(gamePlayer.getClass().getClassLoader(), gamePlayer.getClass().getInterfaces(), handler);

        // 执行对应的方法
        gamePlay.login();
        gamePlay.killBoss();
        gamePlay.upgrade();
    }
}

案例三

Interface

// 租房接口
public interface Rent {
    // 租房
    void rent();
}

Service

// 房东(被代理对象)
public class Lord implements Rent {
    // 租房
    @Override
    public void rent() {
        System.out.println("房东租房");
    }
}

InvocationHandler

// 中介公司
public class Agent implements InvocationHandler {
    // 包含一个被代理的对象
    private final Object object;

    // 通过构造方法进行赋值
    public Agent(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介看房");

        // 执行被代理对象的方法
        Object result = method.invoke(object, args);

        System.out.println("签合同");

        return result;
    }
}

Client

// 租客租房
public class Renter {
    @Test
    public void rent() {
        // 房东安排上
        Lord lord = new Lord();

        // 中介公司安排上
        InvocationHandler handler = new Agent(lord);

        // 获取代理对象
        Rent rent = (Rent) Proxy.newProxyInstance(lord.getClass().getClassLoader(), lord.getClass().getInterfaces(), handler);

        // 执行方法
        rent.rent();
    }
}

CGLib 动态代理

概述

由于 JDK 只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以 cgLib 横空出世!CGLib(Code Generation Library)是一个强大、高性能的 Code 生成类库,它可以在程序运行期间动态扩展类或接口,AOP,测试,数据访问框架使用它来生成动态代理对象并拦截字段访问。它的底层是使用 java 字节码操作框架 ASM 实现。

GitHub:https://github.com/cglib/cglib

原理

动态生成一个要代理类的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它在运行的时候比使用 Java 反射的 JDK 动态代理要快,但是在创建代理对象的时候要慢。

核心 API

// 方法增强回调器,用来拦截代理对象的方法并对其增强
interface MethodInterceptor extends Callback {
    
    // 所有的代理对象都将执行此方法而不是原始方法
    // 参数:
    // 		obj --> 被代理对象
    // 		method --> 被代理对象的方法
    // 		args --> 被代理对象的方法参数
    //		proxy --> 代理对象
    // 返回值:被代理对象的方法返回值
    Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}

// 代理类
class MethodProxy {
    // 调用相同类型且不同对象的原始方法,此方法可能会导致递归,进而引发栈内存溢出,慎用
    Object invoke(Object obj, Object[] args)
    
    // 调用指定对象的原始方法
    Object invokeSuper(Object obj, Object[] args);
}

// 生成动态代理对象以启用方法拦截,该类允许代理除了实现接口外,扩展一个具体的基类
class Enhancer {
    // 设置被代理类
    // 参数:
    // 		superclass --> 被代理类,必须具有可访问的构造方法
    void setSuperclass(Class superclass);
    
    // 设置被代理接口
    void setInterfaces(Class[] interfaces);
    
    // 设置回调函数
    // 参数:
    // 		callback:传入MethodInterceptor的接口实现类
    void setCallback(final Callback callback);
    
    // 根据无参构造创建一个代理对象
    Object create();
    
    // 根据有参构造创建一个代理对象
    Object create(Class[] argumentTypes, Object[] arguments);
}

案例一

pom.xml

<dependencies>
    <!--cglib动态代理-->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Service

// 被代理对象
public class Parties {
    // 提起诉讼
    public void submit() {
        System.out.println("老板欠薪跑路,证据确凿!!!");
    }

    // 法庭辩护
    public void defend() {
        System.out.println("铁证如山,欠债还钱");
    }
}

MethodInterceptor

// 律师事务所,实现CGLib动态代理的核心接口MethodInterceptor
public class LawOffice implements MethodInterceptor {
    /**
     * 所有生成的代理方法都调用此方法,而不是原始方法
     *
     * @param o 被代理对象
     * @param method 被代理对象的方法
     * @param objects 被代理对象的方法参数
     * @param methodProxy 代理对象,用来调用未被拦截的方法,可多次调用
     * @return 被代理对象的方法返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("案件进度:" + method.getName());

        // 执行被代理对象中的方法
        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    }
}

Client

// 开庭
public class Court {
    @Test
    public void test() {
        // 安排一个当事人
        Parties parties = new Parties();

        // cgLib动态代理核心类,用于生成代理对象
        Enhancer enhancer = new Enhancer();

        // 指定被代理对象
        enhancer.setSuperclass(parties.getClass());
        // 设置回调拦截
        enhancer.setCallback(new LawOffice());
        // 创建代理对象
        Parties lawyer = (Parties) enhancer.create();

        // 执行方法
        lawyer.submit();
        lawyer.defend();
    }
}

案例二

pom.xml

<dependencies>
    <!--cglib动态代理-->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Service

// 游戏玩家(被代理对象)
public class GamePlayer implements GamePlay {
    // 用户名
    private final String username;

    // 有参构造
    public GamePlayer(String username) {
        this.username = username;
    }

    // 登录
    @Override
    public void login() {
        System.out.println(username + "登录成功>_<");
    }

    // 打怪
    @Override
    public void killBoss() {
        System.out.println(username + "正在打怪...");
    }

    // 升级
    @Override
    public void upgrade() {
        System.out.println(username + "已升级!!!");
    }
}

MethodInterceptor

// 代练工作室
public class GameOffice implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 被代理对象的方法执行前
        if (method.getName().equals("login")) {
            System.out.println("代练正在登录...");
        }

        // 执行被代理对象的方法
        Object result = proxy.invokeSuper(obj, args);

        // 被代理对象的方法执行后
        if (method.getName().equals("upgrade")) {
            System.out.println("此次升级花费3天3夜...");
        }

        return result;
    }
}

Client

// 游戏开始
public class Client {
    @Test
    public void play() {
        // 网瘾少年上线
        GamePlayer gamePlayer = new GamePlayer("易烊千玺");

        // 准备CGLib的核心类对象
        Enhancer enhancer = new Enhancer();

        // 设置被代理对象
        enhancer.setSuperclass(gamePlayer.getClass());
        // 设置回调
        enhancer.setCallback(new GameOffice());
        // 根据有参方法创建对象
        GamePlayer player = (GamePlayer) enhancer.create(new Class[]{String.class}, new Object[]{"易烊千玺"});

        // 执行方法
        player.login();
        player.killBoss();
        player.upgrade();
    }
}

案例三

pom.xml

<dependencies>
    <!--cglib动态代理-->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Service

// 房东(被代理对象)
public class Lord implements Rent {
    // 租房
    @Override
    public void rent() {
        System.out.println("房东租房");
    }
}

MethodInterceptor

// 中介公司
public class Agent implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("带租客看房");

        // 执行被代理对象的方法
        Object result = proxy.invokeSuper(obj, args);

        System.out.println("签合同");

        return result;
    }
}

Client

// 租客
public class Rent {
    @Test
    public void rent() {
        // 房东安排上
        Lord lord = new Lord();

        // 获取CGLib核心类对象
        Enhancer enhancer = new Enhancer();

        // 设置被代理对象
        enhancer.setSuperclass(lord.getClass());
        // 设置回调
        enhancer.setCallback(new Agent());
        // 获取代理对象
        Lord proxy = (Lord) enhancer.create();

        // 租房
        proxy.rent();
    }
}

案例场景

概述

模拟实现 mybatis 中代理类生成部分,即 Mybatis 的使用中只需要定义接口不需要写实现类就可以完成增删改查操作。

案例代码

自定义注解

// 自定义注解
@Documented
// 作用域
@Retention(RetentionPolicy.RUNTIME)
// 作用位置
@Target({ElementType.METHOD})
public @interface Select {
    // sql语句
    String value() default "";
}

InvocationHandler

// 自定义InvocationHandler接口实现类
public class Handler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取方法上的注解
        Select select = method.getAnnotation(Select.class);

        // 展示
        System.out.println("sql:" + select.value().replace("#{id}", args[0].toString()));

        // 连接数据库获取受影响的行数
        int affectedRows = 1;

        return affectedRows;
    }
}

代理工厂类

// 代理工厂类
public class MapperFactoryBean<T> {
    // 声明传入的接口类型(被代理接口)
    private final Class<T> mapperInterface;

    // 通过构造方法进行赋值
    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    // 获取代理对象
    public T getObject() {
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, new Handler());
    }
}

dao 层接口

// dao层接口
public interface UserDao {
    @Select("select name from user where id = #{id}")
    int findById(String id);
}

测试类

public class TestUserDao {
    @Test
    public void test() {
        // 获取mapper工厂
        MapperFactoryBean<UserDao> factoryBean = new MapperFactoryBean<>(UserDao.class);

        // 获取代理对象
        UserDao userDao = factoryBean.getObject();

        // 执行sql语句获取受影响的行数
        int affectedRows = userDao.findById("1"); // sql:select name from user where id = 1

        System.out.println("受影响的行数:" + affectedRows);
    }
}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐