AOP的推导
假如有一个实现转账的案例
- 持久层实现
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner queryRunner;
@Autowired
private TxManager txManager;
@Override
public Account findByName(String name) {
try {
return queryRunner.query(txManager.getConnection(), "select * from account where name = ?",
new BeanHandler<Account>(Account.class), name);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
try {
System.out.println(txManager.getConnection());
queryRunner.update(txManager.getConnection(), "update account set balance = ? where name = ?",
account.getBalance(), account.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 业务层实现
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private TxManager txManager;
@Override
public void transfer(String sourceAccountName, String targetAccountName, Float amount) {
try {
//关闭自动提交
txManager.begin();
//查询
Account sourceAccount = accountDao.findByName(sourceAccountName);
Account targetAccount = accountDao.findByName(targetAccountName);
//更改
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
//更新
accountDao.update(sourceAccount);
int i = 1 / 0;
accountDao.update(targetAccount);
//手动提交
txManager.commit();
} catch (Exception e) {
//事务回滚
txManager.rollback();
} finally {
//事务关闭
txManager.close();
}
}
}
- 事务管理类的实现
//管理事务
@Component
public class TxManager {
private ThreadLocal<Connection> th = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
//获取Connnection
public Connection getConnection() throws SQLException {
Connection connection = th.get();
if (connection == null) {
connection = dataSource.getConnection();
th.set(connection);
}
return connection;
}
//开启
public void begin() {
try {
getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交
public void commit() {
try {
getConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚
public void rollback() {
try {
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭
public void close() {
try {
getConnection().close();
th.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 配置文件
<!--注解扫描-->
<context:component-scan base-package="com.itheima"/>
<!--dataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_109"/>
<property name="username" value="root"/>
<property name="password" value="adminadmin"/>
</bean>
<!--queryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
</beans>
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Test
public void testTransfer() {
accountService.transfer("B01", "B02", 1f);
}
}
以上代码存在的问题
现在事务代码和业务代码严重耦合
我们希望在不改变源代码的基础上
给业务代码添加事务管理的功能
解决问题的方法
使用动态代理
- 修改的业务层的代码
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String sourceAccountName, String targetAccountName, Float amount) {
//查询
Account sourceAccount = accountDao.findByName(sourceAccountName);
Account targetAccount = accountDao.findByName(targetAccountName);
//更改
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
//更新
accountDao.update(sourceAccount);
int i = 1 / 0;
accountDao.update(targetAccount);
}
}
- 使用动态代理产生一个代理对象
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
@Autowired
private AccountService accountService;
@Autowired
private TxManager txManager;
@Test
public void testTransfer() {
//代理逻辑
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try {
//关闭自动提交
txManager.begin();
obj = method.invoke(accountService, args);
//手动提交
txManager.commit();
} catch (Exception e) {
//事务回滚
txManager.rollback();
} finally {
//事务关闭
txManager.close();
}
return obj;
}
};
//产生代理对象
AccountService instance = (AccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
invocationHandler
);
//转账
instance.transfer("B01", "B02", 1f);
}
}
动态代理的方式分为两种
- jdk动态代理
- cglib动态代理
两种种代理的选择
首先创建代理实现时,jdk的速度要高于cglib,所以选择的时候:
1. 当被代理类有接口的时候,选择jdk动态代理,因为他的效率高
2. 当被代理类没有接口的时候,选择cglib动态代理,因为没有办法
总结
- 当核心业务(转账)和增强业务(事务)同时出现的时候,我们在开发的时候可以分开开发,运行时再进行组装(使用动态代理)
- 好处:
- 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
- 代码复用性高,增强代码不用重复书写
这就是一种AOP的思想
开发时分开开发 运行时组装运行