在 Web 項目中使用 Spring 框架,首先要解決在 web 層(這里指 Servlet)中獲取到 Spring容器的問題。只要在 web 層獲取到了 Spring 容器,便可從容器中獲取到 Service 對象
一、Web項目中使用Spring
1. 新建一個Maven項目
此時選擇的就是maven-archetype-webapp
2. 使用之前的案例
還是使用Spring集成MyBatis那個案例的代碼,目錄如下
- service層、Dao層,domain全部代碼復制
- 配置文件applicationContext.xml、jdbc.properties,mybatis.xml,復制
- pom.xml、主要新增加入servlet,jsp依賴
這里還是直接把整個的pom.xml文件放在下面
<?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.md</groupId>
<artifactId>10-spring-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--spring事務用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis的-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring集成的-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--德魯伊,數據庫連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- servlet依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依賴 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
<!--為了使用監聽器對象,加入依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<!--目的是把src/main/java目錄中的xml文件包含到輸出結果中,也就是輸出到classes目錄中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目錄-->
<includes><!--包括目錄下的.properties,.xml 文件都會掃描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
3. 定義index頁面
<%--
Created by IntelliJ IDEA.
User: MD
Date: 2020/8/11
Time: 15:12
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>學生注冊</p>
<form action="reg" method="post">
<table>
<tr>
<td>id</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>姓名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>email:</td>
<td><input type="text" name="email"></td>
</tr>
<tr>
<td>年齡</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="注冊"></td>
</tr>
</table>
</form>
</body>
</html>
4. 定義RegisterServlet
在com.md下新建一個包controller,在下面創建RegisterServlet,繼承HttpServlet
package com.md.controller;
import com.md.domain.Student;
import com.md.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author MD
* @create 2020-08-11 15:22
*/
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String strId = request.getParameter("id");
String strName = request.getParameter("name");
String strEmail = request.getParameter("email");
String strAge = request.getParameter("age");
// 創建spring的容器對象
String config = "spring.xml";
ApplicationContext c = new ClassPathXmlApplicationContext(config);
// 獲取service
StudentService studentService = (StudentService) c.getBean("studentService");
studentService.addStudent(new Student(Integer.parseInt(strId),
strName,strEmail,Integer.parseInt(strAge)));
// 跳的另一個頁面
request.getRequestDispatcher("/result.jsp").forward(request,response);
}
}
5. 定義result頁面
<%--
Created by IntelliJ IDEA.
User: MD
Date: 2020/8/11
Time: 15:30
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
注冊成功
</body>
</html>
6. 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">
<!--
version="4.0"
如果新建的這個版本低,不是4.0的,可以找之前寫的項目,把上面的信息粘貼過來就行
-->
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.md.controller.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/reg</url-pattern>
</servlet-mapping>
</web-app>
此時和java web類似,配置Tomcat,啟動,然后運行
7. 運行結果分析
當表單提交,跳轉到 result.jsp 后,多刷新幾次頁面,查看后台輸出,發現每刷新一次頁面,就 new 出一個新的 Spring 容器。即,每提交一次請求,就會創建一個新的 Spring 容器
對於一個應用來說,只需要一個 Spring 容器即可。所以,將 Spring 容器的創建語句放在 Servlet 的 doGet()或 doPost()方法中是有問題的,需要改進
二、 使用 Spring 的器監聽器 ContextLoaderListener
對於 Web 應用來說,ServletContext 對象是唯一的,一個 Web 應用,只有一個ServletContext 對象,該對象是在 Web 應用裝載時初始化的。
若將 Spring 容器的創建時機,放在 ServletContext 初始化時,就可以保證 Spring 容器的創建只會執行一次,也就保證了Spring 容器在整個應用中的唯一性
當 Spring 容器創建好后,在整個應用的生命周期過程中,Spring 容器應該是隨時可以被訪問的。即,Spring 容器應具有全局性。而放入 ServletContext 對象的屬性,就具有應用的全局性。所以,將創建好的 Spring 容器,以屬性的形式放入到 ServletContext 的空間中,就保證了 Spring 容器的全局性
上述的這些工作,已經被封裝在了如下的 Spring 的 Jar 包的相關 API 中:spring-web-5.2.5.RELEASE
1. maven依賴pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2. 注冊監聽器 ContextLoaderListener
若 要 在 ServletContext 初 始 化 時 創 建 Spring 容 器 , 就 需 要 使 用 監 聽 器 接 口ServletContextListener 對 ServletContext 進行監聽。在 web.xml 中注冊該監聽器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3. 指定 Spring 配置文件的位置
<context-param>
ContextLoaderListener 在對 Spring 容器進行創建時,需要加載 Spring 配置文件。其默認的 Spring 配置文件位置與名稱為:WEB-INF/applicationContext.xml。
但一般會將該配置文件放置於項目的 classpath 下,即 src 下,所以需要在 web.xml 中對 Spring 配置文件的位置及名稱進行指定
此時為了和默認的不同,把applicationContext.xml重命名為spring.xml文件
<context-param>
<!-- contextConfigLocation:表示配置文件的路徑 -->
<param-name>contextConfigLocation</param-name>
<!--自定義配置文件的路徑-->
<param-value>classpath:spring.xml</param-value>
</context-param>
此時web.xml中的全部代碼
<?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">
<!--如果新建的這個版本低,可以找之前寫的項目,把上面的信息粘貼過來就行-->
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.md.controller.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/reg</url-pattern>
</servlet-mapping>
<!-- 注冊監聽器
監聽器被創建后或讀取這個/WEB-INF/applicationContext.xml這個文件
為什么要讀取文件:
因為監聽器中要創建ApplicationContext對象,需要加載配置文件
/WEB-INF/applicationContext.xml就是監聽器默認讀取的spring配置文件路徑
可以修改默認的文件位置
配置監聽器:目的是創建容器對象,創建了容器對象,就能把spring.xml配置文件中的所有對象創建好
用戶發起請求就可以直接使用對象了
重點:下面的這段代碼和如何獲取對象
-->
<context-param>
<!-- contextConfigLocation:表示配置文件的路徑 -->
<param-name>contextConfigLocation</param-name>
<!--自定義配置文件的路徑-->
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
此時的目錄結構
4. 獲取Spring容器對象
1. 直接從 ServletContext 中獲取
WebApplicationContext c = null;
// 獲取ServletContext中的容器對象,創建好的容器對象
String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr = getServletContext().getAttribute(key);
if (attr != null){
c = (WebApplicationContext) attr;
}
2. 通過 WebApplicationContextUtils 獲取
// 使用框架中的方法獲取容器對象
WebApplicationContext c = null;
ServletContext sc = getServletContext();
c = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
System.out.println("容器對象的信息--------"+c);
以上兩種方式,無論使用哪種獲取容器對象,刷新 result頁面后,可看到代碼中使用的 Spring 容器均為同一個對象
此時RegisterServlet的全部代碼
package com.md.controller;
import com.md.domain.Student;
import com.md.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author MD
* @create 2020-08-11 15:22
*/
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String strId = request.getParameter("id");
String strName = request.getParameter("name");
String strEmail = request.getParameter("email");
String strAge = request.getParameter("age");
// 創建spring的容器對象
//String config = "spring.xml";
//ApplicationContext c = new ClassPathXmlApplicationContext(config);
// 配置完成之后可以直接這么使用
// WebApplicationContext c = null;
// // 獲取ServletContext中的容器對象,創建好的容器對象
// String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
// Object attr = getServletContext().getAttribute(key);
// if (attr != null){
// c = (WebApplicationContext) attr;
// }
// 使用框架中的方法獲取容器對象,推薦
WebApplicationContext c = null;
ServletContext sc = getServletContext();
c = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
// 直接縮短為一行
//WebApplicationContext c = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
System.out.println("容器對象的信息--------"+c);
// 獲取service
StudentService studentService = (StudentService) c.getBean("studentService");
studentService.addStudent(new Student(Integer.parseInt(strId),
strName,strEmail,Integer.parseInt(strAge)));
// 跳的另一個頁面
request.getRequestDispatcher("/result.jsp").forward(request,response);
}
}