1、Servlet3.0
在Servlet3.0之前,创建web工程都是需要web.xml文件的,三大组件:Servlet、Filter和Listener以及SpringMVC的前端控制DispatchServlet等都是需要在web.xml文件中进行配置的。
但是这次我们要做的是基于Spring的注解配置,不想使用web.xml来进行Spring的Listener和SpringMVC的DispatchServlet的配置,怎么实现呢?首先我们先看看Servlet3.0提供的新特性。
在Servlet3.0之后,提供了相关的注解来添加组件到web容器中,web.xml文件就不再是必须的了。
1.1 Servlet3.0原生版的注解开发
Servlet3.0的开发需要tomcat7及以上版本的支持,是JSR315系列的规范。
创建普通的动态web工程,选择3.0及以上的版本,不选择创建web.xml文件;
1.1.1 @WebServlet和@WebInitParam
@WebServlet注解可以向web容器注册一个Servlet,配置拦截的路径等等。这样的话,我们就不用配置web.xml也可以很容易的实现Servlet的装配。
@WebInitParam注解用在Servlet和Filter中,配置一些初始化属性的
1、index.jsp中添加超链接
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="hello">hello</a>
</body>
</html>
2、创建HelloServlet
package com.servlet3.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(value = "/hello",initParams = {@WebInitParam(name = "password",value = "123456")})
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("------------------HelloServlet执行了------------------");
ServletConfig servletConfig = getServletConfig();
String password = servletConfig.getInitParameter("password");
System.out.println("初始化参数password的值是:"+password);
response.getWriter().write("Hello servlet3.0");
}
}
启动项目,点击首页的hello超链接,就能返回Hello servlet3.0到浏览器。说明注册Servlet是成功的。
1.1.2 @WebFilter
@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 (所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
package com.servlet3.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "helloFilter",urlPatterns = "/hello")
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----HelloFilter过滤器初始化----");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// 对request和response进行一些预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("HelloFilter执行前!!!");
filterChain.doFilter(request, response); // 让目标资源执行,放行
System.out.println("HelloFilter执行后!!!");
}
@Override
public void destroy() {
System.out.println("----HelloFilter过滤器销毁----");
}
}
Filter的生命周期:
Filter的创建:Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作,filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
Filter的销毁:Web容器调用destroy方法销毁Filter。destroy方法在Filter的生命周期中仅执行一次。在destroy方法中,可以释放过滤器使用的资源。
FilterConfig接口:用户在配置filter时,可以使用为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
- String getFilterName():得到filter的名称。
- String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
- Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
- public ServletContext getServletContext():返回Servlet上下文对象的引用。
1.1.3 @WebListener
@WebListener注解将一个实现了特定监听器接口的类定义为监听器,这样我们在web应用中使用监听器时,也不再需要在web.xml文件中配置监听器的相关描述信息了。
package com.servlet3.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* 使用@WebListener注解将实现了ServletContextListener接口的MHelloListener标注为监听器
*/
@WebListener
public class HelloListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("============ServletContext 初始化============");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("============ServletContext 销毁============");
}
}
1.2 Shared libraries(共享库)/ runtimes pluggability(运行时插件能力)
Servlet3.0的Shared libraries(共享库)/ runtimes pluggability(运行时插件能力):
1、Servlet容器(web容器比如tomcat)启动会扫描 当前应用里面每一个jar包的 ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;该实现类必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件中,文件的内容就是ServletContainerInitializer实现类的全类名;
总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;
1.2.1 创建测试的几个类
package com.servlet3.service;
public interface HelloService {
}
package com.servlet3.service;
public interface HelloServiceExt extends HelloService {
}
package com.servlet3.service;
public class AbstractHelloService implements HelloService {
}
package com.servlet3.service;
public class HelloServiceImpl implements HelloService {
}
1.2.2 创建HelloServletContainerInitializer
package com.servlet3.initializer;
import com.servlet3.service.HelloService;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.util.Set;
//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class HelloServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
* Set<Class<?>> set:感兴趣的类型的所有子类型;
* ServletContext servletContext:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感兴趣的类型:");
for (Class<?> clazz : set) {
System.out.println(clazz);
}
}
}
1.2.3 创建javax.servlet.ServletContainerInitializer文件
在src根目录下创建 META-INF/services目录,里面创建文件:javax.servlet.ServletContainerInitializer(该文件没有后缀名)
注意:在IDEA中的src下面创建普通的文件夹,需要这样:File——》Project Structure——》选择Moudles——》选择sources
文件里面配置:
com.servlet3.initializer.HelloServletContainerInitializer
启动可以看到:
感兴趣的类型:
interface com.servlet3.service.HelloServiceExt
class com.servlet3.service.AbstractHelloService
class com.servlet3.service.HelloServiceImpl
1.2.3 使用ServletContext注册web容器的三大组件
之前我们了可以使用注解@WebServlet等注解来向容器中注册组件,但是当我们是导入的第三方jar包的时候,就不好使用注解来进行注册了。以前有web.xml时候,是可以在web.xml中进行配置的。那么现在没有了web.xml,要怎么把组件注册到容器中呢?
必须在项目启动的时候来添加,在运行是不允许注册的:
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
package com.servlet3.initializer;
import com.servlet3.filter.HelloFilter;
import com.servlet3.listener.HelloListener;
import com.servlet3.service.HelloService;
import com.servlet3.servlet.HelloServlet;
import javax.servlet.*;
import javax.servlet.annotation.HandlesTypes;
import java.util.EnumSet;
import java.util.Set;
//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class HelloServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
* Set<Class<?>> set:感兴趣的类型的所有子类型;
* ServletContext servletContext:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("感兴趣的类型:");
for (Class<?> clazz : set) {
System.out.println(clazz);
}
//注册HelloServlet组件
ServletRegistration.Dynamic helloServlet = servletContext.addServlet("helloServlet", HelloServlet.class);
//配置HelloServlet的映射
helloServlet.addMapping("/hello");
//注册HelloFilter组件
FilterRegistration.Dynamic helloFilter = servletContext.addFilter("helloFilter", HelloFilter.class);
//配置HelloFilter的拦截路径
helloFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/hello");
//注册HelloListener组件
servletContext.addListener(HelloListener.class);
}
}
2、整合SpringMVC
2.1 思路分析
1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、在spring-web的jar包下,META-INF/services下面的javax.servlet.ServletContainerInitializer文件中配置的类是:org.springframework.web.SpringServletContainerInitializer
3、在类SpringServletContainerInitializer上使用了注解@HandlesTypes,配置了WebApplicationInitializer,所以spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
@HandlesTypes({WebApplicationInitializer.class})
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类),他主要有以下三个实现类:
1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
2)、AbstractDispatcherServletInitializer:
创建一个web的ioc容器;createServletApplicationContext();
创建了DispatcherServlet;createDispatcherServlet();
将创建的DispatcherServlet添加到ServletContext中;
getServletMappings();
3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
创建根容器(父容器):createRootApplicationContext()
getRootConfigClasses();传入一个配置类
创建web的ioc容器(子容器): createServletApplicationContext();
获取配置类;getServletConfigClasses();
总结:
以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;实现抽象方法指定DispatcherServlet的配置信息;
2.2 整合SpringMVC
2.2.1 创建maven web工程,导入依赖
参照博客(不使用骨架)创建maven web工程:https://blog.csdn.net/weixin_42396239/article/details/92830544
<?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.springmvc.annotation</groupId>
<artifactId>springmvc-annotation</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2.2.2 自定义WebAppInitializer
定义MyWebAppInitializer,继承:AbstractAnnotationConfigDispatcherServletInitializer,实现在web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
package com.springmvc;
import com.springmvc.config.SpringConfig;
import com.springmvc.config.SpringMvcConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//获取根容器的配置类;(Spring的配置文件) 父容器
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//获取web容器的配置类(SpringMVC配置文件) 子容器;
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
//获取DispatcherServlet的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2.2.3 创建在MyWebAppInitializer中对应的主配置类和子容器配置类
1、SpringConfig
package com.springmvc.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
//Spring的容器不扫描controller;父容器
@ComponentScan(value = "com.springmvc",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class SpringConfig {
}
2、SpringMvcConfig
package com.springmvc.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
//SpringMVC只扫描Controller,ControllerAdvice(全局异常处理器注解);子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value = "com.springmvc",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class})
},useDefaultFilters = false)
public class SpringMvcConfig {
}
2.2.4 在webapp下创建index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="hello">hello</a>
</body>
</html>
启动工程,首页点击hello,可以看到浏览器上的正确返回。说明整合是成功的。
2.3 定制SpringMVC
在进行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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将SpringMVC处理不了的请求交给tomcat;静态资源 就可以访问 -->
<mvc:default-servlet-handler/>
<!-- SpringMVC的高级功能开启 -->
<mvc:annotation-driven />
<!-- <mvc:interceptors>
</mvc:interceptors>
<mvc:view-controller path=""/> -->
</beans>
那么现在没有了xml,怎么通过注解来进行定制化的配置呢?
1、在一个SpringMVC配置类(这里就用上面创建的SpringMvcConfig,也可以另建一个)上,添加注解:@EnableWebMvc:开启SpringMVC定制配置功能;相当于以前的配置文件的:<mvc:annotation-driven/>
2、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。),需要实现:WebMvcConfigurer接口(以前的版本是继承:WebMvcConfigurerAdapter类,但是已经过时了),可以根据需要,重写对应的方法,可以配置视图解析器等组件。
2.3.1 自定义一个拦截器
package com.springmvc;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyFirstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("目标方法之前执行---------------");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("目标方法执行正确之后执行===============================");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面响应以后执行############################");
}
}
2.3.2 首页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="hello">hello</a>
<a href="success">success</a>
<br/>
<img src="img/datupian.png"/>
</body>
</html>
2.3.3 在webapp下面创建WEB-INF/views文件夹,里面创建success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功页面</title>
</head>
<body>
SUCCESS PAGE
</body>
</html>
2.3.4 在SpringMVC的配置类中定制SpringMVC
package com.springmvc.config;
import com.springmvc.MyFirstInterceptor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.config.annotation.*;
//SpringMVC只扫描Controller,ControllerAdvice(全局异常处理器注解);子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value = "com.springmvc",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class})
},useDefaultFilters = false)
@EnableWebMvc //开启SpringMVC定制功能
public class SpringMvcConfig implements WebMvcConfigurer {
//定制视图解析器
public void configureViewResolvers(ViewResolverRegistry registry) {
//默认所有的页面都从 /WEB-INF/ xxx .jsp
//registry.jsp();
registry.jsp("/WEB-INF/views/",".jsp");
}
//静态资源访问
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//开启静态资源访问,
//相当于配置了:<mvc:default-servlet-handler/> 将SpringMVC处理不了的请求交给tomcat;静态资源 就可以访问
configurer.enable();
}
//拦截器
public void addInterceptors(InterceptorRegistry registry) {
// /** 拦截任意多层路径的任意请求
registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
}
}
@EnableWebMvc //开启SpringMVC定制功能