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}"/\> \ @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; } } // 接口 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); // 收款 } } // 接口 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); } } <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="{jdbc.driverClassName}"/\> \ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_dao2.xml") public class Demo3 { @Autowired private AccountService accountService; @Test public void testPay() { accountService.pay("熊大", "熊二", 100); } }
六、模拟转账开发案例
1. 完整实现(DAO 层继承 JdbcDaoSupport)
(1)Service 层
(2)DAO 层
(3)Spring 配置文件