Spring AOP(AOP+JDBC 模板 + 转账案例)

Spring 框架核心笔记(AOP+JDBC 模板 + 转账案例)

一、AOP 概念与入门案例

AOP(面向切面编程)是 Spring 核心特性,通过横向抽取机制分离业务逻辑与通用功能(如事务、日志),降低耦合度。

1. 入门案例环境准备

创建 Maven 项目,引入核心依赖:
<dependencies> <!-- Spring核心 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- 日志相关 --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <!-- 测试相关 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> <scope>test</scope> </dependency> <!-- 数据库相关 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies>

2. 核心业务代码

(1)实体类 Account

public class Account { private Integer id; private String name; private Double money; // getter、setter方法省略 }

(2)DAO 层接口与实现

// AccountDao接口 public interface AccountDao { void save(Account account) throws SQLException; } // AccountDaoImpl实现类 public class AccountDaoImpl implements AccountDao { @Override public void save(Account account) throws SQLException { Connection conn = TxUtils.getConnection(); String sql = "insert into account values (null,?,?)"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, account.getName()); stmt.setDouble(2, account.getMoney()); stmt.executeUpdate(); stmt.close(); } }

(3)Service 层接口与实现

// AccountService接口 public interface AccountService { void saveAll(Account account1, Account account2) throws SQLException; } // AccountServiceImpl实现类 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void saveAll(Account account1, Account account2) throws SQLException { accountDao.save(account1); // 模拟异常:int a = 1/0; accountDao.save(account2); } }

(4)事务工具类 TxUtils(需拷贝引入)

用于事务管理,包含开启事务、提交、回滚、关闭资源等方法。

(5)JDK 动态代理类

public class JdkProxy { public static Object getProxy(AccountService accountService) { return Proxy.newProxyInstance( JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { TxUtils.startTransaction(); result = method.invoke(accountService, args); TxUtils.commit(); } catch (Exception e) { e.printStackTrace(); TxUtils.rollback(); } finally { TxUtils.close(); } return result; } } ); } }

(6)测试类

public class Demo1 { @Test public void run1() throws SQLException { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_demo1.xml"); 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); } }

(7)Spring 配置文件 applicationContext_demo1.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountDao" class="com.qcbyjy.demo1.AccountDaoImpl"/> <bean id="accountService" class="com.qcbyjy.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> </beans>


二、AOP 核心概念与优势

1. 核心概念

  • 连接点(Joinpoint):被拦截的方法(Spring 仅支持方法级连接点)。
  • 切入点(Pointcut):定义需要拦截的连接点(即要增强的方法)。
  • 通知(Advice):拦截后执行的操作,分前置、后置、异常、最终、环绕通知。
  • 目标对象(Target):被代理的原始业务对象。
  • 织入(Weaving):将通知应用到目标对象生成代理的过程。
  • 代理(Proxy):织入增强后的结果对象。
  • 切面(Aspect):切入点与通知的组合。

2. 核心优势

  • 无需修改源代码,运行期动态增强。
  • 减少重复代码(如事务、日志)。
  • 提高开发效率,简化维护。

3. 底层原理

