Java学习笔记分享之Spring篇(原理)

阅读: 评论:0

Java学习笔记分享之Spring篇(原理)

Java学习笔记分享之Spring篇(原理)

Spring 笔记分享

整体架构

1.1 概述

Spring框架可在任何类型的部署平台上为基于Java的企业应用程序提供全面的编程和配置模型。

Spring的一个关键元素是在应用程序级别的基础框架支持:Spring专注于企业应用程序的“探索”,以便于团队可以专注于应用程序级别的业务逻辑,而不必处理特定的部署环境问题。

​ Spring可以理解为框架粘合剂,大部分的框架都是不可以相互作用的。Spring提供了这样一个平台,框架只需要向Spring靠拢即可。最终结果就是基于Spring实现了不同框架技术的整合。而且Spring提供了对各大框架的支持,我们可以更加方便的使用。

1.2 优点

Spring的核心是IOC和AOP功能,基于IOC实现对象的依赖和管理,基于AOP实现了事务的控制和管理。

  • 简化开发、功能解耦

    通过IOC容器,那么对象直接的依赖关系交给Spring管理与控制,而且基于面向接口编程可以使得代码解耦,改变实现类不需要修改引用。

  • AOP编码支持

    通过AOP功能,可以面向切面编程,传统的基于OOP实现的功能可以通过AOP轻松搞定。

  • 声明式事务

    @Transactional 事务注解可以帮助我们实现事务控制与管理,而不需要手动进行事务管理。是的事务与代码解耦,声明式事务可以非常灵活的配置,提供开发的效率和质量。

  • 方便程序测试

    Spring提供了对测试的支持,可以非常方便的构建测试。

  • 方便集成框架

    前面说过Spring其实是一个粘合剂,可以非常轻松的将各种框架整合到项目中,而且还提供了更加简便的操作方式。

  • 降低JavaEE API的使用难度

    Spring内部其实对很多的JavaEE API进行了封装,是的我们面的JavaEE编程的时候更加方便。如JDBC、JavaMail等功能。

1.3 核心思想功能
1.3.1 IoC

IOC:Inversion of Control(控制反转),这是一种技术思想。主要是解决Java开发领域的对象创建和管理问题。

传统对象管理(手动创建对象和依赖)

IoC管理(由IoC创建并管理对象)

IoC主要解决对象之间的耦合问题,我们不关心对象的创建和如何依赖,只需要在使用的地方@Autowired注入即可。IoC会帮助我们注入和管理所需要的Bean

IoC和DI的关系

DI:Dependancy Injection(依赖注入)

IoC是控制反转,DI是依赖注入。它们共同完成了对象管理这一件事情。

1.3.2 AOP

AOP:Aspect oriented Programming面向切面编程

AOP其实是OOP思想的延续和扩展。OOP是一种垂直结构,AOP是一种横向结构。

OOP的体系开发模式

AOP切面编程

如何理解切面

1.4 基于IOC和AOP代码实现

​ 在2.2.3部分,我们已经对IoCAOP思想有了大致的了解,但是这都是基于概念和思想上的。下面我们通过一个《银行转账》的基础案例分析其中的问题,然后基于IoC和AOP思想慢慢的改造解决痛点问题并加深我们的理解和认识。

开发过程:转账操作可以写html页面发起请求,也可以使用功能http模拟请求。

银行转账:

  • A用户发起向B用户转账100元人民币请求
  • A用户账户扣款100元(当然要校验账户余额,这里省略)
  • B用户账户加上100元
  • 最终A账户少100元,而B用户多100元

