0 复习
-
工厂设计模式
工厂设计模式代替new方式创建对象,目的是解耦合。
-
Spring做为工厂的使用
applicationContext.xml配置bean标签
如何从工厂中获取对象
//创建工厂 ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); //从工厂中获取bean ctx.getBean("id值");
-
属性注入(Spring工厂创建对象的同时,为属性赋值)
-
set注入
-
简单类型
<bean id="id值" class="全类名"> <property name="属性名" value="简单属性值"/> </bean>
-
复杂自定义类型
<bean id="id值" class="全类名"> <property name="属性名" ref="另外一个bean的id"/> </bean>
-
数组、List、Set
<bean id="id值" class="全类名"> <property name="属性名"> <array> <value>简单值</value> <ref bean="另外一个bean的id"/> ... </array> </property> </bean> <bean id="id值" class="全类名"> <property name="属性名"> <list> <value>简单值</value> <ref bean="另外一个bean的id"/> ... </list> </property> </bean> <bean id="id值" class="全类名"> <property name="属性名"> <set> <value>简单值</value> <ref bean="另外一个bean的id"/> ... </set> </property> </bean>
-
Map
<bean id="id值" class="全类名"> <property name="属性名"> <map> <entry key="键的值" value="简单值"/> <entry key="键的值" value—ref="另外一个bean的id"/> </map> </property> </bean>
-
Properties
<bean id="id值" class="全类名"> <property name="属性名"> <props> <prop key="字符串的键">字符串的值</prop> <prop key="字符串的键">字符串的值</prop> ... </props> </property> </bean>
-
-
构造注入
<bean id="id值" class="全类名"> <constructor-arg value="简单值"/> <constructor-arg ref="另外一个bean的id"/> </bean> <bean id="id值" class="全类名"> <constructor-arg value="简单值" type="值的类型" index="0"/> <constructor-arg ref="另外一个bean的id" type="值的类型" index="1"/> </bean>
-
1 注入补充【了解】
1.1 注入null值
需要显式的为一个属性赋null值时,需要使用null子标签。
<bean id="addr" class="com.bz.entity.Address">
<constructor-arg value="硅谷"/>
<constructor-arg><null></null></constructor-arg>
</bean>
1.2 内部bean
当一个bean只为另外一个bean使用时,可以写成内部bean的形式。
2 FactoryBean技术(创建复杂对象)
当Spring工厂需要创建一些无法通过new直接创建的对象,就需要使用FactoryBean技术创建复杂对象。
通过完成Spring创建JDBC中Connection的需求,演示FactoryBean的使用步骤:
准备工作:pom.xml导入mysql-connector-java依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
-
定义一个用于创建复杂对象的类,必须实现FactoryBean接口
public class ConnectionFactoryBean implements FactoryBean<Connection> { @Override // 用于返回 创建的复杂对象 public Connection getObject() throws Exception { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/bz?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"; String username = "root"; String password = "root"; return DriverManager.getConnection(url,username,password); } @Override //返回 复杂对象的类型(类对象) public Class<?> getObjectType() { return Connection.class; } @Override //决定复杂对象是不是单例 public boolean isSingleton() { return false; } }
-
在applicationContext.xml中配置上一步定义的FactoryBean
<!-- getBean("conn")获取不是ConnectionFactoryBean类型的对象,获取getObject()返回的复杂对象 --> <bean id="conn" class="com.bz.factory.ConnectionFactoryBean"/>
3.测试
@Test
public void a(){
ApplicationContext ac= new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object o1=ac.getBean("conn");
Object o2=ac.getBean("conn");
Object o3=ac.getBean("conn");
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);
}
注意:
- 复杂对象的单例控制,由isSingleton()方法返回值决定
- 通过getBean(“&id属性”),可以获取自定义的FactoryBean对象
3 Spring的IOC和DI
IOC(Inversion of Control)控制反转、反转控制。
DI(Dependency Injection)依赖注入
控制:对于类中属性的赋值的控制权。
传统的正向控制:
正向控制存在的问题:类和类之间强耦合。
IOC和DI对同一件事,不同角度的描述。IOC更加偏重思想,DI更加偏重于实现手段。
IOC:属性的赋值权力从代码反转到Spring框架中。
DI:Spring通过依赖注入完成属性赋值。
3 Spring配置文件的拆分
3.1 applicationContext.xml的拆分
一个复杂的企业应用会拆分成多个模块,不同模块中有各自的spring配置,最终还需要聚合在一起。
<import resource="classpath:datasource/a.xml"/>
classpath:JVM寻找类的起始路径,可以认为java、resources都是classpath。
3.2 Spring配置文件的xsd
XML的格式约束文件有2种:DTD(Document Type Definition)和XSD(XML Schema Definition)。XSD和DTD一样用来约束配置文件,DTD编写简单,XSD功能强大。
一般地,简单的配置文件用dtd约束(如Struts2的配置文件,MyBatis的配置文件),复杂的配置文件使用xsd约束(如Spring)。
-
xsd的基本使用
-
在一个配置文件中使用多个xsd
Spring不同的模块定义了不同的xsd文件,在一个Spring配置文件中使用多个模块的功能,也就要在配置文件中使用多个xsd。
使用步骤:
- 在跟标签中添加xmlns:前缀=“其它xsd的命名空间”
- 在xsi:schemaLocation=“添加路径对”
- 使用其它xsd中定义的标签需要添加
前缀:
示例:
3.3 Spring 配置文件properties的拆分
程序中关于数据库的参数配置,一般都抽取到jdbc.properties文件中。Spring的applicationContext.xml中关于数据库的配置也一样要抽取。
-
将数据库参数抽取到jdbc.properties中
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/bz?useUnicode=true&characterEncoding=utf-8 user=root password=root
-
在Spring的配置文件中读取jdbc.properties
<!-- 读取配置文件 使用context.xsd定义property-placeholder标签读取jdbc.properties文件--> <context:property-placeholder location="classpath:jdbc.properties"/>
-
在需要使用参数的地方,通过${参数名}获取参数值
<bean id="conn" class="com.bz.factory.ConnectionFactoryBean" > <property name="url" value="${url}"/> <property name="driverClassName" value="${driverClassName}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </bean>
注意:${username}会优先读取操作系统的用户名,jdbc.properties中必须改名
4 Spring整合MyBatis
4.1 整合效果分析
首先,回顾下MyBatis项目开发步骤:
-
搭建开发环境
-
新建项目
-
导入依赖
数据库驱动依赖:mysql-connector-java.jar
MyBatis相关的依赖
servlet+jsp+jstl的依赖
junit+hutool+druid
-
配置文件+工具类
jdbc.properties
log4j.properties
xxxMapper.xml
mybatis-config.xml
- 配置文件初始化
mybatis-config.xml 初始化配置
-
-
建表
-
实体
-
mapper
- 接口
- 实现:XxxMapper.xml
-
service
-
接口
-
实现:
SqlSession sqlSession = MyBatisUtils.openSession();
XxxMapper mapper = sqlSession.getMapper(XxxMapper.class);
-
-
test
Spring整合MyBatis的效果就是使用Spring的工厂功能创建MyBatis项目中需要的关键对象:Dao对象和Service对象。
4.2 整合思路分析
整合的关键在于如何在applicationContext.xml中配置,由Spring工厂创建Dao对象。
思路:原dao的编码创建过程中需要创建哪些对象,在Spring中也需要创建哪些对象,只不过编码方式转为Spring的配置。原来编码创建的对象,在Spring中以bean标签的形式体现。
4.3 整合实战
准备工作,添加 mybatis-spring依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!-- 整合spring mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
初版配置:
<!-- 读取jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 0 定义一个连接池 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 定义SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<!--
配置实体类的包名,自动为实体配置短类名的别名
-->
<property name="typeAliasesPackage" value="com.bz.entity"/>
<property name="mapperLocations">
<!-- 配置mapper.xml的路径-->
<list>
<!--<value>classpath:com/bz/mapper/UserMapper.xml</value>
<value>classpath:com/bz/mapper/StudentMapper.xml</value>
<value>classpath:com/bz/mapper/BookMapper.xml</value>-->
<value>classpath:com/bz/mapper/*Mapper.xml</value>
</list>
</property>
</bean>
<!-- 定义SqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
<!-- 创建 UserMapper实现类对象-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionTemplate" ref="sqlSession"/>
<property name="mapperInterface" value="com.bz.mapper.UserMapper"/>
</bean>
<!--<bean id="bookMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionTemplate" ref="sqlSession"/>
<property name="mapperInterface" value="com.bz.mapper.BookMapper"/>
</bean>-->
<bean id="userService" class="com.bz.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
最终配置:
<!-- 读取jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 0 定义一个连接池 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 定义SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<!--
配置实体类的包名,自动为实体配置短类名的别名
-->
<property name="typeAliasesPackage" value="com.bz.entity"/>
<property name="mapperLocations">
<!-- 配置mapper.xml的路径-->
<list>
<value>classpath:com/bz/mapper/*Mapper.xml</value>
</list>
</property>
</bean>
<!--
自动创建Mapper实现类对象
自动扫描basePackage包下的Mapper接口,自动创建Mapper接口的实现类对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
mapper实现类对象的id规则:接口名首字母小写
UserMapper ==> userMapper
BookMapper ==> bookMapper
-->
<property name="basePackage" value="com.bz.mapper"/>
</bean>
<bean id="userService" class="com.bz.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
注意:Service中的编码式事务控制,对由Spring创建的Dao无效。所以,我们删除service中事务控制代码,暂时以无事务的状态运转。
5 Spring整合Servlet
5.1 整合效果分析
预期效果:Servlet中获取Service对象的方式从new改为从工厂中获取。
5.2 整合方案
Spring整合Servlet的关键在于从Spring工厂中获取Service对象,这就必须先解决2个问题:
- 如何创建Spring工厂
- 何时创建Spring工厂
方案1: 直接在Servlet的服务方法中,创建工厂。
public class XxxController extends HttpServlet{
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 收参
...
//调用业务层方法
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
XxxService xxxService = (XxxService) ctx.getBean("xxxService");
//跳转
...
}
}
存在的问题:每请求一次服务方法,就重新创建1次工厂(工厂是重量级对象,占用内容空间较大),性能较差
方案2:将工厂做成属性
public class XxxController extends HttpServlet{
private ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 收参
...
//调用业务层方法
XxxService xxxService = (XxxService) ctx.getBean("xxxService");
//跳转
...
}
}
存在的问题:每一个Servlet独有一个工厂属性,有多少Servlet就会有多少Spring工厂,性能仍待提升
方案3:工厂应该全局共享,整个web应用中只创建一个工厂
public class bzWebApplicationContextUtils{
private static ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
public static ApplicationContext getWebApplicationContext(){
return ctx;
}
}
public class XxxController extends HttpServlet{
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 收参
...
//调用业务层方法
ApplicationContext ctx = bzWebApplicationContextUtils.getWebApplicationContext();
XxxService xxxService = (XxxService) ctx.getBean("xxxService");
//跳转
...
}
}
存在的问题:第1次访问Servlet在调用WebApplicationContextUtils时需要创建工厂,工厂的创建比较耗时。第1次请求的响应会比较耗时,用户体验差。
方案4:可以在Web应用一启动时就提前创建Spring工厂,在ServletContextListener中监听到Web应用的启动
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ServletContext servletContxt = sec.getServletContext();
servletContext.setAttribute("ctx",ctx);
}
}
public class bzWebApplicationContextUtils{
public static ApplicationContext getWebApplicationContext(ServletContext sc){
return (ApplicationContext).getAttribute("ctx");
}
}
public class XxxController extends HttpServlet{
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 收参
...
//调用业务层方法
ApplicationContext ctx = bzWebApplicationContextUtils.getWebApplicationContext(req.getServletContext());
XxxService xxxService = (XxxService) ctx.getBean("xxxService");
//跳转
...
}
}
方案5(重点):和上一个方案的思路一致,只不过使用Spring-Web模块内置的监听器和工具类。
准备工作,pom.xml添加 spring-web依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
web.xml
<!-- 设置applicationConext.xml的路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 配置监听器:在web应用启动时,自动的创建工厂并保存到ServletContext中-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
public class XxxController extends HttpServlet{
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 收参
...
//调用业务层方法
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(req.getServletContext());
XxxService xxxService = (XxxService) ctx.getBean("xxxService");
//跳转
...
}
}
方案1~方案4为思路过渡方案,最终在理解各种方案的优缺点后,掌握最终的标准方案即可。
6 总结:SM项目开发步骤
-
搭建开发环境
-
新建web项目(补全项目结构)
-
导入依赖,pom.xml引入依赖
数据库依赖:
mysql-connector-java.jar
druid
spring依赖:
spring-context
mybatis依赖:
mybatis
slf4j-log4j12
mybatis和spring整合
spring-jdbc
mybatis-spring
servlet+jsp+jstl依赖
servlet-api
jsp-api
jstl
springmvc依赖
spring-webmvc
hutool工具
hutool-all
-
配置文件和工具类
jdbc.properties
lo4j.properties
mybatis-config.xml(不再需要)xxxMapper.xml
web.xml
applicationContext.xml
MyBatisUtils.java(不再需要) -
配置文件初始化
web.xml中配置Spring监听器,创建Spring工厂
-
-
建表
-
实体
-
dao
- 接口
- 实现: mapper.xml中定义sql语句
-
service
- 接口
- 实现:暂不提供事务控制(删除掉事务控制的代码)
-
test
-
Controller+jsp
Controller:从ServletContext中获取工厂,再从工厂中获取Service对象
-
集成测试
pom.xml
<!-- jdbc依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- 阿里巴巴连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.24</version>
</dependency>
<!--引入Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
<!-- spring 整合 mybatis 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!-- servlet jsp jstl 依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- SpringMVC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!--
hutool工具类
-->
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.3</version>
</dependency>
<!-- junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.12</version>
</dependency>
web.xml
<!-- 配置spring配置文件的路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 监听器:监听web应用启动,根据上面配置的spring配置文件路径创建Spring工厂-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
applicationContext.xml
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 创建连接池 DataSource -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 必须的配置 -->
<property name="url" value="${url}"/>
<property name="driverClassName" value="${driverClassName}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<!-- 额外的配置-->
</bean>
<!-- 定义SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<!--
配置实体类的包名,自动为实体配置短类名的别名
-->
<property name="typeAliasesPackage" value="com.bz.entity"/>
<property name="mapperLocations">
<!-- 配置mapper.xml的路径-->
<list>
<value>classpath:com/bz/mapper/*Mapper.xml</value>
</list>
</property>
</bean>
<!--
自动创建Mapper实现类对象
自动扫描basePackage包下的Mapper接口,自动创建Mapper接口的实现类对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--
mapper实现类对象的id规则:接口名首字母小写
UserMapper ==> userMapper
BookMapper ==> bookMapper
-->
<property name="basePackage" value="com.bz.mapper"/>
</bean>
<!-- 定义service对象-->
<bean id="userService" class="com.bz.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
<!--
配置实体类的包名,自动为实体配置短类名的别名
-->
<property name="typeAliasesPackage" value="com.bz.entity"/>
<property name="mapperLocations">
<!-- 配置mapper.xml的路径-->
<list>
<value>classpath:com/bz/mapper/*Mapper.xml</value>
</list>
</property>
```
文章对应源代码地址: https://download.csdn.net/download/qq_36827283/87382597