1. AOP优化转账案例
依然使用前面的转账案例,将两个代理工厂对象直接删除!改为spring的aop思想来实现。
1.2 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"
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
">
<!--开启注解扫描-->
<context:component-scan base-package="com.lagou"></context:component-scan>
<!--引入properties-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置DataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置queryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!--开启AOP的自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--AOP配置-->
<aop:config>
<!--1.切点表达式-->
<aop:pointcut id="myPointcut" expression="execution(* com.lagou.servlet.impl.AccountServiceImpl.*(..))"/>
<!--2.切面配置-->
<aop:aspect ref="transactionManager">
<aop:before method="beginTransaction" pointcut-ref="myPointcut"/>
<aop:after-returning method="commit" pointcut-ref="myPointcut"/>
<aop:after-throwing method="rollback" pointcut-ref="myPointcut"/>
<aop:after method="release" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
1.3 事务管理器的编写
package com.lagou.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.SQLException;
/*
事务管理器工具类:包含:开启事务、提交事务、回滚事务、释放资源
通知类
*/
@Component("transactionManager")
@Aspect //表明该类为切面类
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
@Around("execution(* com.lagou.servlet.impl.AccountServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws SQLException {
Object proceed = null;
try {
// 开启手动事务
connectionUtils.getThreadConnection().setAutoCommit(false);
// 切入点方法执行
proceed = pjp.proceed();
// 手动提交事务
connectionUtils.getThreadConnection().commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
// 手动回滚事务
connectionUtils.getThreadConnection().rollback();
} finally {
// 将手动事务恢复成自动事务
connectionUtils.getThreadConnection().setAutoCommit(true);
// 将连接归还到连接池
connectionUtils.getThreadConnection().close();
// 解除线程绑定
connectionUtils.removeThreadConnection();
}
return proceed;
}
/*
开启事务
*/
public void beginTransaction(){
// 获取connection对象
Connection connection = connectionUtils.getThreadConnection();
try {
// 开启了一个手动事务
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
提交事务
*/
public void commit(){
Connection connection = connectionUtils.getThreadConnection();
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
回滚事务
*/
public void rollback(){
Connection connection = connectionUtils.getThreadConnection();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
释放资源
*/
public void release(){
// 将手动事务改回成自动提交事务
Connection connection = connectionUtils.getThreadConnection();
try {
connection.setAutoCommit(true);
// 将连接归还到连接池
connectionUtils.getThreadConnection().close();
// 解除线程绑定
connectionUtils.removeThreadConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
1.3 数据库连接工具类
package com.lagou.utils;
/*
连接工具类:从数据源中获取一个连接,并且将获取到的连接与线程进行绑定
ThreadLocal : 线程内部的存储类,可以在指定的线程内存储数据 key:threadLocal(当前线程) value:任意类型的值 Connection
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class ConnectionUtils {
@Autowired
private DataSource dataSource;
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
/*
获取当前线程上绑定连接:如果获取到的连接为空,那么就要从数据源中获取连接,并且放到ThreadLocal中(绑定到当前线程)
*/
public Connection getThreadConnection(){
// 1. 先从ThreadLocal上获取连接
Connection connection = threadLocal.get();
// 2. 判断当前线程中是否是有Connection
if(connection == null){
// 3. 从数据源中获取一个连接,并且存入ThreadLocal中
try {
// 不为null
connection = dataSource.getConnection();
threadLocal.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
/*
解除当前线程的连接绑定
*/
public void removeThreadConnection(){
threadLocal.remove();
}
}
1.4 service的编写
package com.lagou.servlet.impl;
import com.lagou.dao.AccountDao;
import com.lagou.servlet.AccountService;
import com.lagou.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Autowired
private TransactionManager transactionManager;
/*
转账方法 切入点 添加上事务控制的效果
*/
public void transfer(String outUser, String inUser, Double money) {
//手动开启事务:调用事务管理器中的开启事务方法
//transactionManager.beginTransaction();
// 编写了事务相关代码
// 调用了减钱方法
try {
accountDao.out(outUser,money);
//int i= 1/0;
// 调用了加钱方法
accountDao.in(inUser,money);
//手动提交事务
//transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//手动回滚事务
//transactionManager.rollback();
} finally {
//资源释放
//transactionManager.release();
}
}
@Override
public void save() {
// 编写了事务相关代码
System.out.println("save方法");
}
@Override
public void update() {
System.out.println("update方法");
}
@Override
public void delete() {
System.out.println("delete方法");
}
}
1.5 测试类的编写
package com.lagou.test;
import com.lagou.proxy.CglibProxyFactory;
import com.lagou.proxy.JDKProxyFactory;
import com.lagou.servlet.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class AccountServiceImplTest {
@Autowired
private AccountService accountService;
@Autowired
private JDKProxyFactory proxyFactory;
@Test
public void testTransfer(){
accountService.transfer("tom","jerry",100d);
}
/*
测试JDK动态代理优化转账案例
*/
@Test
public void testTransferProxyJDK(){
// 当前返回的实际上是AccountService的代理对象proxy
AccountService accountSericeJDKProxy = proxyFactory.createAccountSericeJDKProxy();
// 代理对象proxy调用接口中的任意方法时,都会执行底层的invoke方法
//accountSericeJDKProxy.transfer("tom","jerry",100d);
accountSericeJDKProxy.save();
}
@Autowired
private CglibProxyFactory cglibProxyFactory;
/*
测试Cglib动态代理优化转账案例
*/
@Test
public void testTransferProxyCglib(){
// accountServiceCglibProxy: proxy
AccountService accountServiceCglibProxy = cglibProxyFactory.createAccountServiceCglibProxy();
accountServiceCglibProxy.transfer("tom","jerry",100d);
}
}
节选自拉钩教育JAVA系列课程