spring learning code 3
This is my learning note about how Spring core features like IoC, AOP works, with Code Examples .
- spring Transaction(ACID
- spring5 新功能
- Spring V1.0 DispatcherServlet
- Spring IoC, DI, MVC 的工作原理
- Spring I
- from Servlet to ApplicatoinContext
- here is a case
spring Transaction(ACID)
- Atomicity 原子性(
- Consistency 一致性
- Isolateion 隔离性
- Durability 持久性
转账环境
经典场景: 银行转账
-
lucy 转账 100 给 mary
-
lucy 少 100, mary 多 100
-
Dao: 数据库操作,不写业务
- 多钱方法
- 少钱方法
-
Service: 业务操作
- 创建转账方法
- 调用 dao 两个方法
-
project step:
-
- 创建数据库表, 添加记录
-
- 创建 Service, 搭建 dao, 完成对象创建和注入关系
- service 注入 dao, 在 dao 注入 jdbcTemplate, 在 JdbcTemplate 注入 DataSource
service
@Service
public class UserService {
@Autowired
private UserDao userDao;
}
UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
bean.xml
<!-- 引入外部属性文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--4.1 开启组件扫描-->
<context:component-scan base-package="com.learn"/>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="driverClassName" value="${prop.driverClass}"/>
</bean>
<!--JdbcTemplate 对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--set方法注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
-
- 在 dao 创建两个方法,多钱少钱, 在 service 创建方法 (转账的方法)
-
- 发生错误, 比如转账到一半断电,产生 lucy 钱少了, mary 没到账
事务解决问题
-
- 开启事务: 业务层(javaEE 三层结构)
-
- Sping 进行事务管理操作
- 2.1 编程式管理: 一般开发中不用
-
2.2 声明式事务管理 (开发中使用)
- 注解方式 (开发中)
- xml 配置方式
-
3.spring 进行声明式事务管理,底层使用 AOP 原理
-
4.spring 事务管理 API
-
4.1 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实体类
PlatformTransactionManager -> DataSourceTransactionManager
-
4.2 开启事务注解
-
5.service 类上面 (获取 service 类里面方法上面) 添加事务注解
-
5.1 @Transactional 可以加到类或者某个方法上面
事务操作 (声明式事务管理参数配置)
-
- 事务方法 : 对数据库数据进行变化的操作
-
- service 类上面添加注解 @Transactional
-
- propagation : 事务传播行为:有事务的方法调用没事务的方法,或者反过来 或者两个方法都有事务 就是事务传播行为
@Transactional(propogation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void add() {
// call update method
update();
}
public void update() {
}
-
- isolation: 事务隔离级别 : 多事务操作直接不会产生影响,不考虑隔离性产生很多问题 解决三个读问题: 脏读,不可重复读,虚读
-
3.1 脏读: 一个未提交事务读取到另一个未提交事务的数据
-
3.2 不可重复读: 一个未提交事务读取到另一个提交事务修改数据
-
3.3 虚读: 一个未提交事务读取到另一个提交事务添加数据
-
3.4 READ UNCOMMITTED(读未提交) :无法解决 3 问题
-
3.5 READ COMMITTED(读已提交) :解决 脏读问题
-
3.6 REPEATABLE READ(可重复度) :解决 脏读 不可重复读 问题
-
3.7 SERIALIZABLE(串行化) :解决脏读 不可重复读 虚读问题
-
- timeout: 超时
-
- readOnly : 是否只读
-
- rollbackFor : 回滚
-
- norollbackFor
事务操作 (完全注解声明式事务管理)
-
- 创建配置类, 使用配置类替代 xml 配置文件
spring5 新功能
自带通用的日志封装
-
- 整合 Log4j2
-
- 引入 jar 包
-
2.1 log4j-api
-
2.2 log4j-core
-
2.3 log4j-slf4j-impl
-
2.4 slf4j-api
-
- 创建 log4j2.xml 配置
JUnit5
-
- test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
-
- 整合 JUnit5
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
+ 2. JUnit5 复合注解
```java
@SpringJunitConfig(lacation="classpath:bean1.xml")
// ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
}
}
Webflux
-
- 响应式编程: Reacter 实现
-
1.1 Reacter 两个核心类, Mono 和 Flux, 实现接口 Publisher。 Flux 对象实现发布者,返回 N 个元素; Mono 实现发布者,返回 0 或者 1 个元素
-
1.2 Mono 和 Flux 都是数据流的发布者,都可以发出三种数据信号: 元素值, 错误信号, 完成信号, 错误信号, 完成信号 都代表终止信号
-
1.3 引入依赖: Reactor-core
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.13</version>
</dependency>
-
1.3 调用 just 或 者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流
-
1.4 操作符: map 元素映射为新元素; flatMap 元素映射为流
-
- SpringWebflux 执行流程和核心 api SpringWebflux 基于 Reactor, 默认容器是 Netty, Netty 是高性能 NIO 框架,异步非阻塞的框架
-
2.1 Netty 阻塞状态 Socket -》 InputStream -》 read() -》 Thread
非阻塞 NIO 状态
-
2.2 SpringWebflux 核心控制器 DispatchHandler, 实现接口 WebHandler, 负责请求的处理
- HanddlerMapping : 请求查询处理的方法
- HandlerAdapter: 真正负责请求处理
- HandlerResultHandler: 响应结果处理
-
2.3 函数式编程,两个接口: RouterFunction 和 HandlerFunction
-
- SpringWebflux (基于注解)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.4.1</version>
</dependency>
- 3.1 配置启动端口号
resources -> application.properties -> server.port = 8081
- 3.2 创建包和相关类
-
- SpringWebflux (基于函数)
Spring V1.0 DispatcherServlet
Spring IoC, DI, MVC 的工作原理
遵循单一职责原则
MIN 版本 Spring 基本实现思路
- 配置阶段
- 配置 web.xml | DispatcherServlet
- 设定 init-param | contextConfigLocation = classpath:application.xml
- 设定 url-pattern | /*
- 配置 Annotation | @Controller @Service @Autowrited @RequestMapping
- 初始化阶段
- 调用init()方法 | 加载配置文件
- IOC容器初始化 | *Map<String, Object>
- 扫描相关的类 | scan-package=“com.gupaoedu” IOC + 创建实例化并保存至容器 | 通过反射机制将类实例化放入IOC容器中 DI + 进行DI操作 | 扫描IOC容器中的实例,给没有赋值的属性自动赋值 MVC + 初始化HandlerMapping | 将一个URL和一个Method进行一对一的关联映射Map<String, Method>
-
运行阶段
- 调用doPost()/doGet() | web容器调用doPost/doGet方法,获得request/response对象
- 匹配HandlerMapping | 从request对象中获得用户输入的url,找到对应的Method
- 反射调用method.invoker() | 利用反射调用方法并返回结果
- response.getWrite().write() | 将返回结果输出到游览器
新建一个类,实现三个方法
代码折叠
public class GPDispatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
<!-- 6.根据URL委派给具体的调用的方法 -->
doDispatch();
}
@Override
protected void init(ServletConfig config) throws ServletException {
<!-- 1.初始化方法init -->
<!--1. 加载配置文件 -->
doLoadConfig();
<!-- 2.扫描相关的类 -->
doScanner();
<!-- Ioc功能 -->
<!-- 3.初始化Ioc容器,将扫描的类进行实例化,缓存到Ioc容器中 -->
doInstance();
<!-- DI功能 -->
<!--4.完成依赖注入 -->
doAutowired();
<!-- MVC功能 -->
<!-- 5.初始化HandlerMapping -->
doInitHandlerMapping();
System.out.printLn("GP sTRING Framework is init.")
}
//根据contextConfigLocation的classpath找到对应的配置文件
private void doLoadConfig(String contextConfigLocatoin)
InputString is = this.getClass.
}
Spring
工厂模式,原型,单例(工厂怎么把对象创建出来,交给用户)
from Servlet to ApplicatoinContext
1. Spring IOC
without IOC
when we need a new Impl, problem occurs
use IOC, we save lots of time for adjusting code
here is a case
Transfer Funds to Other Bank’s Account: A sent money to B account
without IOC
table
name varcher 255 username
money int 255 balance
cardNo varcher 255 bank account
call flow
Transfer page
// page transfer
botton, send ajax requirement to TransferServlet
⇕ ⇕
TransferServlet
// servlet instance is initialized, invokes its Service layer method: TransferService transferService = newTransferServiceImpl();
⇕ ⇕
TransferService
// service class create Dao instance as singleton, invokes its Dao layer method: AccountDao accountDao = new JdbcAccountDaoImpl();
⇕ ⇕
AccountDao
// Dao provides multiple Impl
↓………………………………… ↓
JdbcAccountDaoImpl
MybatisAccountDaoImpl
core code
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
private TransferService transferService = new TransferServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOE {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOE {
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// invoke service method
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
// response
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
}
public interface TransferService {
void transfer(String fromCardNo, String toCardNo, int money) throws Exception;
}
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
accountDao.updateAccountByCardNo(from);
}
}
public interface AccountDao {
/**
* get account by query accountNo
* @param cardNo
* @return
* @throws Exception
*/
Account queryAccountByCardNo(String cardNo) throws Exception;
/**
* up aacount information
* @param account
* @return
* @throws Exception
*/
int updateAccountByCardNo(Account account) throws Exception;
}
public class JdbcAccountDaoImpl implements AccountDao {
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
Connection con = DruidUtils.getInstance().getConnection();
String sql = "select * from account where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {
account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
Connection con = DruidUtils.getInstance().getConnection();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
con.close();
return i;
}
}
analysis:
Transfer page
⇕ ⇕
TransferServlet
// public class TransferServlet extends HttpServlet { TransferService transferService = newTransferServiceImpl(); }
⇕ ⇕
interface: TransferService
// public class TransferServiceImpl implements TransferService {
impl: TransferServiceImpl
// private AccountDao accountDao = new JdbcAccountDaoImpl();
⇕ ⇕
interface:AccountDao
impl: JdbcAccountDaoImpl
P1:in service level New define the references to DAO to the concrete implementations as that will make it tightly coupled, not easy in different implementation scenarios, which will lead to modify service code.
P2:serivice layer miss TransactionStatus. And data error may occur in transferring process, cause serious damage.
solution
P1: new tight coupled problem solution –Using Reflection to instantiate the object in factory pattern
public class TransferServiceImpl implements TransferService {
// private AccountDao accountDao = new JdbcAccountDaoImpl();
private AccountDao accountDao;
// @Override
// public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
// Account from = accountDao.queryAccountByCardNo(fromCardNo);
// Account to = accountDao.queryAccountByCardNo(toCardNo);
// from.setMoney(from.getMoney()-money);
// to.setMoney(to.getMoney()+money);
// accountDao.updateAccountByCardNo(to);
// accountDao.updateAccountByCardNo(from);
// }
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
Method injection, allows to inject a dependency right at the point of use when you have mutiple implementation, Method injection is a good choice
before:
TransferServiceImpl
↓
new
↓
JdbcAccountDaoImpl
now:
TransferServiceImpl
↓
factory read xml file,
use reflection to instantiate the object,
defines an interface for creating objects
↑
xml:
com.test.dao.JdbcAccountDaoImpl
com.test.service.TransferServiceImpl
code
define bean.xml file
<?xml version="1.0" encoding="UTF-8" ?>
<!--each bean represent a class-->
<beans>
<bean id="accountDao" class="com.test.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<bean id="transferService" class="com.test.service.impl.TransferServiceImpl">
<!--set+ name, Method injectione -->
<property name="AccountDao" ref="accountDao"></property>
</bean>
</beans>
define BeanFactory
/*
* factory class
* task1: read xml, use reflection to instantiate the object, store in the map
* task2: provide interfaces for creating objects (id)
*/
public class BeanFactory {
private static Map<String, Object> map = new HashMap<>(); // store object
/*
* read xml, use reflection to instantiate the object, store in the map
*/
static {
// load xml
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml")
// read xml
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.getRootElement();
Element rootElement = document.getRootElement();
List<Element> beanList = rootElement.selectNodes("//bean");
for (int i = 0; i < beanList.size(); i++) {
Element element = beanList.get(i);
// get bean id 和 class property
String id = element.attributeValue("id"); // accountDao
String clazz = element.attributeValue("class"); // com.test.dao.impl.JdbcAccountDaoImpl
// use reflection to instantiate the object
Class<?> aClass = Class.forName(clazz);
Object o = aClass.newInstance();
// store in the map
map.put(id, o);
}
// pass property of the bean into Oject
List<Element> propertyList = rootElement.selectNodes("//property");
// read property, get super element
for (int i = 0; i < propertyList.size(); i++) {
Element element = propertyList.get(i); //<property name="AccountDao" ref="accountDao"></property>
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
// find parent
Element parent = element.getParent();
// call parent element reflection
String parentId = parent.attributeValue("id");
Object parentObject = map.get(parentId);
// search all the methods
Method[] methods = parentObject.getClass().getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
if (method.getName().equalsIgnoreCase("set" + name)) { // setAccountDao(AccountDao accountDao)
method.invoke(parentObject, map.get(ref));
}
}
// put parentObject back into map
map.put(parentId, parentObject);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/*
* provide interfaces for creating objects (id)
* @param id
* @return
*/
public static Object getBean(String id) {
return map.get(id);
}
}
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
// private TransferService transferService = new TransferServiceImpl();
// get service object through BeanFactory
private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
public class TransferServiceImpl implements TransferService {
// get Dao object through BeanFactory
// private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");
// better way
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
}
P2 TransactionStatus make one wrong transaction
accountDao.updateAccountByCardNo(to);
int i = 1/0;
accountDao.updateAccountByCardNo(from);
run the application, click transfer button(A sent $100 to B), get an error, however, when checking the database,
B get $100, but A is still have the same amount of money!!!
solution
- executing multiple UPDATE statement in one connection
- put tranaction in service layer
TransactionStatus code add ConnectionUtils
/**
* get connection
*/
public class ConnectionUtils {
private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // store current threadlocal
/**
* connection from thread
*/
public Connection getCurrentThreadConn() throws SQLException {
Connection connection = threadLocal.get();
if(connection == null) {
// get connection
connection = DruidUtils.getInstance().getConnection();
// put into threadlocal
threadLocal.set(connection);
}
return connection;
}
}
add TransactionManager
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// begin transaction
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// commit transaction
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// rollback
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
add ProxyFactory
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/*
Jdk Dynamic Proxy
@param obj
@return proxy object
*/
public Object getJdkProxy(Object obj) {
// get proxy object
return Proxy.newProxyInstance(obj.getClass.getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
transactionManager.beginTransaction();
result = method.invoke(obj,args);
//
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
//
transactionManager.rollback();
// throw e can be caught by servlet
throw e;
}
return result;
}
}
});
}
/*
* cglib get Dynamic Proxy
@param obj
@return proxy object
*/
public Object getCglibProxy(Object obj) {
// get proxy object
return Enhancer.create(obj.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
transactionManager.beginTransaction();
result = method.invoke(obj,objects);
//
transactionManager.commit();
}catch (Exception e) {
e.printStackTrace();
//
transactionManager.rollback();
// throw e can be caught by servlet
throw e;
}
return result;
}
}
});
}
}
modify beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--each bean represent a class-->
<beans>
<bean id="accountDao" class="com.test.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<bean id="transferService" class="com.test.service.impl.TransferServiceImpl">
<!--set+ name, Method injectione -->
<property name="AccountDao" ref="accountDao"></property>
</bean>
<!--add three new bean-->
<bean id="connectionUtils" class="com.test.utils.ConnectionUtils">
</bean>
<!--transactionManager bean-->
<bean id="transactionManager" class="com.test.utils.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<!--ProxyFactory-->
<bean id="proxyFactory" class="com.test.factory.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"/>
</bean>
</beans>
public class JdbcAccountDaoImpl implements AccountDao {
private ConnectionUtils connectionUtils;
public void setConnecionUtils(ConnectionUtils connecionUtils) {
this.connectionUtils = connectionUtils;
}
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
// Connection con = DruidUtils.getInstance().getConnection();
// get connection from threadlocal
Connection con = connectionUtils.getCurrentThreadConn();
String sql = "select * from account where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1,cardNo);
ResultSet resultSet = preparedStatement.executeQuery();
Account account = new Account();
while(resultSet.next()) {
account.setCardNo(resultSet.getString("cardNo"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getInt("money"));
}
resultSet.close();
preparedStatement.close();
con.close();
return account;
}
@Override
public int updateAccountByCardNo(Account account) throws Exception {
// Connection con = DruidUtils.getInstance().getConnection();
Connection con = connectionUtils.getCurrentThreadConn();
String sql = "update account set money=? where cardNo=?";
PreparedStatement preparedStatement = con.prepareStatement(sql);
preparedStatement.setInt(1,account.getMoney());
preparedStatement.setString(2,account.getCardNo());
int i = preparedStatement.executeUpdate();
preparedStatement.close();
// con.close();
return i;
}
}
modify TransferServlet
@WebServlet(name = "transerServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// create service instance
// private TransferService transferService = new TransferServiceImpl();
// get service object through BeanFactory
// private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");
// get proxy object from factory
private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
private TransferService transferService = (TransferService) proxyFactory.getProxy(BeanFactory.getBean("transferService"));
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String fromCardNo = req.getParameter("fromCardNo");
String toCardNo = req.getParameter("toCardNo");
String moneyStr = req.getParameter("money");
int money = Integer.parseInt(moneyStr);
Result result = new Result();
try {
// 2. call service method
transferService.transfer(fromCardNo,toCardNo,money);
result.setStatus("200");
} catch (Exception e) {
e.printStackTrace();
result.setStatus("201");
result.setMessage(e.toString());
}
//
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().print(JsonUtils.object2Json(result));
}
}
p2 problem solved
Q: why use proxy for transferServiceImpl?
without proxy, all transtation relate code would be put in the TransferServiceImpl
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
try{
TransactionManager.getInstance().beginTransaction();*/
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(to);
// make a error to test
int c = 1/0;
accountDao.updateAccountByCardNo(from);
TransactionManager.getInstance().commit();
}catch (Exception e) {
e.printStackTrace();
TransactionManager.getInstance().rollback();
throw e;
}
}
}
we can see the transaction and Impl have become a tight couple, if there are multiple Impl need transaction method, we need add the same codes on every single method. Using proxy way to realize transaction management is a AOP