spring learning code 3

Thu, Apr 16, 2020 12-minute read

This is my learning note about how Spring core features like IoC, AOP works, with Code Examples .

spring Transaction(ACID)

  • Atomicity 原子性(
  • Consistency 一致性
  • Isolateion 隔离性
  • Durability 持久性

转账环境

经典场景: 银行转账

  • lucy 转账 100 给 mary

  • lucy 少 100, mary 多 100

  • Dao: 数据库操作,不写业务

    • 多钱方法
    • 少钱方法
  • Service: 业务操作

    • 创建转账方法
    • 调用 dao 两个方法
  • project step:

    1. 创建数据库表, 添加记录
image

spring Transaction

    1. 创建 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>
    1. 在 dao 创建两个方法,多钱少钱, 在 service 创建方法 (转账的方法)
    1. 发生错误, 比如转账到一半断电,产生 lucy 钱少了, mary 没到账

事务解决问题

    1. 开启事务: 业务层(javaEE 三层结构)
    1. Sping 进行事务管理操作
  • 2.1 编程式管理: 一般开发中不用
image

spring Transaction

  • 2.2 声明式事务管理 (开发中使用)

    • 注解方式 (开发中)
    • xml 配置方式
  • 3.spring 进行声明式事务管理,底层使用 AOP 原理

  • 4.spring 事务管理 API

  • 4.1 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实体类

PlatformTransactionManager -> DataSourceTransactionManager

image

spring Transaction

image

spring Transaction

  • 4.2 开启事务注解

    image

    spring Transaction

  • 5.service 类上面 (获取 service 类里面方法上面) 添加事务注解

  • 5.1 @Transactional 可以加到类或者某个方法上面

    image

    spring Transaction

事务操作 (声明式事务管理参数配置)

    1. 事务方法 : 对数据库数据进行变化的操作
    1. service 类上面添加注解 @Transactional
    1. propagation : 事务传播行为:有事务的方法调用没事务的方法,或者反过来 或者两个方法都有事务 就是事务传播行为

@Transactional(propogation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void add() {
    // call update method
    update();
}

public void update() {

}
    1. isolation: 事务隔离级别 : 多事务操作直接不会产生影响,不考虑隔离性产生很多问题 解决三个读问题: 脏读,不可重复读,虚读
  • 3.1 脏读: 一个未提交事务读取到另一个未提交事务的数据

  • 3.2 不可重复读: 一个未提交事务读取到另一个提交事务修改数据

  • 3.3 虚读: 一个未提交事务读取到另一个提交事务添加数据

  • 3.4 READ UNCOMMITTED(读未提交) :无法解决 3 问题

  • 3.5 READ COMMITTED(读已提交) :解决 脏读问题

  • 3.6 REPEATABLE READ(可重复度) :解决 脏读 不可重复读 问题

  • 3.7 SERIALIZABLE(串行化) :解决脏读 不可重复读 虚读问题

    1. timeout: 超时
    1. readOnly : 是否只读
    1. rollbackFor : 回滚
    1. norollbackFor

事务操作 (完全注解声明式事务管理)

    1. 创建配置类, 使用配置类替代 xml 配置文件
      image

      spring Transaction

spring5 新功能

自带通用的日志封装

    1. 整合 Log4j2
    1. 引入 jar 包
  • 2.1 log4j-api

  • 2.2 log4j-core

  • 2.3 log4j-slf4j-impl

  • 2.4 slf4j-api

    1. 创建 log4j2.xml 配置
      image

      spring Transaction

JUnit5

    1. 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() {

    }
}

    1. 整合 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

image

spring Transaction

    1. 响应式编程: 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 或 者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流

    image

    spring Transaction

  • 1.4 操作符: map 元素映射为新元素; flatMap 元素映射为流

    1. SpringWebflux 执行流程和核心 api SpringWebflux 基于 Reactor, 默认容器是 Netty, Netty 是高性能 NIO 框架,异步非阻塞的框架
  • 2.1 Netty 阻塞状态 Socket -》 InputStream -》 read() -》 Thread

非阻塞 NIO 状态

image

spring Transaction

  • 2.2 SpringWebflux 核心控制器 DispatchHandler, 实现接口 WebHandler, 负责请求的处理

    • HanddlerMapping : 请求查询处理的方法
    • HandlerAdapter: 真正负责请求处理
    • HandlerResultHandler: 响应结果处理
  • 2.3 函数式编程,两个接口: RouterFunction 和 HandlerFunction

    1. 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 创建包和相关类
    image

    spring Transaction

image

spring Transaction

image

spring Transaction

    1. SpringWebflux (基于函数)

Spring V1.0 DispatcherServlet

Spring IoC, DI, MVC 的工作原理

遵循单一职责原则

MIN 版本 Spring 基本实现思路

  1. 配置阶段
    • 配置 web.xml | DispatcherServlet
    • 设定 init-param | contextConfigLocation = classpath:application.xml
    • 设定 url-pattern | /*
    • 配置 Annotation | @Controller @Service @Autowrited @RequestMapping

  2. 初始化阶段
    • 调用init()方法 | 加载配置文件
    • IOC容器初始化 | *Map<String, Object>
    • 扫描相关的类 | scan-package=“com.gupaoedu” IOC + 创建实例化并保存至容器 | 通过反射机制将类实例化放入IOC容器中 DI + 进行DI操作 | 扫描IOC容器中的实例,给没有赋值的属性自动赋值 MVC + 初始化HandlerMapping | 将一个URL和一个Method进行一对一的关联映射Map<String, Method>

  1. 运行阶段

    • 调用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

I

工厂模式,原型,单例(工厂怎么把对象创建出来,交给用户)

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


Ok, It’s time for spring1

Ok, It’s time for spring2

Ok, It’s time for spring mvc