  • JDK 动态代理:基于接口生成代理类,要求目标对象实现接口。
  • CGLIB 代理:基于类生成代理子类,无需目标对象实现接口。

三、Spring AOP 配置文件方式

1. 依赖补充

需额外引入 AOP 相关依赖:
<!-- AOP联盟 --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- Spring Aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- AspectJ Weaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.3</version> </dependency>

2. 配置文件约束

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://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"> </beans>

3. 切入点表达式

格式:execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 修饰符可省略,返回值类型不可省略(可用*通配)。
  • 包名、类名、方法名可用*通配,多级包可用..省略。
  • 参数用*表示单个任意参数,..表示任意参数(个数 / 类型)。
  • 通用表达式:execution(* com.qcbyjy.*.*ServiceImpl.*(..))

4. 通知类型配置

(1)切面类

public class MyXmlAspect { // 前置通知 public void beforeLog() { System.out.println("前置通知:方法执行前增强"); } // 后置通知 public void afterReturningLog() { System.out.println("后置通知:方法执行成功后增强"); } // 异常通知 public void afterThrowingLog() { System.out.println("异常通知:方法执行失败后增强"); } // 最终通知 public void afterLog() { System.out.println("最终通知:方法执行完毕(成功/失败)后增强"); } // 环绕通知 public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕通知:方法执行前"); Object result = pjp.proceed(); // 执行目标方法 System.out.println("环绕通知:方法执行后"); return result; } }

(2)AOP 配置

<!-- 目标对象 --> <bean id="userService" class="com.qcbyjy.demo2.UserServiceImpl"/> <!-- 切面类 --> <bean id="myXmlAspect" class="com.qcbyjy.demo2.MyXmlAspect"/> <!-- AOP配置 --> <aop:config> <aop:aspect ref="myXmlAspect"> <!-- 切入点定义(可复用) --> <aop:pointcut id="pointcut" expression="execution(* com.qcbyjy.*.*ServiceImpl.*(..))"/> <!-- 各类通知 --> <aop:before method="beforeLog" pointcut-ref="pointcut"/> <aop:after-returning method="afterReturningLog" pointcut-ref="pointcut"/> <aop:after-throwing method="afterThrowingLog" pointcut-ref="pointcut"/> <aop:after method="afterLog" pointcut-ref="pointcut"/> <aop:around method="aroundLog" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>

(3)测试类

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_demo2.xml") public class Demo2 { @Autowired private UserService userService; @Test public void run1() { userService.save(); } }


四、Spring AOP 注解方式

1. 纯注解配置步骤

(1)切面类(含注解)

@Component // 交给IOC容器管理 @Aspect // 声明为切面类 public class MyAnnoAspect { // 前置通知 @Before("execution(* com.qcbyjy.demo3.OrderServiceImpl.save(..))") public void log() { System.out.println("注解方式:前置增强"); } }

(2)Spring 配置类

@Configuration @ComponentScan("com.qcbyjy.demo3") // 扫描包 @EnableAspectJAutoProxy // 开启AOP自动代理 public class SpringConfig { }

(3)通知类型注解
  • @Before:前置通知
  • @AfterReturning:后置通知
  • @AfterThrowing:异常通知
  • @After:最终通知
  • @Around:环绕通知(需手动执行pjp.proceed())

五、Spring JDBC 模板技术

1. 依赖补充

<!-- Spring JDBC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- Spring事务 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency>

2. 配置文件方式使用

(1)属性文件 jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///spring_db jdbc.username=root jdbc.password=root

(2)Spring 配置文件

<context:property-placeholder location="classpath:jdbc.properties"/> <!-- 数据源(Druid) --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="{jdbc.driverClassName}"/\> \{jdbc.username}"/\> \(3)增删改查操作

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_jdbc.xml") public class Demo1_1 { @Autowired private JdbcTemplate jdbcTemplate; // 新增 @Test public void add() { jdbcTemplate.update("insert into account values (null,?,?)", "熊二", 500); } // 修改 @Test public void update() { jdbcTemplate.update("update account set money=? where name=?", 800, "熊二"); } // 删除 @Test public void delete() { jdbcTemplate.update("delete from account where name=?", "熊二"); } // 查询单个 @Test public void findOne() { Account account = jdbcTemplate.queryForObject( "select * from account where name=?", new BeanMapper(), "熊大" ); System.out.println(account); } // 查询所有 @Test public void findAll() { List<Account> list = jdbcTemplate.query("select * from account", new BeanMapper()); list.forEach(System.out::println); } } // 结果集映射类 class BeanMapper implements RowMapper<Account> { @Override public Account mapRow(ResultSet rs, int rowNum) throws SQLException { Account account = new Account(); account.setId(rs.getInt("id")); account.setName(rs.getString("name")); account.setMoney(rs.getDouble("money")); return account; } }


六、模拟转账开发案例

1. 完整实现(DAO 层继承 JdbcDaoSupport)

(1)Service 层

// 接口 public interface AccountService { void pay(String out, String in, double money); } // 实现类 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void pay(String out, String in, double money) { accountDao.outMoney(out, money); // 付款 // 模拟异常:int a = 1/0; accountDao.inMoney(in, money); // 收款 } }

(2)DAO 层

// 接口 public interface AccountDao { void outMoney(String out, double money); void inMoney(String in, double money); } // 实现类(继承JdbcDaoSupport) public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void outMoney(String out, double money) { this.getJdbcTemplate().update("update account set money=money-? where name=?", money, out); } @Override public void inMoney(String in, double money) { this.getJdbcTemplate().update("update account set money=money+? where name=?", money, in); } }

(3)Spring 配置文件

<context:property-placeholder location="classpath:jdbc.properties"/> <!-- 数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="{jdbc.driverClassName}"/\> \{jdbc.username}"/\> \(4)测试类

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_dao2.xml") public class Demo3 { @Autowired private AccountService accountService; @Test public void testPay() { accountService.pay("熊大", "熊二", 100); } }

相关推荐
遇印记2 小时前
蓝桥java蜗牛
java·学习·蓝桥杯
m0_565611132 小时前
Java-泛型
java·windows
Python私教2 小时前
Rust基本语法
后端
张np2 小时前
java基础-集合接口(Collection)
java·开发语言
Python私教2 小时前
Rust环境搭建
后端
jakeswang2 小时前
ServletLess架构简介
java·后端·servletless
搬山境KL攻城狮2 小时前
maven 私服上传jar
java·maven·jar
夕颜1113 小时前
如何让 AI 按照你的预期输出
后端
q***56383 小时前
Spring Boot--@PathVariable、@RequestParam、@RequestBody
java·spring boot·后端