javassist implements interface 模拟mybatis 生成代理类

 

动态创建代理对象的工具类 

package com.wsd.util;import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.session.SqlSession;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;/*** 使用javassist库动态生成dao接口的实现类* @author: Mr.Wang* @create: 2023-07-08 18:44**/
public class ProxyUtil {public static Object getMyMapper(SqlSession sqlSession, Class daoInterface){//mybatis 依赖中内置了 javassist,不需要再引入javassist dependency//获取一个默认的 ClassPool 实例//Javassist 是一个 Java 字节码编辑库,它可以在运行时修改已加载的类或者生成新的类。// ClassPool 是 Javassist 的核心组件,它是一个类容器,负责存储和管理字节码(.class 文件)ClassPool pool = ClassPool.getDefault();// 生成代理类CtClass ctClass = pool.makeClass(daoInterface.getPackage().getName() + ".impl." + daoInterface.getSimpleName() + "Impl");// 接口CtClass ctInterface = pool.makeClass(daoInterface.getName());// 代理类实现接口ctClass.addInterface(ctInterface);// 获取所有的方法Method[] methods = daoInterface.getDeclaredMethods();Arrays.stream(methods).forEach(method -> {// 拼接方法的签名StringBuilder methodStr = new StringBuilder();String returnTypeName = method.getReturnType().getName();//返回值类型methodStr.append(returnTypeName);methodStr.append(" ");//方法名String methodName = method.getName();methodStr.append(methodName);methodStr.append("(");//参数列表Class<?>[] parameterTypes = method.getParameterTypes();for (int i = 0; i < parameterTypes.length; i++) {methodStr.append(parameterTypes[i].getName());methodStr.append(" arg");methodStr.append(i);//不是最后一个参数的情况下,需要拼接一个逗号if (i != parameterTypes.length - 1) {methodStr.append(",");}}methodStr.append("){");// 方法体当中的代码怎么写?// 获取sqlId(这里非常重要:因为这行代码导致以后namespace必须是接口的全限定接口名,sqlId必须是接口中方法的方法名。)String sqlId = daoInterface.getName() + "." + methodName;// 获取SqlCommondType 获取配置文件中该sql tag 的类型String sqlCommondTypeName = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType().name();if ("SELECT".equals(sqlCommondTypeName)) {methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.wsd.util.SqlSessionUtil.openSession();");methodStr.append("Object obj = sqlSession.selectOne(\"" + sqlId + "\", arg0);");methodStr.append("return (" + returnTypeName + ")obj;");} else if ("UPDATE".equals(sqlCommondTypeName)) {methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.wsd.util.SqlSessionUtil.openSession();");methodStr.append("int count = sqlSession.update(\"" + sqlId + "\", arg0);");methodStr.append("return count;");}methodStr.append("}");System.out.println(methodStr);try {// 创建CtMethod对象CtMethod ctMethod = CtMethod.make(methodStr.toString(), ctClass);//添加访问修饰符publicctMethod.setModifiers(Modifier.PUBLIC);// 将方法添加到类ctClass.addMethod(ctMethod);} catch (Exception e) {throw new RuntimeException(e);}});try {// 创建代理对象Class<?> aClass = ctClass.toClass();Constructor<?> defaultCon = aClass.getDeclaredConstructor();Object o = defaultCon.newInstance();return o;} catch (Exception e) {throw new RuntimeException(e);}}
}

 

pom.xml 

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wsd</groupId><artifactId>web-mybatis01</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>web-mybatis01 Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><!--mybatis依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version></dependency><!--MySQL驱动依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--logback 日志依赖--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.11</version></dependency><!--servlet 依赖--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency></dependencies><build><finalName>web-mybatis01</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.2.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin></plugins></pluginManagement></build>
</project>

dao interface

package com.wsd.dao;import com.wsd.pojo.Account;/*** @author: Mr.Wang* @create: 2023-07-02 01:30**/
public interface AccountDao {/*** 根据账号获取账户信息* @param actno 账号* @return 账户信息*/Account selectByActno(String actno);/*** 更新账户信息* @param act 账户信息* @return 1表示更新成功,其他值表示失败*/int update(Account act);
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsd.dao.AccountDao"><select id="selectByActno" resultType="com.wsd.pojo.Account">select * from t_act where actno = #{actno}</select><update id="update">update t_act set balance = #{balance} where actno = #{actno}</update>
</mapper>

mybatis 配置文件 

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment>
</environments>
<mappers><mapper resource="AccountMapper.xml"/>
</mappers>
</configuration>

jdbc.properties 

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

获取sqlsession的工具类 

package com.wsd.util;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;/*** @description: Utility class for mybatis* @author: Mr.Wang* @create: 2023-06-17 17:38**/public class SqlSessionUtil {private SqlSessionUtil(){}private static SqlSessionFactory sqlSessionFactory;//保存sqlSession , 一个线程一个private static ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();/*** 类加载时初始化sqlSessionFactory对象*/static {try {SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));} catch (Exception e) {e.printStackTrace();}}/*** 每调用一次openSession()可获取一个新的会话。** @return 新的会话对象*/public static SqlSession openSession() {// Get a sqlSession instance from sqlSessionThreadLocalSqlSession sqlSession = sqlSessionThreadLocal.get();//如果 sqlSessionThreadLocal 未获取到 sqlSession,让工厂生产一个新的sqlSessionif(sqlSession == null){sqlSession = sqlSessionFactory.openSession();sqlSessionThreadLocal.set(sqlSession);}return sqlSession;}/*** @description close sqlSession and remove it from sqlSessionThreadLocal* @param sqlSession* @return*/public static void close(SqlSession sqlSession) {if(sqlSession != null){sqlSession.close();//remove from sqlSessionThreadLocalsqlSessionThreadLocal.remove();}}
}

封装数据的 pojo  class

package com.wsd.pojo;/*** @description: pojo for Account* @author: Mr.Wang* @create: 2023-07-01 23:41**/
public class Account {private Long id;private String actno;private Double balance;@Overridepublic String toString() {return "Account{" +"id=" + id +", actno='" + actno + '\'' +", balance=" + balance +'}';}public Account() {}public Account(Long id, String actno, Double balance) {this.id = id;this.actno = actno;this.balance = balance;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getActno() {return actno;}public void setActno(String actno) {this.actno = actno;}public Double getBalance() {return balance;}public void setBalance(Double balance) {this.balance = balance;}
}

service interface

package com.wsd.service;import com.wsd.exception.AppException;
import com.wsd.exception.MoneyNotEnoughException;public interface AccountService {void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException;
}

实现类

package com.wsd.service.impl;import com.wsd.dao.AccountDao;
import com.wsd.exception.AppException;
import com.wsd.exception.MoneyNotEnoughException;
import com.wsd.pojo.Account;
import com.wsd.service.AccountService;
import com.wsd.util.ProxyUtil;
import com.wsd.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;/*** @author: Mr.Wang* @create: 2023-07-02 01:19**/
public class AccountServiceImpl implements AccountService {//使用工具类生成代理对象private AccountDao accountDao = (AccountDao) ProxyUtil.getMyMapper(SqlSessionUtil.openSession(), AccountDao.class);@Overridepublic void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException,AppException{//创建sql sessionSqlSession sqlSession =  SqlSessionUtil.openSession();// 查询转出账户的余额Account fromAct = accountDao.selectByActno(fromActno);//余额不足,抛出异常if (fromAct.getBalance() < money) {throw new MoneyNotEnoughException("对不起,您的余额不足。");}// 程序如果执行到这里说明余额充足// 修改账户余额Account toAct = accountDao.selectByActno(toActno);fromAct.setBalance(fromAct.getBalance() - money);toAct.setBalance(toAct.getBalance() + money);// 更新数据库int count = accountDao.update(fromAct);/*//模拟异常String s = null;s.toString();*/count += accountDao.update(toAct);if (count != 2) {throw new AppException("转账失败,未知原因!");}sqlSession.commit();SqlSessionUtil.close(sqlSession);}
}

Exception

package com.wsd.exception;/*** @author: Mr.Wang* @create: 2023-07-02 11:57**/
public class AppException extends Exception {public AppException() {}public AppException(String message) {super(message);}
}
package com.wsd.exception;/*** @author: Mr.Wang* @create: 2023-07-02 09:06**/
public class MoneyNotEnoughException extends Exception{public MoneyNotEnoughException() {}public MoneyNotEnoughException(String message) {super(message);}
}

servlet

package com.wsd.web;import com.wsd.exception.AppException;
import com.wsd.exception.MoneyNotEnoughException;
import com.wsd.service.AccountService;
import com.wsd.service.impl.AccountServiceImpl;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @description:* @author: Mr.Wang* @create: 2023-07-02 00:52**/
@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {private AccountService accountService = new AccountServiceImpl();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取页面表单数据String fromActno = req.getParameter("fromActno");String toActno = req.getParameter("toActno");Double money = Double.parseDouble( req.getParameter("money") ) ;//调用servicetry {accountService.transfer(fromActno,toActno,money);//Show the result after transfer successresp.sendRedirect(req.getContextPath()  + "/success.html");} catch (MoneyNotEnoughException e) {resp.sendRedirect(req.getContextPath()  + "/error1.html");} catch (Exception e) {resp.sendRedirect(req.getContextPath() + "/error2.html");}}
}

页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>银行账户转账</title>
</head>
<body>
<!--/mybatis是应用的根,部署web应用到tomcat的时候一定要注意这个名字-->
<form action="/mybatis/transfer" method="post">转出账户:<input type="text" name="fromActno"/><br>转入账户:<input type="text" name="toActno"/><br>转账金额:<input type="text" name="money"/><br><input type="submit" value="转账"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>transfer result</title>
</head>
<body>
<h1>The transfer failed because of insufficient balance</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>transfer result</title>
</head>
<body>
<h1>The transfer failed, error occurred</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>transfer result</title>
</head>
<body>
<h1>The transfer success</h1>
</body>
</html>

test:

table before test: 

 

 

 

 

mybatis 框架中的sqlSession.getMapper(接口类) 可以生成代理对象

//sqlsession.getMapper生成代理对象
private AccountDao accountDao = (AccountDao) SqlSessionUtil.openSession().getMapper(AccountDao.class);
package com.wsd.service.impl;import com.wsd.dao.AccountDao;
import com.wsd.exception.AppException;
import com.wsd.exception.MoneyNotEnoughException;
import com.wsd.pojo.Account;
import com.wsd.service.AccountService;
import com.wsd.util.ProxyUtil;
import com.wsd.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;/*** @author: Mr.Wang* @create: 2023-07-02 01:19**/
public class AccountServiceImpl implements AccountService {//sqlsession.getMapper生成代理对象private AccountDao accountDao = (AccountDao) SqlSessionUtil.openSession().getMapper(AccountDao.class);@Overridepublic void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException,AppException{//创建sql sessionSqlSession sqlSession =  SqlSessionUtil.openSession();// 查询转出账户的余额Account fromAct = accountDao.selectByActno(fromActno);//余额不足,抛出异常if (fromAct.getBalance() < money) {throw new MoneyNotEnoughException("对不起,您的余额不足。");}// 程序如果执行到这里说明余额充足// 修改账户余额Account toAct = accountDao.selectByActno(toActno);fromAct.setBalance(fromAct.getBalance() - money);toAct.setBalance(toAct.getBalance() + money);// 更新数据库int count = accountDao.update(fromAct);/*//模拟异常String s = null;s.toString();*/count += accountDao.update(toAct);if (count != 2) {throw new AppException("转账失败,未知原因!");}sqlSession.commit();SqlSessionUtil.close(sqlSession);}
}

test:

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/14101.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++ OJ练习】4.字符串中的第一个唯一字符

1.题目链接 力扣 2.解题思路 利用计数排序的思想 映射进行计数 最后计数为1的那个字符就是唯一字符 从前往后遍历 可以得到 第一个唯一字符 3.代码 class Solution { public:int firstUniqChar(string s) {//使用映射的方式统计次数 计数排序思想int count[26] { 0 };fo…

rpm包安装mysql8.0

一、环境准备 1查看本机IP地址&#xff0c;使用Xshell工具登录 [rootmysql ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope ho…

基于java+swing+mysql商城购物系统

基于javaswingmysql商城购物系统 一、系统介绍二、功能展示1.项目骨架2.主界面3.用户登陆4.添加商品类别5、添加商品6、商品管理 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java SE项目 项目名称&#xff1a;商城购物系统 用户类型&#xff1a;双…

SpringMVC跨域写入Cookie

前后端分离的项目&#xff0c;SpringMVCTomcat(SpringBoot)&#xff0c;前端Vueaxios。 不建议后端去写入Cookie&#xff0c;一般都是在前端写入Cookie&#xff0c;如果后端使用&#xff1a;CrossOrigin(origins "http://localhost", allowCredentials "true…

SpringBoot实战(二十)集成Druid连接池

目录 一、简介1.定义2.特点3.竞品对比 二、搭建测试项目1.Maven依赖2.yaml配置2.1 JDBC配置2.2 连接池配置2.3 监控配置 三、测试1.查看监控页面2.单元测试 四、补充&#xff1a;1.如何打印慢SQL&#xff1f;2.去除广告3.如何手动获取监控内容 一、简介 1.定义 Druid数据库连…

EtherCAT转TCP/IP网关ethercat最大通讯距离

天啊&#xff01;你们听说了吗&#xff1f;数据互联互通问题终于迎来了突破性进展&#xff01;作为生产管理系统的关键部分&#xff0c;数据互联互通一直是个大问题。然而&#xff0c;ETHERCAT和TCP/IP是两个不同的协议&#xff0c;它们之间的通讯一直是个大 问题。但是&#x…

【论文基本功】【LaTeX】参考文献中常见属性的用法及特点(bib文件)【IEEE论文】

【论文基本功】【LaTeX】参考文献中常见属性的用法及特点&#xff08;bib文件&#xff09;【IEEE论文】 一、author&#xff08;作者&#xff09;1. 使用方法用法1&#xff1a;作者名字的两种写法用法2&#xff1a;使用and连接不同作者姓名用法3&#xff1a;超过3个作者时如何使…

使用Jetpack Compose集成WebView

在Android开发中&#xff0c;WebView是一个非常重要的组件&#xff0c;它可以用来显示网页或加载在线内容。然而&#xff0c;在Jetpack Compose&#xff08;Google推出的新的UI工具包&#xff09;中&#xff0c;目前没有内置的WebView Composable。但不必担心&#xff0c;你可以…

量化风控算法详解之CatBoost

CatBoost是俄罗斯的搜索巨头Yandex在2017年开源的机器学习库&#xff0c;与XGBoost、LightGBM并称为GBDT三大主流神器库。LightGBM和XGBoost已经在各领域得到了广泛的应用&#xff0c;而Yandex的CatBoost作为后起之秀则是号称比XGBoost和LightGBM在算法准确率等方面表现更为优秀…

ESP32(掌控板) 陀螺仪显示与RGB灯

ESP32&#xff08;掌控板&#xff09; 陀螺仪显示与RGB灯 本程序图形化显示陀螺仪位置&#xff08;注意要换算&#xff09;&#xff0c;根据陀螺仪位置控制RGB灯颜色并有3种颜色组合和关灯可选&#xff0c;通过触摸按键调节亮度。 图形化程序如下 Arduino代码如下 /*!* MindP…

【记录】SMB|Windows下修改SMB端口并挂载

环境&#xff1a;Window11 使用背景&#xff1a;勒索病毒导致445端口不安全&#xff0c;故而该端口在服务器端被全面禁用了&#xff0c;如需使用SMB服务需要换个SMB服务端口。 方法1&#xff1a;端口转发 winx点开管理员权限的终端&#xff1a; 运行以下指令&#xff0c;检查…

数字电路设计——加法器

数字电路设计——加法器 半加器 半加器只有两个一位宽的输入 a a a 和 b b b &#xff0c;输出 a b ab ab 所产生的本位和 s u m sum sum 和进位 c o u t cout cout。组合逻辑为&#xff1a; S A ⊕ B , C o u t A B S A \oplus B,Cout AB SA⊕B,CoutAB 真值表和原…