3.2 Web配置
在Servlet 3规范出来之后,Web项目可以不使用web.xml配置文件。该规范提供了相关注解,可以用来实现web.xml的配置工作,例如Servlet、Listener、Filter等都可以使用注解进行配置。这一节将向大家介绍相关的Web配置。
3.2.1 Servlet配置
新建一个名称为“web-test”的Maven项目,在该项目中只使用“spring-boot-starter-web”依赖。新建启动类,请见代码清单3-6。
代码清单3-6:codes\03\3.2\web-test\src\main\java\org\crazyit\boot\c3\WebApp.java
@SpringBootApplication @ServletComponentScan public class WebApp { public static void main(String[] args) { SpringApplication.run(WebApp.class, args); } }
在启动类中,除了包含原来的SpringBootApplication注解外,还加入了ServletComponentScan注解,这个注解主要用于扫描源码包中的Servlet组件,包括使用@WebServlet、@WebFilter和@WebListener进行修饰的类。接下来,新建一个Servlet,使用@WebServlet进行修饰,请见代码清单3-7。
代码清单3-7:odes\03\3.2\web-test\src\main\java\org\crazyit\boot\c3\web\MyServlet.java
@WebServlet(value = "/servlet") public class MyServlet extends HttpServlet { public MyServlet() { System.out.println("my servlet class"); } protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException { System.out.println("servlet service method"); } }
代码清单3-7定义了一个MyServlet,在浏览器中访问http://localhost:8080/servlet,则可以看到这个service方法的执行情况。MyServlet依然继承于HttpServlet,只是使用@WebServlet进行了修饰。该注解主要有以下属性。
name:String类型,配置servlet的名称。
value:String数组,相当于配置了urlPatterns属性。
urlPatterns:String数组,用于配置多个url-pattern,配置方式如下:
@WebServlet(urlPatterns = {"a", "b"})
loadOnStartup:相当于<load-on-startup>元素。
initParams:WebInitParam数组,用于设置Servlet的初始化参数,配置方式如下:
@WebServlet(value = "/servlet", initParams = { @WebInitParam(name = "paramA", value= "angus") })
asyncSupported:等同于Servlet的<async-supported>元素,用于配置该Servlet是否支持异步。
description:等同于<description>元素。
displayName:等同于<display-name>元素。
3.2.2 Listener配置
监听器(Listener)与Servlet一样,使用@WebListener注解进行修饰后,就可以被ServletComponentScan扫描并注册,请见代码清单3-8。
代码清单3-8:codes\03\3.2\web-test\src\main\java\org\crazyit\boot\c3\web\MyListener.java
@WebListener public class MyListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { System.out.println("context init"); } public void contextDestroyed(ServletContextEvent sce) { } }
@WebListener注解只有一个value属性,用来描述这个Listener,其使用方法较为简单,在此不再赘述。
3.2.3 Filter配置
使用@WebFliter来修饰一个过滤器(Filter),请见代码清单3-9。
代码清单3-9:codes\03\3.2\web-test\src\main\java\org\crazyit\boot\c3\web\MyFilter.java
@WebFilter(value = "/filter/*") public class MyFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("my filter init"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter method"); chain.doFilter(request, response); } public void destroy() { } }
@WebFilter注解主要有以下属性。
filterName:String类型,用于指定过滤器名称。
value:String数组,相当于配置了urlPatterns。
urlPatterns:String数组,用于配置多个url-pattern,等同于<url-pattern>元素。
servletNames:配置过滤器将会在哪些Servlet中使用。
dispatcherTypes:DispatcherType数组,相当于配置了<filter-mapping>的<dispatcher>元素,其取值可以为FORWARD、INCLUDE、REQUEST、ASYNC、ERROR。
除了以上几个属性外,还有initParams、asyncSupported、description与displayName属性,它们的使用方法与@WebServlet一致。
通过上面的例子可以知道,Servlet、Listener与Filter这3个注解基本上与web.xml中的配置一样,只是将XML代码变为注解而已,使用方法较为简单。再次提醒,要想让这3个注解生效,则需要使用ServletComponentScan注解。
3.2.4 构建可部署的war包
前面的所有示例都使用了内嵌的Tomcat服务器,这些项目最终可被打成jar包来运行,每个jar包都可以被看作一个独立的Web服务器。对于传统的Web开发,一般会将Web应用打成一个war包,然后将其部署到Web服务器中运行。Spring Boot也支持传统的部署模式。新建一个Maven项目,其pom.xml文件内容如代码清单3-10所示。
代码清单3-10:codes\03\3.2\war-test\pom.xml
<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>org.crazyit.boot.c3</groupId> <artifactId>war-test</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <build> <finalName>war-test</finalName> </build> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> </dependencies> </project>
注意pom.xml文件中的粗体字部分,该项目最终会被打成一个名称为“war-test”的war包,其所依赖的“spring-boot-starter-tomcat”项目,会由war所在的容器提供。接下来,编写启动类与测试用的控制器,请见代码清单3-11。
代码清单3-11:codes\03\3.2\war-test\src\main\java\org\crazyit\boot\c3\WarApp.java
@SpringBootApplication @RestController public class WarApp extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(WarApp.class); } public static void main(String[] args) { SpringApplication.run(WarApp.class, args); } @GetMapping("/hello") public String hello() { return "hello"; } }
启动类继承自SpringBootServletInitializer类,这里需要重写父类的configure方法。SpringBootServletInitializer类将会调用configure方法来得到一个SpringApplicationBuilder实例。根据名称可知,这个SpringApplicationBuilder会帮我们创建Spring上下文,实际上它会帮我们创建WebApplicationContext实例,其在创建Spring上下文的过程中,会查找使用了@Configuration注解的配置类,然后对它们进行初始化。
对于启动类,我们可以这样理解:在Eclipse运行main方法时,会使用原来的方式(执行jar包的方式)来启动Web应用(含服务器),如果将项目打成一个war包放到Web服务器上运行,就会创建WebApplicationContext,在创建的过程中,会调用configure方法。另外,父类调用一个方法来实现部分工作,但该方法需要由继承的子类来实现,这种模式是GoF设计模式的一种:模板方法。
编写完启动类和控制器之后,使用命令行工具,进入项目的根目录(codes\03\3.2\ war-test),输入Maven命令对项目进行构建:mvn clean package。执行成功后,到codes\03\3.2\war-test\target目录,可以看到war-test.war,将该包复制到Tomcat的webapps下。启动Tomcat后,访问:http://localhost:8080/war-test/hello,在正常情况下可以看到结果,笔者在Tomcat 8中测试成功,建议使用7.0以上版本的Tomcat。
3.2.5 JSP配置
JSP是传统Web应用最常使用的表现层技术,Spring Boot的官方文档不建议在可执行的jar包中使用JSP,但我们可以在war包里面使用它。新建一个Maven项目,在配置中将其打成一个war包(packaging元素),在pom.xml文件中加入相应的依赖,请见代码清单3-12。
代码清单3-12:codes\03\3.2\jsp-test\pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> </dependencies>
前面两个依赖我们之前介绍过,这里重点来看下“tomcat-embed-jasper”,这个包主要用于编译JSP,它同样由容器提供。另外,本例将会使用JSTL标签,因此加入JSTL的依赖。在src/main/resources下新建application.yml,添加以下内容:
spring: mvc: view: prefix: /pages/ suffix: .jsp
这段内容用于配置Spring MVC视图,本例默认会将JSP文件放到src/main/webapp/ pages目录下。新建启动类与控制器,请见代码清单3-13。
代码清单3-13:codes\03\3.2\jsp-test\src\main\java\org\crazyit\boot\c3\JspApp.java
@SpringBootApplication @Controller public class JspApp extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(JspApp.class); } public static void main(String[] args) { SpringApplication.run(JspApp.class, args); } @GetMapping("/hello") public String hello(HttpServletRequest request) { request.setAttribute("name", "angus"); return "hello"; } }
控制器的hello方法会设置名称为name的参数,最后跳转到pages下面的hello.jsp。新建hello.jsp,内容请见代码清单3-14。
代码清单3-14:codes\03\3.2\jsp-test\src\main\webapp\pages\hello.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <c:set var="ctx" value="${pageContext.request.contextPath}" /> <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" href="${ctx}/css/test.css" type="text/css" /> <script type="text/javascript" src="${ctx}/js/test.js"></script> <title></title> </head> <body> <div onClick="myClick()" class="main_text">Hello ${name}</div> </body> </html>
这是一个最简单的JSP文件,在开头声明了JSTL的标签库与ctx变量,该变量用于定位CSS与JS资源。在JSP中使用了一个div元素来显示name参数。本例的项目结构如图3-2所示。
图3-2 项目结构
在Eclipse中运行JspApp类,打开浏览器访问:http://localhost:8080/hello,则可以看到JSP页面的输出。同样地,可以使用命令行工具进入jsp-test目录,执行mvn clean package命令将项目打成一个war包,放到另外的Tomcat容器下运行。在浏览器中访问:http://localhost:8080/jsp-test/hello,可以看到同样的结果。