数据库SQL准备

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (`cardNo` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`name` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`money` int(11) NULL DEFAULT 0
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1006029621011001', '张三', 100000);
INSERT INTO `account` VALUES ('2006029621011000', '李四', 100000);SET FOREIGN_KEY_CHECKS = 1;

Maven依赖

<dependencies><!-- 单元测试Junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!-- mysql数据库驱动包 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.35</version></dependency><!--druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version></dependency><!-- servlet --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- jackson依赖 --><dependency><groupId>com.</groupId><artifactId>jackson-databind</artifactId><version>2.9.6</version></dependency><!--dom4j依赖--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><!--xpath表达式依赖--><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency><!--引入cglib依赖包--><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.1_2</version></dependency>
</dependencies>
1.4.1 调用流程

1.4.2 基础版本
  • 基础实体
// 数据库实体映射
public class Account {private String cardNo;private String name;private int money;// getter setter@Overridepublic String toString() {return "Account{" +"cardNo='" + cardNo + ''' +", name='" + name + ''' +", money=" + money +'}';}
}
// 处理结果响应
public class Result {private String status;private String message;// getter setter@Overridepublic String toString() {return "Result{" +"status='" + status + ''' +", message='" + message + ''' +'}';}
}
  • 基础工具类
// 封装数据库连接池
public class DruidUtils {private DruidUtils() {}private static DruidDataSource druidDataSource = new DruidDataSource();static {druidDataSource.setDriverClassName(&#sql.jdbc.Driver");druidDataSource.setUrl("jdbc:mysql://localhost:3306/lagou_spring");druidDataSource.setUsername("root");druidDataSource.setPassword("root");}public static DruidDataSource getInstance() {return druidDataSource;}
}
  • TransferServlet
@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {// 实例化service层对象private TransferService transferService = new TransferServiceImpl();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponseresp) throws ServletException, IOException {// 设置请求体的字符编码,避免编码错误req.setCharacterEncoding("UTF-8");String fromCardNo = Parameter("fromCardNo");String toCardNo = Parameter("toCardNo");String moneyStr = Parameter("money");int money = Integer.parseInt(moneyStr);Result result = new Result();try {// 2. 调用transferService实现转账ansfer(fromCardNo, toCardNo, money);result.setStatus("200");} catch (Exception e) {e.printStackTrace();result.setStatus("201");result.String());}// 响应resp.setContentType("application/json;charset=utf-8");Writer().print(JsonUtils.object2Json(result));}
}
  • TransferService接口及实现
public interface TransferService {void transfer(String fromCardNo, String toCardNo, int money) throws Exception;
}
public class TransferServiceImpl implements TransferService {// 实例化Jdbc操作数据库对象private AccountDao accountDao = new JdbcAccountDaoImpl();@Overridepublic void transfer(String fromCardNo, String toCardNo, int money) throws Exception {Account from = accountDao.queryAccountByCardNo(fromCardNo);Account to = accountDao.queryAccountByCardNo(toCardNo);from.Money() - money);to.Money() + money);accountDao.updateAccountByCardNo(to);// 模拟转账异常,现在暂时不开启,记住后面需要开启模拟异常// int c = 1 / 0;accountDao.updateAccountByCardNo(from);}
}
  • AccountDao接口和实现类
public interface AccountDao {Account queryAccountByCardNo(String cardNo) throws Exception;int updateAccountByCardNo(Account account) throws Exception;
}
public class JdbcAccountDaoImpl implements AccountDao {@Overridepublic Account queryAccountByCardNo(String cardNo) throws Exception {//从连接池获取连接Connection con = Instance().getConnection();String sql = "select * from account where cardNo=?";PreparedStatement preparedStatement = con.prepareStatement(sql);preparedStatement.setString(1, cardNo);ResultSet resultSet = uteQuery();Account account = new Account();// 结果封装while (()) {account.String("cardNo"));account.String("name"));account.Int("money"));}resultSet.close();preparedStatement.close();con.close();return account;}@Overridepublic int updateAccountByCardNo(Account account) throws Exception {//从连接池获取连接Connection con = Instance().getConnection();String sql = "update account set money=? where cardNo=?";PreparedStatement preparedStatement = con.prepareStatement(sql);preparedStatement.setInt(1, Money());preparedStatement.setString(2, CardNo());int i = uteUpdate();preparedStatement.close();con.close();return i;}
}

问题分析

问题解决

1)new关键字代码耦合问题?对象的依赖关系如何确定?什么时候实例化对象?

解决方案:

  • 使用反射技术实例化对象。
  • 对象的依赖关系可以通过xml配置文件的方式确定。
  • 对象的实例化可以使用工厂模式,在项目启动的时候实例化对象并解决依赖的Bean注入问题(使用set方式注入)。

2)没有事务控制,无法保证数据库操作的原子性?

解决方案:

  • 基于JDBC实现事务控制,事务配置在Service层。
  • 使用本地线程绑定Connection事务,这样就可以保证数据库都是同一个事务。
1.4.3 解决问题一
<?xml version="1.0" encoding="UTF-8"?>
<beans><!--托管:创建Dao接口--><bean id="accountDao" class=&#j.dao.impl.JdbcAccountDaoImpl"></bean><bean id="transferService" class=&#j.service.impl.TransferServiceImpl"><property name="accountDao" ref="accountDao"/></bean></beans>
  • 增加BeanFactory解析l创建对象
/*** 任务一:解析xml配置文件,利用反射技术生产对应的实例对象。同时管理对象的注入问题* 任务二:提供静态方法根据ID获取类对象** @Author zhichunqiu* @time 2020/6/3 14:56*/
public class BeanFactory {// 存储实例化的Beanprivate static Map<String, Object> beans = new HashMap<>();public static Object getBean(String name) {(name);}static {try {InputStream resourceAsStream = ClassLoader().getResourceAsStream(&#l");// 使用dom4j技术解析xml配置文件SAXReader saxReader = new SAXReader();Document document = ad(resourceAsStream);Element rootElement = RootElement();// 读取bean标签List<Element> elementList = rootElement.selectNodes("//beans/bean");// 实例化所有对象,并且放到Map中for (Element element : elementList) {String id = element.attributeValue("id");String clazz = element.attributeValue("class");Class<?> aClass = Class.forName(clazz);Object instance = wInstance();beans.put(id, instance);}// 维护bean之间的依赖关系List<Element> propertyList = rootElement.selectNodes("//bean/property");for (Element element : propertyList) {// 属性名称String name = element.attributeValue("name");// 应用类型的值IDString ref = element.attributeValue("ref");// 获取父标签的属性Element parentElement = Parent();String parentId = parentElement.attributeValue("id");Object o = (parentId);Method[] methods = o.getClass().getMethods();for (Method method : methods) {// 使用set方法注入if (Name().equalsIgnoreCase("set" + name)) {method.invoke(o, (ref));}}}System.out.println(beans);} catch (Exception e) {e.printStackTrace();}}
}
  • 修改TransferServlet、TransferServiceImpl从BeanFactory中获取对象
@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {// 获取TransferService对象private TransferService transferService = (TransferService) Bean("transferService");
}    
public class TransferServiceImpl implements TransferService {private AccountDao accountDao;// set方式注入对象public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}
}    
1.4.4 解决问题二
  • 增加ConnectionUtils管理数据库连接
/*** Connecion获取类,与本地线程绑定* @Author zhichunqiu* @time 2020/6/3 18:55*/
public class ConnectionUtils {// 本地线程,存储连接private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();// 从本地线程中获取连接,如果没有就从数据库连接池中获取并设置到本地线程public Connection getCurrentThreadConn() throws SQLException {Connection connection = ();if (connection == null) {connection = Instance().getConnection();threadLocal.set(connection);}return connection;}
}
  • 增加TransactionManager类管理事务
j.utils;import java.sql.Connection;
import java.sql.SQLException;/*** 事务控制管理器** @Author zhang yong jun* @time 2020/6/3 19:02*/
public class TransactionManager {private ConnectionUtils connectionUtils;public void setConnectionUtils(ConnectionUtils connectionUtils) {tionUtils = connectionUtils;}// 开启事务public void start() throws SQLException {CurrentThreadConn().setAutoCommit(false);}// 提交事务public void commit() throws SQLException {CurrentThreadConn()mit();}// 回滚事务public void rollback() throws SQLException {CurrentThreadConn().rollback();}
}
  • 增加ProxyFactory代理工厂类,生成Service代理实现事务控制
j.factory;j.utils.TransactionManager;import flect.InvocationHandler;
import flect.Method;
import flect.Proxy;/*** 代理工厂,负责控制事务的开启,提交与回滚** @Author zhang yong jun* @time 2020/6/3 19:30*/
public class ProxyFactory {private TransactionManager transactionManager;public void setTransactionManager(TransactionManager transactionManager) {ansactionManager = transactionManager;}public Object getJdkProxy(Object obj) {Class().getClassLoader(), Class().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {// 代理对象开启事务transactionManager.start();// 执行原方法逻辑method.invoke(obj, args);// 提交事务transactionManagermit();} catch (Exception e) {// 异常回滚事务llback();// 异常由上层处理throw e;}return result;}});}
}
  • 修改l,增加事务管理器、代理对象、数据库连接工具对象
<?xml version="1.0" encoding="UTF-8"?>
<beans><!--将ConnectionUtils工具类托管--><bean id="connectionUtils" class=&#j.utils.ConnectionUtils"></bean><!--托管:创建Dao接口--><bean id="accountDao" class=&#j.dao.impl.JdbcAccountDaoImpl"><property name="connectionUtils" ref="connectionUtils"/></bean><bean id="transferService" class=&#j.service.impl.TransferServiceImpl"><property name="accountDao" ref="accountDao"/></bean><!--托管:创建事务管理对象--><bean id="transactionManager" class=&#j.utils.TransactionManager"><property name="connectionUtils" ref="connectionUtils"/></bean><!--托管:创建代理工厂对象--><bean id="proxyFactory" class=&#j.factory.ProxyFactory"><property name="transactionManager" ref="transactionManager"/></bean></beans>
  • 修改JdbcAccountDaoImpl类
/*** @author zhichunqiu*/
public class JdbcAccountDaoImpl implements AccountDao {private ConnectionUtils connectionUtils;// 注入对象public void setConnectionUtils(ConnectionUtils connectionUtils) {tionUtils = connectionUtils;}@Overridepublic Account queryAccountByCardNo(String cardNo) throws Exception {//从连接池获取连接
//        Connection con = Instance().getConnection();// 改为注入
//        Connection con = CurrentThreadConn();Connection con = CurrentThreadConn();String sql = "select * from account where cardNo=?";PreparedStatement preparedStatement = con.prepareStatement(sql);preparedStatement.setString(1, cardNo);ResultSet resultSet = uteQuery();Account account = new Account();while (()) {account.String("cardNo"));account.String("name"));account.Int("money"));}resultSet.close();preparedStatement.close();// 从本地线程获取连接,不可以释放,否则就解除了绑定
//        con.close();return account;}@Overridepublic int updateAccountByCardNo(Account account) throws Exception {
//        Connection con = Instance().getConnection();// 改为注入
//        Connection con = CurrentThreadConn();Connection con = CurrentThreadConn();String sql = "update account set money=? where cardNo=?";PreparedStatement preparedStatement = con.prepareStatement(sql);preparedStatement.setInt(1, Money());preparedStatement.setString(2, CardNo());int i = uteUpdate();preparedStatement.close();
//        con.close();return i;}
}
  • 修改TransferServlet从代理工厂获取有事务控制的Service
@WebServlet(name = "transferServlet", urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {// 获取代理工厂private ProxyFactory proxyFactory = (ProxyFactory) Bean("proxyFactory");// 从代理工厂中使用JDK代理返回TransferService对象private TransferService transferService = (TransferService) Bean("transferService"));
}    

总结

​ 截止目前,我们已经完成了《银行转账》案例基础到问题分析,然后基于IoCAOP思想的改造。从而实现了对象统一管理和依赖注入、事务管理控制的问题。其实这些就是Spring框架的IoCAOP原理。

挑战

  • 上诉的银行转账案例是基于XML配置方式实现的,你可以挑战一下将其改造成基于注解方式实现。
  • 你了解了Spring的IoC和AOP核心思想,那么是否可以读懂了Spring框架源码呢?
1.5 Spring学习思维导图(提供参考)

本文发布于:2024-02-01 08:23:02,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170674698235202.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:学习笔记   原理   Java   Spring
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23