Spring AOP事务管理实战:转账案例详解
1.AOP概念的引入示例场景事务处理的场景依旧是转账涉及到一个人给一个人转账失败的话就都失败成功的话就都成功不会出现一个失败一个成功的情况。这边我们在处理业务的时候在中间添加除零异常来模拟错误的出现如果不引入事务就会导致前边的语句执行后边的语句不执行导致只有一个账户的金额变动而另一个账户不变。再将事务打开就会执行到错误语句后自动回滚事务让两个语句都执行失败。1.1 创建maven项目引入坐标导入依赖properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.0.2.RELEASE/version /dependency dependency groupIdcommons-logging/groupId artifactIdcommons-logging/artifactId version1.2/version /dependency dependency groupIdlog4j/groupId artifactIdlog4j/artifactId version1.2.12/version /dependency !--有单元测试的环境Spring5 版本Junit4.12 版本-- dependency groupIdjunit/groupId artifactIdjunit/artifactId version4.12/version scopetest/scope /dependency !--连接池-- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.1.10/version /dependency !--mysql 驱动包-- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency !-- Spring 整合 Junit 测试的 jar 包 -- dependency groupIdorg.springframework/groupId artifactIdspring-test/artifactId version5.0.2.RELEASE/version scopetest/scope /dependency /dependencies1.2 编写实体类Accountpackage com.qcby.entity; public class Account { private Integer id; private String name; private Double money; GetterSetter方法 toString方法 }1.3 编写工具类这个工具类有许多方法比如创建数据库连接池连接数据库在数据库连接池中获取链接开启事务回滚事务提交事务关闭数据库连接不是真关闭是把连接还给连接池获取数据源package com.qcby.utils; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.alibaba.druid.pool.DruidDataSource; /** * 事务的工具类 */ public class TxUtils { private static DruidDataSource ds null; // 使用ThreadLocal存储当前线程中的Connection对象 private static ThreadLocalConnection threadLocal new ThreadLocalConnection(); // 在静态代码块中创建数据库连接池 static { try { // 通过代码创建C3P0数据库连接池 ds new DruidDataSource(); ds.setDriverClassName(com.mysql.jdbc.Driver); ds.setUrl(jdbc:mysql:///spring_db); ds.setUsername(root); ds.setPassword(2020); // 设置连接超时时间避免长时间卡住 ds.setInitialSize(5); ds.setMaxActive(10); ds.setMaxWait(5000); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /** * Method: getConnection * Description: 从数据源中获取数据库连接 * Anthor: * return Connection * throws SQLException */ public static Connection getConnection() throws SQLException { // 从当前线程中获取Connection Connection conn threadLocal.get(); if (conn null) { // 从数据源中获取数据库连接 conn getDataSource().getConnection(); // 将conn绑定到当前线程 threadLocal.set(conn); } return conn; } /** * Method: startTransaction * Description: 开启事务 * Anthor: * */ public static void startTransaction() { try { Connection conn threadLocal.get(); if (conn null) { conn getConnection(); // 把 conn绑定到当前线程上 threadLocal.set(conn); } // 开启事务 conn.setAutoCommit(false); } catch (Exception e) { throw new RuntimeException(e); } } /** * Method: rollback * Description:回滚事务 * Anthor: */ public static void rollback() { try { // 从当前线程中获取Connection Connection conn threadLocal.get(); if (conn ! null) { // 回滚事务 conn.rollback(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * Method: commit * Description:提交事务 * Anthor: */ public static void commit() { try { // 从当前线程中获取Connection Connection conn threadLocal.get(); if (conn ! null) { // 提交事务 conn.commit(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * Method: close * Description:关闭数据库连接(注意并不是真的关闭而是把连接还给数据库连接池) * Anthor: * */ public static void close() { try { // 从当前线程中获取Connection Connection conn threadLocal.get(); if (conn ! null) { conn.close(); // 解除当前线程上绑定conn threadLocal.remove(); } } catch (Exception e) { throw new RuntimeException(e); } } /** * Method: getDataSource * Description: 获取数据源 * Anthor: * return DataSource */ public static DataSource getDataSource() { // 从数据源中获取数据库连接 return ds; } }1.4 编写AccountDao接口和AccountDaoImpl类AccountDaopackage com.qcby.dao; import com.qcby.entity.Account; import java.sql.SQLException; public interface AccountDao { void save(Account account)throws SQLException; }AccountDaoImplpackage com.qcby.dao; import com.qcby.entity.Account; import com.qcby.utils.TxUtils; import org.springframework.beans.factory.annotation.Autowired; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class AccountDaoImpl implements AccountDao { Autowired private DataSource dataSource; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource dataSource; } /** * 保存 * param account */ Override public void save(Account account) throws SQLException { // System.out.println(持久层保存账户...); // 把数据存储到数据库中 // 先获取到连接 Connection conn TxUtils.getConnection(); // 编写 sql 语句 String sql insert into account values (null,?,?); // 预编译 SQL 语句 PreparedStatement stmt conn.prepareStatement(sql); // 设置值 stmt.setString(1,account.getName()); stmt.setDouble(2,account.getMoney()); // 执行操作 stmt.executeUpdate(); // 关闭资源 conn 不能关闭 stmt.close(); } }1.5 编写业务层AccountService接口和AccountServiceImpl实现类AccountServicepackage com.qcby.service; import com.qcby.entity.Account; import java.sql.SQLException; public interface AccountService { void saveAll(Account account1,Account account2)throws SQLException; }AccountServiceImplpackage com.qcby.service; import com.qcby.dao.AccountDao; import com.qcby.entity.Account; import com.qcby.utils.TxUtils; import java.sql.SQLException; public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao accountDao; } public void saveAll(Account account1, Account account2)throws SQLException{ try{ TxUtils.startTransaction(); // 保存 1 账号 accountDao.save(account1); // 模拟异常 // int a 1/0; // 保存 2 账号 accountDao.save(account2); TxUtils.commit(); System.out.println(保存成功); }catch (Exception e){ e.printStackTrace(); TxUtils.rollback(); System.out.println(保存失败); }finally { TxUtils.close(); System.out.println(关闭资源); } } // Override // public void saveAll(Account account1, Account account2) { // try { // // 开启事务 // TxUtils.startTransaction(); // // 保存 1 账号 // accountDao.save(account1); // // 模拟异常 // // int a 1/0; // // 保存 2 账号 // accountDao.save(account2); // // 提交事务/回滚事务 // TxUtils.commit(); // } catch (Exception e) { // // 打印异常信息 // e.printStackTrace(); // // 回滚事务 // TxUtils.rollback(); // } finally { // // 关闭资源 // TxUtils.close(); // } // } }1.6 编写applicationContext_demo1.xml?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd !-- 只配置 AccountDao不需要 dataSource -- bean idaccountDao classcom.qcby.dao.AccountDaoImpl/ !-- 配置 AccountService -- bean idaccountService classcom.qcby.service.AccountServiceImpl property nameaccountDao refaccountDao/ /bean /beans1.7 编写测试类Demo1package com.qcby.test; import com.qcby.entity.Account; import com.qcby.proxy.JdkProxy; import com.qcby.service.AccountService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.sql.SQLException; public class Demo1 { Test public void run1() throws SQLException { ApplicationContext ac new ClassPathXmlApplicationContext(applicationContext_demo1.xml); // 获取 service 对象 AccountService accountService (AccountService)ac.getBean(accountService); Account account1 new Account(); account1.setName(熊大); account1.setMoney(1000d); Account account2 new Account(); account2.setName(熊二); account2.setMoney(1000d); // 生成代理对象 Object proxyobj JdkProxy.getProxy(accountService); // 强转 AccountService proxy (AccountService) proxyobj; // 调用代理对象方法 proxy.saveAll(account1,account2); } }1.8 展示两种测试结果没有除零异常有除零异常2.AOP相关概念2.1 AOP概述AOPAspect Oriented Programming面向切面编程·是一种编程范式指导开发者如何组织程序结构·是由AOP联盟组织提出制定了一套规范后来Spring把AOP的思想引入到了框架·通过预编译方式或者运行期动态代理实现程序功能的统一维护的一种技术·是OOP的延续是软件开发中的一个热点是Spring框架中的一个重要内容是函数式编程的一种衍生范型·利用AOP对业务逻辑进行各个部分的隔离使得各个业务部分的耦合度降低提高程序可重用性提高开发效率。·采取横向抽取机制取代了传统纵向继承体系重复性代码性能监视、事务管理、安全检查、缓存AOP可以在不修改源代码的前提下对程序进行增强2.2 AOP的优势因为我们会在运行期间在不修改源代码的前提下对已有方法进行增强·减少代码复用·提供开发效率·维护方便2.3 AOP的底层原理2.3.1 动态代理技术JDK的动态代理技术①为接口创建代理类的字节码文件②使用ClassLoader将字节码文件加载到JVM③创建代理类实例对象执行对象的目标方法CGLIBCode Generation Library是一个强大的Java字节码生成库用于实现动态代理。它通过继承目标类并在运行时动态生成子类方式对非final方法进行拦截与增强是SpringAOP等框架的底层基石。3.AOP相关术语·JoinPoint连接点——指的是被拦截到的点。在Spring这些点指的是方法因为Spring只支持方法类型的连接点·PointCut切入点——指的是我们要对哪些JoinPoint进行拦截的定义·Advice通知/增强——指的是拦截到JoinPoint之后所要做的事情分为·前置通知·后置通知·异常通知·最终通知·环绕通知·Target目标对象——代理目标对象·Weaving织入——把增强应用的目标对象来创建新的代理对象的过程·Proxy代理——一个类被AOP织入增强后产生一个结果代理类·Aspect切面——是切入点和通知的组合以后自己来编写和配置的4.SpringAOP入门案例完成账户的创建案例用户名和账户余额4.1 纯配置文件的方法4.1.1 首先要用AOP的东西我们先要导入SpringAOP的依赖jar包dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.0.2.RELEASE/version /dependency dependency groupIdcommons-logging/groupId artifactIdcommons-logging/artifactId version1.2/version /dependency dependency groupIdlog4j/groupId artifactIdlog4j/artifactId version1.2.12/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-test/artifactId version5.0.2.RELEASE/version /dependency dependency groupIdjunit/groupId artifactIdjunit/artifactId version4.12/version /dependency !-- AOP 联盟 -- dependency groupIdaopalliance/groupId artifactIdaopalliance/artifactId version1.0/version /dependency !-- Spring Aspects -- dependency groupIdorg.springframework/groupId artifactIdspring-aspects/artifactId version5.0.2.RELEASE/version /dependency !-- aspectj -- dependency groupIdorg.aspectj/groupId artifactIdaspectjweaver/artifactId version1.8.3/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.23/version /dependency !-- ... existing code ... -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency /dependencies4.1.2 导完依赖jar包之后编写account实体类public class Account { private Integer id; private String name; private Double money; } //GetterSetter方法4.1.3 创建AccountDao接口里创建save方法public class AccountDaoImpl implements AccountDao { /** * 保存 * param account */ Override public void save(Account account) throws SQLException { System.out.println(持久层保存账户...); Connection conn TxUtils.getConnection(); String sql insert into account(name, money) values (?,?); PreparedStatement stmt conn.prepareStatement(sql); stmt.setString(1,account.getName()); stmt.setDouble(2,account.getMoney()); stmt.executeUpdate(); } }4.1.4 导入我们的TxUtils工具类见 1. AOP概念引入 4.1.5 创建业务层的AccountService接口编写saveAll方法public interface AccountService { void saveAll(Account account1,Account account2)throws SQLException; }4.1.6 在AccountServiceImpl实现类里实现该接口的方法public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao accountDao; } public void saveAll(Account account1, Account account2)throws SQLException{ // 保存 1 账号 accountDao.save(account1); // 模拟异常 int a 1/0; // 保存 2 账号 accountDao.save(account2); }4.1.7 编写MyXmlAspect类里边有我们自己写的各种通知public class MyXmlAspect { // public void before(){ // System.out.println(----------------------); // TxUtils.startTransaction(); // System.out.println(前置通知...); // System.out.println(----------------------); // } // public void afterReturning(){ // System.out.println(后置通知...); // TxUtils.commit(); // } // public void afterThrowing(){ // System.out.println(异常通知...); // TxUtils.rollback(); // } // public void after(){ // System.out.println(最终通知...); // } public Object around(ProceedingJoinPoint pjp) throws Throwable { try { System.out.println(----------------------); System.out.println(前置通知...); TxUtils.startTransaction(); System.out.println(----------------------); Object result pjp.proceed(); System.out.println(返回通知...); TxUtils.commit(); return result; } catch (Throwable e) { System.out.println(异常通知...); TxUtils.rollback(); throw e; } finally { System.out.println(后置通知...); TxUtils.close(); } } }4.1.8 编写log4j配置文件4.1.9 编写applicationContext.xml文件?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocation http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd !-- 只配置 AccountDao不需要 dataSource -- bean idaccountDao classcom.qcby.dao.AccountDaoImpl/ !-- 配置 AccountService -- bean idaccountService classcom.qcby.service.AccountServiceImpl property nameaccountDao refaccountDao/ /bean bean idmyXmlAspect classcom.qcby.MyXmlAspect/ !--配置AOP的增强-- aop:config !--配置切面切入点通知-- aop:aspect refmyXmlAspect !--配置切入点的idexpression后写自己要增强的方法-- aop:pointcut idpt expressionexecution(* com.qcby.service.AccountServiceImpl.save*(..))/ !-- 前置通知UserServiceImpl的save方法执行前增强-- aop:before methodbefore pointcut-refpt/ !-- 返回通知UserServiceImpl的save方法执行返回增强-- aop:after-returning methodafterReturning pointcut-refpt/ !-- 异常通知UserServiceImpl的save方法执行异常增强-- aop:after-throwing methodafterThrowing pointcut-refpt/ !--最终通知UserServiceImpl的save方法执行最后增强-- aop:after methodafter pointcut-refpt/ !--环绕通知UserServiceImpl的save方法执行环绕增强-- !-- aop:around methodaround pointcut-refpt/-- /aop:aspect /aop:config /beans注意我们在xml里配置的通知顺序会被严格执行所以一定要注意好顺序4.1.10 编写测试类来测试程序RunWith(SpringJUnit4ClassRunner.class) ContextConfiguration(locations classpath:applicationContext_demo1.xml) public class Demo2 { Autowired private AccountService accountService; Test public void run2() throws SQLException { Account account1 new Account(); account1.setName(小黑); account1.setMoney(1000d); Account account2 new Account(); account2.setName(小白); account2.setMoney(1000d); accountService.saveAll(account1,account2); } }①先测试一下around环绕通知A.有异常可以看见各种通知的执行位置出现了异常之后数据会回滚数据库里数据无变化B.无异常相应的数据库也会出现我们写入的数据②测试其他的通知方法A.有异常数据库数据无变化B.无异常数据库添加了新数据4.2 半注解方式相比于全配置文件的方式半注解的方式的配置文件只做两件事·开启事务·扫描注解?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:aophttp://www.springframework.org/schema/aop xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd !-- 开启AOP注解支持 -- aop:aspectj-autoproxy/ context:component-scan base-packagecom.qcby/ /beans配置各种通知依旧在MyXmlAspect类里但是不在xml配置文件里声明那个是切入点哪个是具体通知了而是在MyXmlAspect类里直接用注解声明/** * MyXmlAspect 自定义切面类切入点表达式通知增强的代码 */ import com.qcby.demo2.util.TxUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import com.qcby.demo2.service.AccountServiceImpl; /** * MyXmlAspect 自定义切面类切入点表达式通知增强的代码 */ Aspect Component public class MyXmlAspect { // 定义公共切入点后续通知直接引用 // Pointcut(execution(* com.qcby.demo2.AccountServiceImpl.save*())) // public void pointCut(){ // } Before(execution(* com.qcby.demo2.service.AccountServiceImpl.save*(..))) public void before(){ System.out.println(----------------------); TxUtils.startTransaction(); System.out.println(开启事务...); System.out.println(----------------------); } AfterReturning(execution(* com.qcby.demo2.service.AccountServiceImpl.save*(..))) public void afterReturning(){ System.out.println(提交事务...); TxUtils.commit(); } AfterThrowing(execution(* com.qcby.demo2.service.AccountServiceImpl.save*(..))) public void afterThrowing(){ System.out.println(异常回滚...); TxUtils.rollback(); } After(execution(* com.qcby.demo2.service.AccountServiceImpl.save*(..))) public void after(){ System.out.println(关闭连接...); } // Around(execution(* com.qcby.demo2.service.AccountServiceImpl.save*(..))) // public Object around(ProceedingJoinPoint pjp) throws Throwable { // try { // System.out.println(----------------------); // System.out.println(前置通知...); // TxUtils.startTransaction(); // System.out.println(----------------------); // // Object result pjp.proceed(); // // System.out.println(返回通知...); // TxUtils.commit(); // return result; // } catch (Throwable e) { // System.out.println(异常通知...); // TxUtils.rollback(); // throw e; // } finally { // System.out.println(后置通知...); // TxUtils.close(); // } // } }详见代码·我们需要把MyXmlAspect交给Spring框架来管理固加上Component·我们要声明这是一个切面类固然加上Aspect其他的类都和全配置文件的格式一样只不过相应的类要交给框架管理加上相应的注解部分类的创建也要交给框架4.3 纯注解的方式纯注解模式把配置文件改成了配置类Configuration ComponentScan(com.qcby.demo3) EnableAspectJAutoProxy public class SpringConfig { }需要加上·Configuration----标记是Spring的配置类是配置不是普通的业务类·ComponentScan(“需要扫描的包”)---------让Spring去扫描包下指定的·ControllerServiceRepositoryComponentAspect·EnableAspectJAutoProxy——打开SpringAOP的自动代理告诉Spring我要用切面编程帮我生成代理对象其他的类和半注解里的几乎一致注意加好注解在MyXmlAspect类里可以有更简单的方式不用在每个通知方法写入切入点//定义公共切入点后续通知直接引用 Pointcut(execution(* com.qcby.demo3.service.AccountServiceImpl.save*(..))) public void pointCut(){ }可以用Pointcut自己写一个切入点对应好一个方法然后下边可以直接调用比如直接Before(“pointCut()”)Before(pointCut()) public void before(){ System.out.println(----------------------); TxUtils.startTransaction(); System.out.println(前置通知...); System.out.println(----------------------); }

相关新闻

Slint 商业软件许可(Slint Software License v3.0.5)全解:授权范围、合规条件与付费方案

Slint 商业软件许可(Slint Software License v3.0.5)全解:授权范围、合规条件与付费方案

前端UI组件桌面应用嵌入式移动开发跨平台 【免费下载链接】slint Slint is an open-source declarative GUI toolkit to build native user interfaces for Rust, C, JavaScript, or Python apps. 项目地址: https://gitcode.com/GitHub_Trending/sl/slint 点击查看…

2026/10/10 1:58:48 阅读更多 →
小鸭量化,一个免费开源的量化交易工具

小鸭量化,一个免费开源的量化交易工具

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:58:48 阅读更多 →
JumpServer跨网段访问内网服务器——跳板机配置方案

JumpServer跨网段访问内网服务器——跳板机配置方案

前言:在运维场景中,堡垒机与内网服务器不在同一网段是高频痛点,直接访问会因网络隔离失败。借助JumpServer的“跳板机”功能,可实现跨网段访问,本文分享经优化的两种通用配置方案。 场景说明 假设环境: JumpServer堡垒…

2026/10/10 1:58:48 阅读更多 →

最新新闻

gPROMS二次开发教程(09):自定义单元操作——把设备封装成可被流程调用的黑箱/白箱

gPROMS二次开发教程(09):自定义单元操作——把设备封装成可被流程调用的黑箱/白箱

gPROMS二次开发教程(09):自定义单元操作——把设备封装成可被流程调用的黑箱/白箱版本声明块 工具/软件:gPROMS 桌面建模环境 gPROMS ModelBuilder;检索期官方发布锚点 gPROMS Process 2022.1.0,适用版本以…

2026/10/10 2:44:03 阅读更多 →
gPROMS二次开发教程(13):构建自定义模型库——从单文件模型到可维护的库工程

gPROMS二次开发教程(13):构建自定义模型库——从单文件模型到可维护的库工程

gPROMS二次开发教程(13):构建自定义模型库——从单文件模型到可维护的库工程版本声明块 工具/软件:gPROMS 桌面建模环境 gPROMS ModelBuilder;检索期官方发布锚点 gPROMS Process 2022.1.0,适用版本以官方发…

2026/10/10 2:44:03 阅读更多 →
gPROMS二次开发教程(15):优化与最优控制——动态优化、求解器选项与结果解读

gPROMS二次开发教程(15):优化与最优控制——动态优化、求解器选项与结果解读

gPROMS二次开发教程(15):优化与最优控制——动态优化、求解器选项与结果解读版本声明块 工具/软件:gPROMS 桌面建模环境 gPROMS ModelBuilder;检索期官方发布锚点 gPROMS Process 2022.1.0,适用版本以官方发…

2026/10/10 2:44:03 阅读更多 →
gPROMS二次开发教程(07):物性与热力学——把真物性接进你的模型

gPROMS二次开发教程(07):物性与热力学——把真物性接进你的模型

gPROMS二次开发教程(07):物性与热力学——把真物性接进你的模型版本声明块 工具/软件:gPROMS 桌面建模环境 gPROMS ModelBuilder(gPROMS ModelBuilder 为桌面建模工作台);检索期官方发布锚点 gP…

2026/10/10 2:44:03 阅读更多 →
金属表面RFID标签天线设计:解决电机/不锈钢/铝合金贴标失联问题

金属表面RFID标签天线设计:解决电机/不锈钢/铝合金贴标失联问题

简介:本资源是一篇面向射频识别(RFID)工程技术人员与物联网硬件开发者的技术应用型论文,聚焦金属表面超高频RFID标签天线的设计难题——传统标签在金属上因电磁屏蔽导致读取失效。文章提出一种基于印刷结构的抗金属PIFA型天线方案…

2026/10/10 2:44:03 阅读更多 →
ZoneMinder 仓库内 jwt-cpp 的 Conan 示例:从构建、运行到源码级解析

ZoneMinder 仓库内 jwt-cpp 的 Conan 示例:从构建、运行到源码级解析

音视频后端计算机视觉 【免费下载链接】zoneminder ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras. 项目地址: https://gitcode.com/gh_mirrors/zo/zonemind…

2026/10/10 2:43:03 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →