![7872763849386cecd3da5fb18fa00d24.png](https://i-blog.csdnimg.cn/blog_migrate/b01dae2ab13cac810590de73b688d690.jpeg)
>>>Spring框架完整视频教程<<<
>>> 学习交流群 < < <
一、Spring整合Mybatis
1. 使用Servlet+MVC+Mybatis+Jsp完成用户登录
- 在mysql中创建t_user用户信息表(id,name,pwd).
- 使用idea中使用Maven搭建一个基本的war类型的项目 项目名称为 01-springioc-mybatis-login
- 在web项目中配置SpringIOC+Mybatis+Jdbc的依赖
- 在web项目中创建login.jsp登录页面,并完善发送登录请求
- 在web项目中创建UserServlet,并完善代码处理登录请求
- 在web项目中创建UserService业务,并完善代码处理登录业务
- 在web项目中创建UserMapper,并完善代码根据用户名和密码 查询用户信息
2. service层使用Spring获取mapper对象
问题:
目前我们开发功能的流程中,在service层会手动创建SQLSession对象,并使用SQLSession对象获取Mapper接口的实例化对象,但是我们真正使用的是Mapper接口的对象,目前的代码编写方式极大的影响了开发效率,而且mybatis层和service 层之间的耦合性非常高
解决:
使用SpringIOC技术实现service层和mybatis层的解耦:说白了就是让Spring容器帮我们获取Mapper层接口的实例化 对象,我们直接从Spring容器中获取使用即可.
实现:
① 配置SpringIOC的依赖以及Spring整合Mybatis的依赖
② 在src下创建并配置Spring的配置文件
- 配置DataSource数据源的bean
- 配置SQLSessionFactory的工厂bean(DataSource的bean)
- 配置mapper扫描bean对象(SQLSessionFactory对象,扫 描路径)
③ 创建Spring容器对象,并获取Mapper接口的实例化对象完成数据库操作
3. controller层使用Spring解耦service层
问题:
在业务层使用Spring容器对象获取Mapper接口实例化对象后实现了service层和mybatis层的解耦,但是在controller层我们依然在Servlet 中直接创建Service对象,耦合性过高.
解决:
将service对象配置为bean对象,Servlet中从Spring容器中获取Service对象,完成功能开发.
实现:
- 在applicationcontext.xml文件中配置service的bean
- 在servlet中的service方法中创建Spring容器对象
- 在servlet中的service方法中从Spring容器中获取 service对象
- 使用service对象完成功能处理
4. MVC项目中使用SpringIOC解耦的代码优化
问题:
目前的整合中,tomcat服务在接收到请求后,会创建一个线程来处理请求,每个线程之间都是相互独立运行.流程如下:
- 浏览器发起登录请求
- tomcat服务器获取请求,创建新的线程处理请求
- 调用请求的Servlet对象中的service方法
- Servlet中的service方法被执行,service方法进栈.然后创建Spring容器对象,内 存堆中就会出现一个Spring容器对象s1,而s1被创建的时候,会加载spring的配 置文件,根据配置文件创建对象(dataSource,factory,mapper,us.......).
- 获取Spring容器中的业务层对象us
- 调用us对象的登录业务方法
- 登录业务方法进栈,又会创建一个新的Spring容器对象s2,而s2被创建的时候,又 加载解析Spring的配置文件,又创建dataSource,factory,us,mapper的bean对 象.
- 在业务的登录方法中获取s2容器对象中的mapper对象完成 数据库操作,执行 业务处理,登录业务方法处理完毕,返回结果 给controller的service方法,然后 业务方法出栈.
- servlet的service方法获取到业务层的返回值后,继续处理,然后响应处理结果给浏 览器,service方法结束,service方法出栈,请求处理完毕,线程销毁.
通过以上流程发现,目前的代码的结构,一个线程中会存在除Servlet对象以外至少还有10个对象,一旦高并发访问,会造成内存资源的极大的浪费,造成项目崩溃.
解决:
优化代码中的资源占比.
实现:
- 使用依赖注入的方式在Service对象中获取Mapper对象
- 在Servlet中使用init方法完成Spring容器对象的创建及其资源加载
5. 完整代码示例
5.1.创建Maven的war类型的项目并完成基础配置
5.2.在pom.xml文件中配置相关依赖
- web的依赖
- mybatis的依赖
- jdbc的依赖
- SpringIOC的依赖
- Spring整合Mybatis的依赖
- spring-jdbc的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt</groupId>
<artifactId>03_SpringIOC_Mybatis_Login</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<!--配置版本号-->
<properties>
<servlet-version>3.1.0</servlet-version>
<jsp-version>2.2</jsp-version>
<jstl-version>1.2</jstl-version>
<mybatis.version>3.5.2</mybatis.version>
</properties>
<!--配置依赖-->
<dependencies>
<!--配置Mybatis的依赖-->
<dependency>
<!--资源坐标-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--配置JDBC的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.23</version>
</dependency>
<!--Spring整合Mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--配置SpringIOC的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<!--配置web开发相关的依赖-->
<!--servlet的资源坐标-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-version}</version>
<scope>provided</scope>
</dependency>
<!--jsp的资源坐标-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-version}</version>
<scope>provided</scope>
</dependency>
<!--jstl的资源坐标-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl-version}</version>
</dependency>
</dependencies>
<!--配置tomcat插件-->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port><!--配置tomcat启动的端口号-->
<path>/SMlogin</path><!--配置项目的访问名称-->
</configuration>
</plugin>
</plugins>
</build>
</project>
5.3.在resources下创建并配置applicationcontext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--创建数据源bean对象-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/s105"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!--配置SqlSession的工厂bean-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--依赖注入数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--设置实体类的路径,相当于mybatisx.xml文件中的typeAliases配置-->
<property name="typeAliasesPackage" value="com.bjsxt.pojo"></property>
</bean>
<!--
配置Mapper扫描
Spring容器对象在被创建的时候会加载applicationcontext.xml文件
并根据文件中的bean标签将所有的bean对象初始化创建:
Spring容器中的对象:
DataSource对象
SqlSessionFactoryBean对象(DataSource对象)
MapperScannerConfigurer对象(SqlSessionFactoryBean对象)
Spring容器对象底层会自动调用MapperScannerConfigurer对象中的资源完成SqlSession对象的
生产,以及使用生产的SqlSession对象完成mapper层接口的扫描,将动态生成的Mapper层接口的
实例化对象存储到Spring容器中,默认mapper接口实例化的键名为接口名的首字母小写,例如:userMapper
我们从Spring容器中直接获取Mapper接口对象并完成响应的数据库操作即可。
-->
<bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--依赖注入factory-->
<property name="sqlSessionFactory" ref="factory"></property>
<!--配置mapper扫描路径-->
<property name="basePackage" value="com.bjsxt.mapper"></property>
</bean>
<!--配置业务层的bean对象-->
<bean id="us" class="com.bjsxt.service.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"></property>
</bean>
</beans>
5.4.声明登录的jsp页面
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<base href="<%=basePath %>"/>
<title>Title</title>
</head>
<body>
<h3>Spring整合Mybatis登录案例</h3>
<hr>
<c:if test="${sessionScope.flag=='loginFail'}">
<font color="red" size="20">用户名或密码错误</font>
</c:if>
<c:remove var="flag" scope="session"></c:remove>
<form action="userServlet" method="post">
用户名: <input type="text" name="uname" value=""><br>
密码: <input type="password" name="pwd" value=""><br>
<input type="submit" value="点击登录">
</form>
</body>
</html>
5.5.声明controller层代码UserController
package com.bjsxt.controller;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
import com.bjsxt.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet(value = "/userServlet",loadOnStartup = 1)
public class UserServlet extends HttpServlet {
//声明业务层属性
UserService us;
/**在init方法中完成资源初始化,init方法在Servlet被初始化创建的时候执行,
* 而Servlet只会被初始化创建一次,也就是init方法只会执行一次。
* init的方法执行是先于service方法的执行的。也就说:
*
* Servlet对象被初始化创建的时候,触发init方法的执行,而init方法的在被执行的时候
* 会创建Spring容器对象,并从中获取到业务层的实例化对象,赋值给Servlet的属性。
* init方法执行完毕出栈,然后Spring容器对象及Spring容器对象创建的其他不相关的对象
* 都会被销毁。用户发起请求,请求Servlet时,Servlet调用service方法,service方法中
* 调用业务层属性完成业务处理。
*
*
@Override
public void init() throws ServletException {
//获取Spring容器对象
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationcontext.xml");
//获取业务层对象
us= (UserService) ac.getBean("us");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求编码格式
req.setCharacterEncoding("utf-8");
//设置响应编码格式
resp.setContentType("text/html;charset=utf-8");
resp.setCharacterEncoding("utf-8");
//获取请求信息
String uname=req.getParameter("uname");
String pwd=req.getParameter("pwd");
//处理请求
//登录处理
User user = us.userLoginService(uname, pwd);
//响应结果
HttpSession session = req.getSession();
if(user!=null){
//登录成功 重定向到主页面
session.setAttribute("user",user);
resp.sendRedirect(req.getContextPath()+"/main.jsp");
}else{
//登录失败
session.setAttribute("flag","loginFail");
resp.sendRedirect(req.getContextPath()+"/login.jsp");
}
}
}
5.6.声明业务接口及其实现类
- UserService接口
package com.bjsxt.service;
import com.bjsxt.pojo.User;
import java.io.IOException;
public interface UserService {
//用户登录
User userLoginService(String uname, String pwd);
}
- UserServiceImpl实现类
package com.bjsxt.service.impl;
import com.bjsxt.mapper.UserMapper;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
public class UserServiceImpl implements UserService {
//声明Mapper层属性
private UserMapper userMapper;
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
//用户登录
@Override
public User userLoginService(String uname, String pwd){
//调用mapper方法根据用户名和密码获取用户信息
User user = userMapper.userLoginMapper(uname, pwd);
return user;
}
}
5.7.声明pojo层
package com.bjsxt.pojo;
import java.util.Objects;
public class User {
private Integer uid;
private String uname;
private String pwd;
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", uname='" + uname + ''' +
", pwd='" + pwd + ''' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(uid, user.uid) &&
Objects.equals(uname, user.uname) &&
Objects.equals(pwd, user.pwd);
}
@Override
public int hashCode() {
return Objects.hash(uid, uname, pwd);
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public User() {
}
public User(Integer uid, String uname, String pwd) {
this.uid = uid;
this.uname = uname;
this.pwd = pwd;
}
}
5.8.声明mapper层代码
package com.bjsxt.mapper;
import com.bjsxt.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
//根据用户名和密码获取用户信息
@Select("select * from t_user where uname=#{uname} and pwd=#{pwd}")
User userLoginMapper(@Param("uname") String uname, @Param("pwd") String pwd);
}
二、SpringIOC的创建对象的单例多例模式和自动注入
1. SpringIOC创建对象的单例和多例模式
问题:
Spring容器对象根据配置文件创建对象的时机默认发生在Spring容器对象在被创建的时候,也就是说,我们一旦获取到Spring容器对象,意味着可以直接获取Spring容器中的对象使用了.那么,如果我对同一个bean对象,连续获取N次,获取到的是不是同一个对象呢?因为spring容器对象底层使用的是map集合存储的bean对象,对map集合按照同一个键名获取数据,获取的是同一个,也就说按照同一个键名从Spring容器中获取的都是同一个对象,那么如果我们希望相同的键名获取的对象每次都不一样,怎么实现?
解决:
不要在Spring容器对象创建的时候,就完成对象的初始化创建,而是变为,从Spring容器中获取的时候才创建,每次获取都重新创建.
实现:
Spring容器的单例和多例模式创建对象.
单例模式(默认):
设置了单例模式的bean,会在Spring容器对象被创建的时候就完成初始化创建,无论获取多少次都是同一个对象.
多例模式:
设置了多例模式的bean,在Spring容器对象被创建的时候不会被初 始化创建,每次获取的时候都会重新创建,每次获取的对象都不相同.
使用:
- applicationcontext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
SpringIOC设置对象的单例模式和多例模式创建对象
单例模式:默认模式,在bean标签上使用scope属性,默认值为singleton
多例模式:在bean标签上使用scope属性设置,值为prototype
-->
<bean id="stu" class="com.bjsxt.pojo.Student" scope="singleton"></bean>
<bean id="tea" class="com.bjsxt.pojo.Teacher" scope="prototype"></bean>
</beans>
- TestIocModel
package com.bjsxt.controller;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.rmi.CORBA.StubDelegate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author wuyw2020
* @date 2020/1/8 20:55
*/
@WebServlet(value = "/user", loadOnStartup = 1)
public class UserServlet extends HttpServlet {
public static void main(String[] args) {
//获取Spring容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationcontext.xml");
Student stu =(Student) ac.getBean("stu");
Student stu2 =(Student) ac.getBean("stu");
Teacher teacher = (Teacher) ac.getBean("stu");
Teacher teacher2 = (Teacher) ac.getBean("stu");
System.out.println("学生:"+(stu==stu2));
System.out.println("教室:"+(teacher==teacher2));
}
}
2. SpringIOC的自动注入
问题:
在学习了SpringIOC的DI依赖注入后,我们可以根据对象之间的依赖关系的链,让Spring容器对象帮我们创建有一个组装好的对象,比如A中有B,B中有C,C中有D.将A,B ,C,D都创建为Bean对象,然后使用ref属性告诉Spring对象之间的依赖关系的组装规则,假如依赖责任链特别长,使用ref注入就会很麻烦很麻烦.怎么办?
解决:
不要声明ref属性来指明依赖关系的注入,只需要告诉Spring容器对象依赖关 系的注入规则,Spring容器对象自动根据规则完成依赖关系的注入.
实现:
- 根据bean的ID和属性名一致的规则
- 根据bean的类型和属性的类型一致的规则
- 根据构造器形参的类型和bean的类型一致的规则
- 不能使用自动注入,只能手动声明
- 使用默认规则
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName"><!--设置自动注入-->
<!--
自动注入:
在标签上使用autowire属性设置自动注入规则.该属性声明在要注入资源的bean标签上,比如:学生的bean
autowire属性的值:
byName:按照属性名和某个bean的id相同的规则自动注入
byType:按照属性的类型和某个bean的类型相同的规则自动注入.
注意:同类型的bean只能有一个,否则报错.
constructor:
必须声明对应的构造器,根据构造器形参的类型和某个bean的类型相同的规则自动注入.
no:不使用自动注入,必须使用ref手动注入.
default:使用默认注入规则.默认值.
注意:
可以在beans顶层元标记中使用属性default-autowire声明全局自动注入规则.
-->
<bean id="stu" class="com.bjsxt.pojo.Student" autowire="byName">
<!--用构造器的方法,此处要创建一个只有Teacher一个参数的构造器-->
<!--<constructor-arg index="0" type="com.Teacher" ref="tea"></constructor-arg>-->
<property name="sid" value="1"></property>
<property name="sname" value="张三"></property>
</bean>
<bean id="teacher" class="com.bjsxt.pojo.Teacher">
<property name="tname" value="liu"></property>
<property name="tid" value="1"></property>
</bean>
</beans>
有什么疑问或者不懂的地方进群讨论哦,欢迎大家一起学习~