ioc spring 上机案例_Spring源码高级笔记之——银行案例手写IOC和AOP

手写实现lOC和AOP

上一部分我们理解了loC和AOP思想,我们先不考虑Spring是如何实现这两个思想的,此处准备了一个『银行转账」的案例,请分析该案例在代码层次有什么问题?分析之后使用我们已有知识解决这些问题(痛点)。其实这个过程我们就是在一步步分析并手写实现loC和AOP。

第1节银行转账案例界面

第2节银行转账案例表结构

第3节银行转账案例代码调用关系

第4节银行转账案例关键代码

TransferServlet

packagecom.lagou.edu.servlet;

importcom.lagou.edu.service.impl.TransferServiceImpl;

importcom.lagou.edu.utils.JsonUtils;

importcom.lagou.edu.pojo.Result;

importcom.lagou.edu.service.TransferService;

importjavax.servlet.ServletException;

importjavax.servlet.annotation.WebServlet;

importjavax.servlet.http.HttpServlet;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importjava.io.IOException;

/**

*@author斗帝

*/

@WebServlet(name="transferServlet",urlPatterns ="/transferServlet")

publicclassTransferServletextendsHttpServlet{

// 1. 实例化service层对象

private TransferService transferService = new TransferServiceImpl();

@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. 调用service层方法

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));

}

}

TransferService接口及实现类

packagecom.lagou.edu.service;

/**

*@author斗帝

*/

publicinterfaceTransferService{

voidtransfer(String fromCardNo,String toCardNo,intmoney)throws

Exception;

}packagecom.lagou.edu.service.impl;

importcom.lagou.edu.dao.AccountDao;

importcom.lagou.edu.dao.impl.JdbcAccountDaoImpl;

importcom.lagou.edu.pojo.Account;

importcom.lagou.edu.service.TransferService;

/**

*@author斗帝

*/

