自学ssm->springboot->springcloud,所以很多东西会用但理解较浅,所以现在从最开始的ssm开始进行对原理以及运行过程的整理知识归纳,若有错误感谢指正。
Spring
Spring运行原理
1. Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
- JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
- ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
- OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
- Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
2. Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
- Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
- Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
- Portlet 模块:提供了在 Portlet 环境中使用 MVC实现,类似 Web-Servlet 模块的功能。
3. Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
- Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
- Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
- Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
- Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
4. 其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
- AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
- Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
- Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
Spring IoC容器的基本概念
2.基本讲解
在传统方法中,Java对象需要调用另外一个Java对象时(如①调用②),调用者(①)通常采用“new”被调用者(②)来创建对象,这种方式会增加调用者和被调用者之间的耦合性。
当Spring框架出现后,对象实例不再由调用者来创建,即不通过直接new被调用者来创建对象,而是交给Spring容器来创建。这时候调用者的程序将不再进行直接控制,而实转交给了Spring容器,这就是Spring的控制反转。
从Spring容器角度出发,Spring容器负责将被依赖对象赋值给调用者的成员变量,相当于调用者注入它所有依赖的实例,这就是Spring的依赖注入。
举一个很直白的例子:海绵宝宝做出第一个美味蟹黄堡,当海绵宝宝想做多几个蟹黄堡(调用者)时就得参照第一个蟹黄堡(被调用者)来进行制作。一旦第一个蟹堡王丢了或者变味了,他后面就没办法做出一模一样的美味蟹黄堡(耦合度高),所以这时候海绵宝宝把第一个蟹黄堡放到一个按照配方运转的制造机器里(Spring容器),往后创造新的蟹黄堡都交给制造机来控制(Spring的控制反转)。
2.通过Web服务其方式实例化ApplicationContext容器(最常用的方式)
Spring IoC容器的设计主要是基于BeanFactory和ApplicationContext两个接口,而ApplicationContext是BeanFactory的子接口
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>StudentShopping</display-name> <!-- needed for ContextLoaderListener --> <!-- Spring加载ApplicationContext容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
web.xml中ContextLoaderListener的作用:就是启动Web容器时,读取在contextConfigLocation中定义的xml文件,自动装配ApplicationContext的配置信息,并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。
依赖注入的理解
以下只进行简单代码讲解,关于spring依赖注入类型具体实现可参考这篇文章:https://blog.csdn.net/lyc_liyanchao/article/details/82428726
而这两种注入可以分别用两种注解来实现一个是 @Autowired 和 @Resource :@Autowired 可以用作构造注入 /@Resource 用作 setter注入。
- @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。
- @Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。
另外在构造方法注入中,<constructor-arg>元素【以下黄色标注部分】表示构造方法的一个参数,且使用时不区分顺序。当构造方法的参数出现混淆、无法区分时,可以通过<constructor-arg>元素的index属性指定该参数的位置索引,索引从0开始。<constructor-arg>元素还提供了type属性用来指定参数的类型,避免字符串和基本数据类型的混淆。这也解释了为什么@Autowired 可用作构造注入
public class Car { private String brand; private String corp; private double price; private int maxSpeed; public Car(String brand, String corp, double price) { this.brand = brand; this.corp = corp; this.price = price; } public Car(String brand, String corp, int maxSpeed) { this.brand = brand; this.corp = corp; this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price + ", maxSpeed=" + maxSpeed + "]"; } }
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="car" class="com.auguigu.spring.beans.Car"> <constructor-arg value="Audi" type="java.lang.String"></constructor-arg> <constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg> <constructor-arg value="30000" type="int"></constructor-arg> </bean> <bean id="car2" class="com.auguigu.spring.beans.Car"> <constructor-arg value="BMW" type="java.lang.String"></constructor-arg> <constructor-arg value="BeiJing" type="java.lang.String"></constructor-arg> <constructor-arg value="300.00" type="double"></constructor-arg> </bean> </beans>
关于@Autowired的使用(拓展)
方法1:
//构造函数注入的方式: public class TestController { private final TestService testService; @Autowired public TestController(TestService testService) { this.testService = testService; } … }
方法2:
//变量注入的方式: public class TestController { @Autowired private TestService testService; … }
采用方法2可能会导致NPE(Null Pointer Exception),产生代码如下:
public class TestController { @Autowired private TestService testService; private String testname;
//先执行此处构造函数,而此时testService相当于并未注入,所以报控制针异常 public TestController(){ this.testname = testService.getTestName(); } }
该类的构造函数中的变量值是通过TestService实例来调用TestService类中的方法获得,而Java类会先执行构造函数,然后在通过@Autowired注入实例,因此在执行构造函数的时候就会报错。
解决方法:
//解决方案就是采用构造函数的注入方式,如下: public class TestController { private TestService testService; private String testname; @Autowired public TestController(TestService testService){ this.testService = testService; this.testname = testService.getTestName(); } }
因此,采用构造函数注入有以下号处:依赖不为空(省去了我们对其检查),当要实例化TestController的时候,由于自己实现了有参数的构造函数,所以不会调用默认构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1、有该类型的参数->传入,OK 。2:无该类型的参数->报错。所以保证不会为空。
MyBatis
MyBatis运行流程
MyBatis运行流程解析
- 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
- 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
- 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
- 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
- Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
- MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。【MappedStatement维护了一条<select|update|delete|insert>节点的封装】
<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap"> select id, username from author where id = #{value} </select>
- 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
- 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
MyBatis核心组件
- SqlSessionFactoryBuilder(构造器):它会根据配置或者代码来生成 SqlSessionFactory,采用的是分步构建的 Builder 模式。【SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在】
- SqlSessionFactory(工厂接口):依靠它来生成 SqlSession,使用的是工厂模式。【SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。】
- SqlSession(会话):一个既可以发送 SQL 执行返回结果,也可以获取 Mapper 的接口。在现有的技术中,一般我们会让其在业务逻辑代码中“消失”,而使用的是 MyBatis 提供的 SQL Mapper 接口编程技术,它能提高代码的可读性和可维护性。【SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。】
- SQL Mapper(映射器):MyBatis 新设计存在的组件,它由一个 Java 接口和 XML 文件(或注解)构成,需要给出对应的 SQL 和映射规则。它负责发送 SQL 去执行,并返回结果。【Mapper 是一个接口,它由 SqlSession 所创建,所以它的最大生命周期至多和 SqlSession 保持一致,尽管它很好用,但是由于 SqlSession 的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于 SqlSession 的生命周期。Mapper 代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。】
为什么要利用Spring来整合MyBatis?
1、MyBatis与Spring的整合步骤
<!--配置数据源--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/springtest?seUnicode=true&characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="1128" /> <!-- 最大连接数 --> <property name="maxTotal" value="30"/> <!-- 最大空闲连接数 --> <property name="maxIdle" value="10"/> <!-- 初始化连接数 --> <property name="initialSize" value="5"/> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 引用数据源组件 --> <property name="dataSource" ref="dataSource" /> <!-- 引用MyBatis配置文件中的配置 --> <property name="configLocation" value="classpath:mybatis-config.xml" /> </bean>
由上面可以得知,我们都知道Spring在整合Mybatis的时候都会配置一个SqlSessionFactoryBean对象来生成一个SqlSessionFactory,而这个SqlSessionFactory就是作为SqlSession(数据库会话)的关键部分,那么Spring又怎么把这个对象与DAO接口类关联放在mapperRegistry里。
@Mapper public interface AdminDao { public List<Auser> login(Auser auser); }
AdminMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- com.dao.AdminDao对应Dao接口 --> <mapper namespace="com.dao.AdminDao"> <!-- 查询用户信息 --> <select id="login" resultType="Auser" parameterType="Auser"> select * from ausertable where aname=#{aname} AND apwd=#{apwd} </select> </mapper>
使用 Spring 管理 MyBatis 数据操作接口的方式有多种,其中最常用、最简洁的一种是基于 MapperScannerConfigurer 的整合。该方式需要在 Spring 的配置文件中加入以下内容:
<!-- Mapper代理开发,使用Spring自动扫描MyBatis的接口并装配 (Sprinh将指定包中的所有被@Mapper注解标注的接口自动装配为MyBatis的映射接口) -->
<!--配置扫描,将MyBatis接口加入IoC容器中提供给外部使用--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- mybatis-spring组件的扫描器,com.dao只需要接口(接口方法与SQL映射文件中的相同) --> <property name="basePackage" value="com.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>
如果理解不了上面橙色标记的话的含义,根据上面的代码其实可以理解成:采用MapperScannerConfigurer扫描类让dao包的接口和SQL联系起来,从而实现调用者通过接口自由使用相关数据库访问操作的功能。
2、为什么要利用Spring来整合MyBatis
通过对Spring的讲解可以得知Spring的控制反转机制降低了调用者和被调用者之间的耦合性,因此将MyBatis放入Spring容器后开发者只需要进行业务处理,不需要再写 SqlSession 对象的创建、数据库事务的处理等烦琐代码,提高了开发效率。
SpringMVC
基础知识
1、MVC概念:
- 模型(Model):用于存储数据以及处理用户请求的用户逻辑
- 视图(View):向控制器提交数据,显示模型中的数据
- 控制器(Controller):根据视图提出的请求判断将请求和数据提交给哪个模型处理,将处理后的有关接结果交给哪个视图显示更新
2、基于Servlet的MVC模式
- 模型:一个或多个JavaBean对象,用于存储数据和处理业务逻辑
- 视图:一个或多个JSP页面,向控制器提交数据和为模型提供数据显示,JSP页面主要使用HTML标记和JavaBean标记显示数据
- 控制器:一个或多个Servlet对象,根据视图提交的请求进行控制,将请求转发给处理业务的JavaBean,并将处理结果放到实体模型JavaBean中,输出给视图显示
3、SpringMVC工作原理
- 客户端请求提交到DispatcherServlet;
- 由DispatcherServlet控制器寻找到一个或多个HandlerMapping,找到处理请求Controller;
- DispatcherServlet将请求提交到Controller
- Controller调用业务逻辑处理后返回ModelAndView
- DispatcherServlet心找一个或多个视图解析器,找到ModelAndView指定的视图
- 视图负责把结果显示在客户端
4、SpringMVC接口在工作流程中起的作用
- SpringMVC所有请求都经过DispatcherServlet来统一分布,在DispatcherServlet借助SpringMVC提供的HandlerMapping定位到具体的Controller。
- Controller处理完用户请求后将返回ModelAndView(包含模型和视图)对象给DispatcherServlet前端控制器。
- ViewResolver接口(视图解析器)在Web应用中查找View对象,从而将相应的结果渲染给客户
从宏观角度考虑,DispatcherServlet是整个Web应用的控制器;从微观角度考虑,Controller是单个Http请求处理过程中的控制器,而ModelAndView是Http请求过程中返回的模型和视图。
4、在ssm整合项目中SpringMvc相关:
web.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>StudentShopping</display-name> <!-- needed for ContextLoaderListener --> <!-- Spring加载ApplicationContext容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 部署DispatcherServlet --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- springmvc配置文件 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <absolute-ordering /> </web-app>
SpringMVC的配置文件内包含的 Controller映射以及视图解析器:
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd 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-4.3.xsd"> <context:component-scan base-package="com.controller" /> <mvc:annotation-driven /> <mvc:resources location="/css/" mapping="/css/**"></mvc:resources> <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> <mvc:resources location="/logos/" mapping="/logos/**"></mvc:resources> <!-- LoginController控制类将映射到“/login”接口,之后都用@RequetMapping代替 --> <bean name="/login" class="com.controller.admin.LoginController"/> <!-- 视图解析器,从InternalResourceViewResolver的后缀可以看出这个就是视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8" p:maxUploadSize="5400000" p:uploadTempDir="fileUpload/temp"> </bean> <!-- <bean class="com.exception.MyExceptionHandler"/> --> </beans>