从Servlet讲到SpringMVC
1、MVC软件设计典范
Model(模型)——View(视图)——Controller(控制器)
之前Servlet的MVC设计模式其实是不太符合严格的MVC设计模式的,因为Controller控制器里不仅需要用servlet获取前端请求的参数,还要进行转发,而且有的时候servlet还要打开数据库流然后进行获取数据然后进行增删改查再返回,最后在JSP页面里还要用Java语句进行遍历或者赋值之类的,相当于很杂糅,控制器里既要进行模型层的操作,视图层还要进行控制器层的操作,就特别杂糅。
并且那个时候所有的压力基本上都在Contrller上,所以服务器端的压力特别大,后来有了新的SpringMVC就让用户端也分担小部分压力,同时也基本上实现了真正的前后端分离。
2、回顾Java Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
在JSP(Java Server Pages)里的Servlet:
所以如果我们有很多个页面或者很多种业务的话就需要N多个Servlet,并且没创建一个Servlet就还需要到web.xml里面去注册Servlet。
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//取得参数
String method = req.getParameter("method");
if (method.equals("add")){
req.getSession().setAttribute("msg","执行了add方法");
}
if (method.equals("delete")){
req.getSession().setAttribute("msg","执行了delete方法");
}
//业务逻辑
//视图跳转
req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
我们以前是在Servlet里面获取的HttpServletRequest对象然后用request.getParameter获取参数,最后再转发或者重定向到JSP页面。
而在SpringMVC里实质上就是加了一个最顶层的dispatcherServlet来获取用户请求,然后交给多个Controller来处理再响应给用户。
dispatcherServlet简单原理:
3、创建Maven项目并且集成Tomcat
我用的编译器是IDEA,IDEA集成Tomcat方法:https://www.cnblogs.com/weixinyu98/p/9822048.html
注意选择骨架的使用选webapp然后我们再手动导入SpringMVC需要的相关依赖:
创建完成:
同时我们也需要创建src/main/resources资源文件夹用来放Spring和Mybatis的配置文件,还要创建src/main/java来放Java代码。创建好之后我们来启动Tomcat测试一下没有集成成功。
测试成功:
这时候我们就可以安心的来写自己的Java代码和配置文件了,起码不用担心Tomcat的问题了。
4、在pom.xml里引入相关依赖
因为我们需要跟Mybatis整合,所以需要mybatis和mybatis-spring依赖;如果需要Junit单元测试还需要Junit依赖;还有spring-webmvc依赖,这个依赖就自动包含了spring的绝大部分依赖,因为我们需要用servlet,所以还需要导入servlet的相关依赖,因为要前后端分离所以还需要导入jstl依赖;因为需要连接到数据库还需要MySQL驱动依赖
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.23</version>
</dependency>
<!--druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.7</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.9.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
5、在web.xml里注册dispatcherServlet
因为SpringMVC的核心就是dispatcherServlet,所以我们就立马去web.xml里注册它,而且也只需要注册它一个Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 绑定Springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--启动级别-1,跟随服务器一起启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
/:匹配所有页面,不包括jsp页面(推荐)
/*:匹配所有页面,包括jsp页面
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--2.字符编码过滤器的Servlet-->
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
还注册一个filter来过滤中文乱码。
6、我们依旧是对应数据表建立一个pojo类
以及有参、无参构造方法,toString()方法,getter和setter方法
其中数据表里uid自增
7、然后我们再写Mapper接口进行增删改查
UserMapper.java:
public interface UserMapper {
//查询所有用户
List<User> selectAllUser();
//根据id查询用户
User selectUserByUid(int id);
//新增用户
int insertUser(User user);
}
写了两个需求,分别是查询所有用户,根据id查询用户,新增用户
8、写mapper.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">
<mapper namespace="cn.wqk.mapper.UserMapper">
<select id="selectAllUser" resultType="User">
SELECT
uid,username,password,age
FROM
user
</select>
<select id="selectUserByUid" parameterType="integer" resultType="User">
SELECT
uid,username,password,age
FROM
user
WHERE
uid=#{uid}
</select>
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="uid">
INSERT INTO
user
(username,password,age)
VALUES
(#{username},#{password},#{age})
</insert>
</mapper>
三个标签分别对应三个增删改查的要求。
9、Mybatis配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="cn.wqk.pojo" />
</typeAliases>
<!-- 配置Mapper映射文件地址 -->
<mappers>
<mapper class="cn.wqk.mapper.UserMapper"/>
</mappers>
</configuration>
10、仍然是获取sqlSession对象
用spring-dao.xml配置文件来专注于获取SqlSession对象,当然在这之前,我们仍然需要我们的db.properties数据库连接配置文件
<?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"
xsi:schemaLocation="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.xsd">
<!--引入db.properties文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 1、dataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2、sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 引入mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 3、sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
11、写Mapper接口的实现类,将他与SqlSession封装
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectAllUser() {
return getSqlSession().getMapper(UserMapper.class).selectAllUser();
}
@Override
public User selectUserByUid(int id) {
return getSqlSession().getMapper(UserMapper.class).selectUserByUid(id);
}
@Override
public int insertUser(User user) {
return getSqlSession().getMapper(UserMapper.class).insertUser(user);
}
}
依旧是需要将他的实现类去实现UserMapper接口并且继承SqlSessionDaoSupport,就会有getSqlSession()方法直接获取SqlSession对象了。
12、这个时候我们就需要一个spring-service.xml配置文件来专注于扫描并且获得pojo包、service包和UserMapper的实现
<?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"
xsi:schemaLocation="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.xsd">
<context:component-scan base-package="cn.wqk.pojo"/>
<context:component-scan base-package="cn.wqk.service"/>
<bean id="UserMapper" class="cn.wqk.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
13、spring-mvc.xml配置文件来专注于配置springMVC相关东西
特别是这个视图解析器
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="cn.wqk.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
14、最后我们把spring-dao,spring-service,spring-mvc丢入到applicationContext容器里交给Spring统一管理
<?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"
xsi:schemaLocation="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.xsd">
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
</beans>
到这个时候我们的配置文件基本就配置完成了,以后任何的SpringMVC就可以直接使用这一套配置了,但是SpringMVC仍然是”配置地狱“,所以后面才有了SpringBoot来简化配置。
15、这个时候我们就进入最后一层面向用户的地方了,业务层service
写业务接口:
UserService.java:
public interface UserService {
//查询所有用户但是只输出他们的用户名
List<String> selectAllUsername();
//根据id查询用户只输出名字
String selectUsernameByUid(int uid);
}
因为Mapper只是对数据库进行增删改查,真正的业务不可能只是对数据进行简单的增删改查,还需要对增删改查的数据进行进一步封装,这就是业务service,比如分页之类的,上面两个需求就是将他们查询到的东西进行封装只输出名字,具体的要求得看公司的上级给你的要求是什么,然后你再自己进行封装。因为我们只需要返回一组名字,所以就返回泛型的String,下面同理。
16、实现service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public List<String> selectAllUsername() {
List<User> userList = userMapper.selectAllUser();
List<String> usernameList=new ArrayList<>();
for (int i=0;i<userList.size();i++){
usernameList.add(userList.get(i).getUsername());
}
return usernameList;
}
@Override
public String selectUsernameByUid(int uid) {
User user = userMapper.selectUserByUid(uid);
String username=user.getUsername();
return username;
}
}
因为UserMapper被我们丢到了Spring容器里边,所以我们可以用@Autowired来自动装配他,而不是需要new一个。重写UserService的方法,就将查询到的数据进行进一步封装然后返回得到我们需要的数据。同时也可以写个Test来测试一下我们的Service有没有写正确。
17、我们创建一个jsp页面来测试一下
在WEB-INF下面创建一个jsp包,然后创建一个test.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
用EL表达式${msg}
来获取到我们返回的msg的值,也可以用JSTL来处理,甚至可以用HTML然后用JavaScript来获取数据返回给前端实现真正的前后端分离。
18、控制器controller
控制器就是SpringMVC的重中之重。
在src/main/java里创建cn.wqk.controller包专门存放controller
SpringMVC里控制器的几种实现方式以及Restful风格:
https://blog.youkuaiyun.com/weixin_45747080/article/details/106026230
我们先来写一个TestController试一下:
TestController.java:
@Controller
@RequestMapping("test")
public class TestController {
@Autowired
UserService userService;
@RequestMapping("selectAllUsername")
public String t1(Model model){
List<String> stringList = userService.selectAllUsername();
model.addAttribute("msg",stringList);
return "test";
}
@RequestMapping("selectUsernameByUid")
public String t2(@RequestParam("uid") int uid, Model model){
String username = userService.selectUsernameByUid(uid);
model.addAttribute("msg",username);
return "test";
}
}
因为我们需要用到自动装配UserService,所以请一定切记要在UserService和UserServiceImpl里加上@Service
注解,然后我们就可以直接用@Autowired
直接用了。
就相当于在地址栏请求http:/*********/test/selectAllUsername
就会执行t1里面的方法,这个方法是调用了UserService里的selectAllUsername方法返回的是名字集合然后存到了msg这个对象里,执行完了之后就会返回到WEB/INF/test.sjp
然后用EL表达式显示msg对象里的数据
同理请求selectUsernameByUid也是,只是需要传递一个Uid参数而已,我们也把他存到msg对象里,也返回到test.jsp就实现了jsp页面的复用。
19、测试
所以我们现在只需要把数据丢给前端,让前端帮我们用html或者Jsp渲染然后实现好看的页面,就真正做到了前后端分离!