publicclassTransferServiceImplimplementsTransferService{

privateAccountDao accountDao =newJdbcAccountDaoImpl();

@Override

publicvoidtransfer(String fromCardNo, String toCardNo,intmoney)

throwsException{

Account from = accountDao.queryAccountByCardNo(fromCardNo);Account to = accountDao.queryAccountByCardNo(toCardNo);from.setMoney(from.getMoney()-money);to.setMoney(to.getMoney()+money);accountDao.updateAccountByCardNo(from);accountDao.updateAccountByCardNo(to); }

AccountDao层接口及基于Jdbc的实现类

packagecom.lagou.edu.dao;

importcom.lagou.edu.pojo.Account;

/**

*@author斗帝

*/

publicinterfaceAccountDao{

AccountqueryAccountByCardNo(String cardNo)throwsException;

intupdateAccountByCardNo(Account account)throwsException;

}

JdbcAccountDaoImpl(Jdbc技术实现Dao层接口)

packagecom.lagou.edu.dao.impl;

importcom.lagou.edu.pojo.Account;

importcom.lagou.edu.dao.AccountDao;

importcom.lagou.edu.utils.DruidUtils;

importjava.sql.Connection;

importjava.sql.PreparedStatement;

importjava.sql.ResultSet;

/**

*@author斗帝

*/

publicclassJdbcAccountDaoImplimplementsAccountDao{

@Override

publicAccountqueryAccountByCardNo(String cardNo)throwsException{

//从连接池获取连接

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;

}

}

第5节 银行转账案例代码问题分析

(1)问题一:在上述案例实现中,service 层实现类在使用 dao 层对象时,直接在TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对象,然而一个 new 关键字却将 TransferServiceImpl 和 dao 层具体的一个实现类JdbcAccountDaoImpl 耦合在了一起,如果说技术架构发生一些变动,dao 层的实现要使用其它技术,比如 Mybatis,思考切换起来的成本?每一个 new 的地方都需要修改源代码,重新编译,面向接口开发的意义将大打折扣?

(2)问题二:service 层代码没有竟然还没有进行事务控制 ?!如果转账过程中出现异常,将可能导致数据库数据错乱,后果可能会很严重,尤其在金融业务。

第6节问题解决思路

针对问题—思考:

实例化对象的方式除了new之外,还有什么技术?反射(需要把类的全限定类名配置在xml中)

考虑使用设计模式中的工厂模式解耦合,另外项目中往往有很多对象需要实例化,那就在工厂中使用反射技术实例化对象,工厂模式很合适

更进一步,代码中能否只声明所需实例的接口类型,不出现 new 也不出现工厂类的字眼,如下图? 能!声明一个变量并提供 set 方法,在反射的时候将所需要的对象注入进去吧

针对问题二思考:

service 层没有添加事务控制,怎么办?没有事务就添加上事务控制,手动控制 JDBC 的Connection 事务,但要注意将Connection和当前线程绑定(即保证一个线程只有一个Connection,这样操作才针对的是同⼀个 Connection,进而控制的是同一个事务)

第7节 案例代码改造

(1)针对问题一的代码改造

beans.xml

class="com.lagou.edu.service.impl.TransferServiceImpl">

class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl">

增加 BeanFactory.java

package com.lagou.edu.factory;

importorg.dom4j.Document;

importorg.dom4j.DocumentException;

importorg.dom4j.Element;

importorg.dom4j.io.SAXReader;

importjava.io.InputStream;

importjava.lang.reflect.InvocationTargetException;

importjava.lang.reflect.Method;

importjava.util.HashMap;

importjava.util.List;

importjava.util.Map;

/**

* @author 应癫

*/

publicclassBeanFactory {

/**

* 工厂类的两个任务

* 任务一:加载解析xml,读取xml中的bean信息,通过反射技术实例化bean对象,然后放入

map待用

* 任务二:提供接口方法根据id从map中获取bean(静态方法)

*/

privatestaticMap map =newHashMap<>();

static{

InputStream resourceAsStream =BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

SAXReader saxReader =newSAXReader();

try{

Documentdocument= saxReader.read(resourceAsStream);

Element rootElement =document.getRootElement();

List list = rootElement.selectNodes("//bean");

// 实例化bean对象

for (int i = 0; i < list.size(); i++) {

Element element = list.get(i);

String id = element.attributeValue("id");

String clazz = element.attributeValue("class");

Class> aClass = Class.forName(clazz);

Object o = aClass.newInstance();

map.put(id,o);

}

// 维护bean之间的依赖关系

List propertyNodes =

rootElement.selectNodes("//property");

for (int i = 0; i < propertyNodes.size(); i++) {

Element element = propertyNodes.get(i);

// 处理property元素

String name = element.attributeValue("name");

String ref = element.attributeValue("ref");

String parentId =

element.getParent().attributeValue("id");

Object parentObject = map.get(parentId);

Method[] methods = parentObject.getClass().getMethods();

for (int j = 0; j < methods.length; j++) {

Method method = methods[j];

if(("set" + name).equalsIgnoreCase(method.getName()))

{

// bean之间的依赖关系(注入bean)

Object propertyObject = map.get(ref);

method.invoke(parentObject,propertyObject);

}

}

// 维护依赖关系后重新将bean放⼊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();

}

}

public static Object getBean(String id) {

return map.get(id);

}

}

修改 TransferServlet

修改 TransferServiceImpl

(2)针对问题二的改造

增加 ConnectionUtils

packagecom.lagou.edu.utils;

importjava.sql.Connection;

importjava.sql.SQLException;

/**

*@author斗帝

*/

publicclassConnectionUtils{

/*private ConnectionUtils() {

}

private static ConnectionUtils connectionUtils = new

ConnectionUtils();

public static ConnectionUtils getInstance() {

return connectionUtils;

}*/

privateThreadLocal threadLocal =newThreadLocal<>();//

存储当前线程的连接

/**

* 从当前线程获取连接

*/

public Connection getCurrentThreadConn() throws SQLException {

/**

* 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取一个连接绑定到

当前线程

*/

Connection connection = threadLocal.get();

if(connection == null) {

// 从连接池拿连接并绑定到线程

connection = DruidUtils.getInstance().getConnection();

// 绑定到当前线程

threadLocal.set(connection);

}

return connection;

}

}

增加 TransactionManager 事务管理器类

packagecom.lagou.edu.utils;

importjava.sql.SQLException;

/**

*@author斗帝

*/

publicclassTransactionManager{

privateConnectionUtils connectionUtils;

publicvoidsetConnectionUtils(ConnectionUtils connectionUtils){

this.connectionUtils = connectionUtils;

}// 开启事务

public void beginTransaction() throws SQLException {

connectionUtils.getCurrentThreadConn().setAutoCommit(false);

}

// 提交事务

public void commit() throws SQLException {

connectionUtils.getCurrentThreadConn().commit();

}

// 回滚事务

public void rollback() throws SQLException {

connectionUtils.getCurrentThreadConn().rollback();

}

}

增加 ProxyFactory 代理工厂类

packagecom.lagou.edu.factory;

importcom.lagou.edu.utils.TransactionManager;

importjava.lang.reflect.InvocationHandler;

importjava.lang.reflect.Method;

importjava.lang.reflect.Proxy;

/**

*@author斗帝

*/

publicclassProxyFactory{

privateTransactionManager transactionManager;

publicvoidsetTransactionManager(TransactionManager

transactionManager){

this.transactionManager = transactionManager;

}publicObjectgetProxy(Object target){

returnProxy.newProxyInstance(this.getClass().getClassLoader(),

target.getClass().getInterfaces(),newInvocationHandler() {

@Override

publicObjectinvoke(Object proxy, Method method, Object[]

args)throwsThrowable{

Object result =null;

try{

// 开启事务

transactionManager.beginTransaction();

// 调用原有业务逻辑

result = method.invoke(target,args);

// 提交事务

transactionManager.commit();

}catch(Exception e) {

e.printStackTrace();

// 回滚事务

transactionManager.rollback();

// 异常向上抛出,便于servlet中捕获

throw e.getCause();

}

return result;

}

});

}

}

修改 beans.xml

class="com.lagou.edu.dao.impl.JdbcAccountDaoImpl">

class="com.lagou.edu.service.impl.TransferServiceImpl">

class="com.lagou.edu.utils.ConnectionUtils">

class="com.lagou.edu.utils.TransactionManager">

修改 JdbcAccountDaoImpl

packagecom.lagou.edu.dao.impl;

importcom.lagou.edu.pojo.Account;

importcom.lagou.edu.dao.AccountDao;

importcom.lagou.edu.utils.ConnectionUtils;

importcom.lagou.edu.utils.DruidUtils;

importjava.sql.Connection;

importjava.sql.PreparedStatement;

importjava.sql.ResultSet;

/**

*@author斗帝

*/

publicclassJdbcAccountDaoImplimplementsAccountDao{

privateConnectionUtils connectionUtils;

publicvoidsetConnectionUtils(ConnectionUtils connectionUtils){

this.connectionUtils = connectionUtils;

}@Override

publicAccountqueryAccountByCardNo(String cardNo)throwsException{

//从连接池获取连接

// Connection con = DruidUtils.getInstance().getConnection();

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连接

//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;

}

}

修改 TransferServlet

packagecom.lagou.edu.servlet;

importcom.lagou.edu.factory.BeanFactory;

importcom.lagou.edu.factory.ProxyFactory;

importcom.lagou.edu.service.impl.TransferServiceImpl;

importcom.lagou.edu.utils.JsonUtils;

importcom.lagou.edu.pojo.Result;

importcom.lagou.edu.service.TransferService;

importjavax.servlet.ServletException;

importjavax.servlet.annotation.WebServlet;

importjavax.servlet.http.HttpServlet;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importjava.io.IOException;

/**

*@author斗帝

*/

@WebServlet(name="transferServlet",urlPatterns ="/transferServlet")

publicclassTransferServletextendsHttpServlet{

// 1. 实例化service层对象

//private TransferService transferService = new TransferServiceImpl();

//private TransferService transferService = (TransferService)

BeanFactory.getBean("transferService");

// 从工厂获取委托对象(委托对象是增强了事务控制的功能)

// 首先从BeanFactory获取到proxyFactory代理工厂的实例化对象

private ProxyFactory proxyFactory = (ProxyFactory)

BeanFactory.getBean("proxyFactory");

private TransferService transferService = (TransferService)

proxyFactory.getJdkProxy(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. 调用service层方法

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));

}

}

好了,银行的案例今天就写到这里,喜欢的朋友可以关注一下小编,此系列文章持续更新中;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值