首先需要创建 maven java 项目,引入开发的坐标
<dependencies> <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>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies>public class Account { private int id; private String name; private double money; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }/** * 事务的工具类 */ public class PxUtils { private static DruidDataSource ds = null; // 使用ThreadLocal存储当前线程中的Connection对象 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); // 在静态代码块中创建数据库连接池 static { try { // 通过代码创建C3P0数据库连接池 ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql:///ssm"); ds.setUsername("root"); ds.setPassword("root"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /** * @Method: getConnection * @Description: 从数据源中获取数据库连接 */ 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: 开启事务 */ 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:回滚事务 */ 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:提交事务 */ 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:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池) */ 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: 获取数据源 */ public static DataSource getDataSource() { // 从数据源中获取数据库连接 return ds; } }public interface AccountDao_px { void save(Account account) throws SQLException; }public class AccountDaoPxImpl implements AccountDao_px { /** * 保存 * @param account */ public void save(Account account) throws SQLException { System.out.println("持久层:保存账户..."); // 把数据存储到数据库中 // 先获取到连接 Connection conn = PxUtils.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(); } }/** * 业务层接口 */ public interface AccountService_px { //保存两个账户 public void savaAll(Account account1, Account account2) throws SQLException; }/** * 业务层实现类 */ public class AccountServicePxImpl implements AccountService_px { //DI依赖注入 private AccountDao_px accountDao_px; public void setAccountDao(AccountDao_px accountDao_px) { this.accountDao_px = accountDao_px; } public void savaAll(Account account1, Account account2) throws SQLException { //保存账号1 accountDao_px.save(account1); //显示除零错误 //int a=1/0; //保存账号2 accountDao_px.save(account2); System.out.println("保存成功"); } }/** * 生成代理对象 * 传入目标对象,生成该对象的代理对象,返回,在对目标对象的方法进行增强 */ public class JdkPoxy { /** * 获取代理对象的方法 返回代理对象 对目标对象的方法进行增强 */ public static Object getPoxy(final AccountService_px accountServicePx){ /** * 使用JDK动态代理生成代理对象 *第一个参数:类的加载器 *第二个参数:当前传入的对象实现了哪些接口要字节码对象 *第三个参数:回调函数 */ Object proxy = Proxy.newProxyInstance(JdkPoxy.class.getClassLoader(), accountServicePx.getClass().getInterfaces(), new InvocationHandler() { /** * 调用代理对象的方法invoke方法就会执行 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //System.out.println("你调用了代理对象的invoke方法了。。。。。。。"); //对目标对象的方法进行增强 Object result = null; try { //开启事务 PxUtils.startTransaction(); result = method.invoke(accountServicePx, args); //提交事务 PxUtils.commit(); } catch (Exception e) { e.printStackTrace(); //回滚事务 PxUtils.rollback(); }finally { PxUtils.close(); } return result; } }); return proxy; } }<?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="accountDao_px" class="com.qcby.dao.impl.AccountDaoPxImpl"/> <bean id="accountServicePx" class="com.qcby.service.impl.AccountServicePxImpl"> <property name="accountDao" ref="accountDao_px"/> </bean> </beans>public class Text_px { @Test public void run1() throws SQLException { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_px.xml"); // 获取 service 对象 AccountService_px accountService_px = (AccountService_px) ac.getBean("accountServicePx"); Account account1 = new Account(); account1.setName("熊大"); account1.setMoney(1000d); Account account2 = new Account(); account2.setName("美羊羊"); account2.setMoney(1000d); // 生成代理对象 Object proxyobj = JdkPoxy.getPoxy(accountService_px); // 强转 AccountService_px proxy = (AccountService_px) proxyobj; // 调用代理对象方法 proxy.savaAll(account1,account2); } }总结
- JDK 动态代理是实现手段:代码中JdkPoxy类通过Proxy.newProxyInstance()方法,基于AccountService_px接口生成代理对象,这是 JDK 动态代理的典型实现,依赖接口,通过反射生成代理类。
- 与 AOP 核心概念的精准对应:代码将事务管理这一横切关注点从业务代码中分离出来,代理对象的invoke()方法中,通过PxUtils实现了事务的开启、提交、回滚、关闭等操作(这相当于AOP中的通知 Advice);被代理的AccountService_px的saveAll()方法是核心业务逻辑(相当于AOP中的连接点 Joinpoint);最终通过代理机制,在不修改业务代码的前提下,将事务管理逻辑织入到业务方法的执行流程中。AOP 的本质是将横切关注点与业务逻辑分离,并通过特定方式织入业务流程,这段代码完全符合这一思想。