[JavaWeb 基础增强][Servlet基础][Servlet 3.0+ 从XML配置到注解配置]

[JavaWeb 基础增强][Servlet基础][Servlet 3.0+ 从XML配置到注解配置]

文章目录

  • [JavaWeb 基础增强][Servlet基础][Servlet 3.0+ 从XML配置到注解配置]
  • 前言
  • 各版本更新内容
  • Servlet 2.5及之前的配置
    • 使用xml配置servlet
    • 多Servlet时的配置
  • Servlet 3.0 及之后的配置
    • @WebServlet 注解
          • @WebServlet 主要属性列表
    • @WebFilter
          • @WebFilter 的常用属性
    • @WebInitParam 注解
          • @WebInitParam 的常用属性
    • @WebListener
          • @WebListener 的常用属性
  • Sevlet 3.0/4.0的一些其他新特性
    • 文件上传支持
          • @MultipartConfig注解 的常用属性
    • 使用Java配置加载框架
    • 服务器推送

前言

做JavaWeb开发的 不可能不知道Servlet 知道Servlet是JavaWeb应用的基础

不过搞懂的恐怕不多 毕竟实际开发多使用框架 Servlet被封装在最底层

但是Servlet版本不断演进 新的变化总应该学习和知晓

各版本更新内容

  • Servlet 1.0/2.0 初始的Servlet 功能很简陋 现已无学习必要
  • Servlet 2.2 引入自包含web应用概念
  • Servlet 2.3 增加过滤器Filter和过滤器链filter chain
    增加上下文\会话监听器context\session Listener
  • Servlet 2.4 增强请求处理的功能
  • Servlet 2.5 支持注解 简化的web.xml配置
  • Servlet 3.0 使用注解配置代替xml配置
  • Servlet 3.1 引入异步I/O处理
  • Servlet 4.0 支持HTTP/2 协议

Servlet 2.5及之前的配置

传统的Servlet 是使用web.xml文件配置的
众所周知十分的繁琐 而和此保持一致的框架
比如Spring (3.0之前) 因为配置太过繁杂
而被讥讽为xml地狱

使用xml配置servlet

建立一个web项目

项目结构

–src
|–java
|–resource
|–webapp

在java目录下源码目录新建servlet类

public class HelloServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("Get Http Connect...");

        response.getWriter().println("Get Http Connect Success");
    }
}

在webapp目录下的WEB-INF
建立web应用配置文件web.xml

    <display-name>First Web Applicationdisplay-name>
    <description>第一个网络服务应用description>
       <welcome-file-list>
         <welcome-file>index.jspwelcome-file>
       welcome-file-list>
    
    <servlet>
        <servlet-name>Firstservlet-name>
        <servlet-class>tv.ciyuan2.HelloServletservlet-class>
    servlet>

    <servlet-mapping>
        <servlet-name>Firstservlet-name>
        <url-pattern>/Hellourl-pattern>
    servlet-mapping>
  • 每个Servlet首先必须注册
    使用标签进行注册 子标签指定servlet名
    指定servlet的全限定路径 表明是哪个具体的类

  • 然后要把Servlet映射到URL地址
    使用标签进行映射
    子标签指定要映射的Servlet 此名名称要和之前注册的相同
    子标签映射URL地址 此处必须加上 / 符号 否则访问不到

其他标签如 可以指定欢迎页面

一个Servlet时还可以接受 能想象到多个Servlet实例时配置将会多么繁琐

多Servlet时的配置

在前面的基础上再添加多个Servlet

如登录 注册 提示等等

    <display-name>First Web Applicationdisplay-name>
    <description>第一个网络服务应用description>
       <welcome-file-list>
         <welcome-file>index.jspwelcome-file>
       welcome-file-list>
    
    <servlet>
        <servlet-name>Firstservlet-name>
        <servlet-class>tv.ciyuan2.HelloServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Firstservlet-name>
        <url-pattern>/Hellourl-pattern>
    servlet-mapping>   


    <servlet>
        <servlet-name>Loginservlet-name>
        <servlet-class>tv.ciyuan2.LoginServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Loginservlet-name>
        <url-pattern>/Loginurl-pattern>
    servlet-mapping>  

    <servlet>
        <servlet-name>Registservlet-name>
        <servlet-class>tv.ciyuan2.RegistServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Registservlet-name>
        <url-pattern>/Registurl-pattern>
    servlet-mapping>  

    <servlet>
        <servlet-name>Promptservlet-name>
        <servlet-class>tv.ciyuan2.PromptServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Promptservlet-name>
        <url-pattern>/Prompturl-pattern>
    servlet-mapping>

web程序 过滤器必不可少

再添加上编码过滤器


  <display-name>First Web Applicationdisplay-name>
    <description>第一个网络服务应用description>
       <welcome-file-list>
         <welcome-file>index.jspwelcome-file>
       welcome-file-list>

 <filter>
  	<filter-name>CharactorEncodingFilterfilter-name>
  	<filter-class>filter.CharactorEncodingFilterfilter-class>
  filter>
  <filter>
  	<filter-name>HtmlFilterfilter-name>
  	<filter-class>filter.HtmlFilterfilter-class>
  filter>
  
  <filter-mapping>
  	<filter-name>CharactorEncodingFilterfilter-name>
  	<url-pattern>/*url-pattern>
  filter-mapping>
  <filter-mapping>
  	<filter-name>HtmlFilterfilter-name>
  	<url-pattern>/*url-pattern>
  filter-mapping>
    
    <servlet>
        <servlet-name>Firstservlet-name>
        <servlet-class>tv.ciyuan2.HelloServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Firstservlet-name>
        <url-pattern>/Hellourl-pattern>
    servlet-mapping>   


    <servlet>
        <servlet-name>Loginservlet-name>
        <servlet-class>tv.ciyuan2.LoginServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Loginservlet-name>
        <url-pattern>/Loginurl-pattern>
    servlet-mapping>  

    <servlet>
        <servlet-name>Registservlet-name>
        <servlet-class>tv.ciyuan2.RegistServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Registservlet-name>
        <url-pattern>/Registurl-pattern>
    servlet-mapping>  

    <servlet>
        <servlet-name>Promptservlet-name>
        <servlet-class>tv.ciyuan2.PromptServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>Promptservlet-name>
        <url-pattern>/Prompturl-pattern>
    servlet-mapping>

非常的繁杂 可以说是接近xml地狱了

所以JavaEE 6的Servlet 3.0标准中 提供了新的解决方案
使用注解初始化Servlet

Servlet 3.0 及之后的配置

Servlet 3.0 之后可以使用注解来配置 Servlet Filter Listener等Java Web组件

Servlet 3.0的注解支持 在web.xml中是可以设置的 web.xml的顶层标签 < web-app > 有一个 metadata-complete 属性 设置为false时则启用注解和web插件支持
true时则只初始化web.xml配置中的内容
默认当然是false 也就是启用注解和插件支持

@WebServlet 注解

@WebServlet 注解,用于代替在web.xml注册Servlet类的< servlet >,< servlet-mapping >
标签 可以方便的定义一个Servlet

还是刚才的HelloServlet 使用注解来配置

@WebServlet(name = "HelloServlet",urlPatterns="/hello")
public class HelloServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("Get Http Connect...");

        response.getWriter().println("Get Http Connect Success");

    }
}

简单了许多 只需一个注解 就可以配置成功
用过SpringMVC,Jersey等框架的 可以发现非常类似

都是通过注解标志 来指定请求处理 路径映射的

@WebServlet 主要属性列表
属性名 类型 描述
name String 指定 Servlet 的 name 属性,等价于 < servlet-name>。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。
value String[] 该属性等价于 urlPatterns 属性。两个属性不能同时使用。
urlPatterns String[] 指定一组 Servlet 的 URL 匹配模式。等价于 < url-pattern> 标签。
loadOnStartup int 指定 Servlet 的加载顺序,等价于 < load-on-startup> 标签。
initParams WebInitParam[] 指定一组 Servlet 初始化参数,等价于 < init-param> 标签。
asyncSupported boolean 声明 Servlet 是否支持异步操作模式,等价于 < async-supported> 标签。
description String 该 Servlet 的描述信息,等价于 < description> 标签。
displayName String 该 Servlet 的显示名,通常配合工具使用,等价于 < display-name> 标签。

常用的就是映射路径的urlPatterns属性
初始化参数的inti-param属性
加载顺序的loadOnStartup 属性
很多教程里面把启动框架的servlet(如Spring)设置为loadOnStartup=1
就是为了第一个加载框架以便首先启动
(当然现在一般都是加载框架启动监听器 在Servlet初始化之前就启动框架)

@WebFilter

@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器,等同于web.xml中< filter>标签

@WebFilter 的常用属性
属性名 类型 描述
filterName String 指定过滤器的 name 属性,等价于 < filter-name>
value String[] 该属性等价于 urlPatterns 属性。但是两者不应该同时使用。
urlPatterns String[] 指定一组过滤器的 URL 匹配模式。等价于 < url-pattern> 标签。
servletNames String[] 指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 < servlet-name> 的取值。
dispatcherTypes DispatcherType 指定过滤器的转发模式。具体取值包括:
ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
initParams WebInitParam[] 指定一组过滤器初始化参数,等价于 < init-param> 标签。
asyncSupported boolean 声明过滤器是否支持异步操作模式,等价于 < async-supported> 标签。
description String 该过滤器的描述信息,等价于 < description> 标签。
displayName String 该过滤器的显示名,通常配合工具使用,等价于 < display-name> 标签。

一个登录过滤器


@WebFilter(filterName="LoginFilter",urlPatterns = "/hello") public class LoginFilter implements Filter {    

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {    
      System.out.println("LoginFilter Strat");
    }    
    
    @Override
    public void doFilter(ServletRequest request, 
    ServletResponse response,FilterChain chain) 
    throws IOException, ServletException 
    {    
    
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
    
        String userid=Optional.ofNullable
       ((String)httpServletRequest.getSession().getAttribute("userid"))
       .orElseGet(()->"");

       if(userid.isEmpty())
      {  
         response.getWriter().println("未登录 登陆后再试");  
      }
      else
      {
      chain.doFilter(request,response);
      }
  }
  @Override
  public void destroy() {   } 
  
  }

@WebInitParam 注解

该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数,这等价于 web.xml 中 < servlet> 和 < filter> 的 < init-param> 子标签。

@WebInitParam 的常用属性
属性名 类型 是否可选 描述
name String 指定参数的名字,等价于 < param-name>。
value String 指定参数的值,等价于 < param-value>。
description String 关于参数的描述,等价于 < description>。

这个标签是和@WebServlet 或者 @WebFilter 配合使用的
比如 使用阿里巴巴的Druid数据库连接池时
配置管理界面时

配置Druid的登录和管理界面


@WebServlet(urlPatterns = "/druid/*",
        initParams={
  @WebInitParam(name="allow",value=""),// IP白名单 (没有配置或者为空,则允许所有访问)
  @WebInitParam(name="loginUsername",value="admin"),// 用户名
  @WebInitParam(name="loginPassword",value="admin"),// 密码
  @WebInitParam(name="resetEnable",value="false")// 禁用HTML页面上的“Reset All”功能
  }) public class DruidStatViewServlet extends StatViewServlet { }

从上可知 @WebInitParam是编写在@WebServler注解的initParam标签之中的

配置Druid的资源过滤器

@WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
        initParams={
  @WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")
  // 忽略资源
  }) public class DruidFilter extends WebStatFilter { }

同样 @WebInitParam也可以编写在@WebFilter注解的initParam标签之中的

@WebListener

该注解用于将类声明为监听器,被 @WebListener 标注的类必须实现以下至少一个接口:

  • ServletContextListener
  • ServletContextAttributeListener
  • ServletRequestListener
  • ServletRequestAttributeListener
  • HttpSessionListener
  • HttpSessionAttributeListener

该注解使用非常简单,其属性如下:

@WebListener 的常用属性
属性名 类型 是否可选 描述
value String 该监听器的描述信息。

一个监听来访客户的网络地址的监听器

@WebListener(value="VisitListener")`
public class VisitListener implements ServletRequestListener {    @Override
  public void requestInitialized(ServletRequestEvent sre) {    
	  String ipaddress=sre.getServletRequest().getRemoteHost();
	  System.out.println("id address="+ipaddress);
    }   
 }

如此,则不需要在 web.xml 中配置 < listener> 标签了。它等价的 web.xml 中的配置形式如下:

<listener>
<listener-class>*.VisitListenerlistener-class>
listener>

Sevlet 3.0/4.0的一些其他新特性

文件上传支持

使用@MultipartConfig注解 启用文件上传支持

过去需要借助第三方工具 Servlet3.0以后就不需要了

@WebServlet(name = "UploadServlet",urlPatterns = "/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {

        System.out.println("UploadServlet Strat");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        Collection parts=request.getParts();
        //此处一个Part对应一个上传的文件

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

@MultipartConfig注解 的常用属性
属性名 类型 是否可选 描述
fileSizeThreshold int 当数据量大于该值时,内容将被写入文件。
location String 存放生成的文件地址。
maxFileSize long 允许上传的文件最大值。默认值为 -1,表示没有限制。
maxRequestSize long 针对该 multipart/form-data 请求的最大数量,默认值为 -1,表示没有限制。

使用Java配置加载框架

使用web.xml配置框架加载 大部分人应该都是很熟悉了

原理是在web.xml中配置监听器或者Servlet
这样在应用容器启动时会自动加载框架的启动器

那么 使用注解后 应该怎样加载框架呢

JavaEE 也提供了解决方案

那就是 ServletContainerInitializer

javax.servlet.ServletContainerInitializer

源码

public interface ServletContainerInitializer {


    public void onStartup(Set> c, ServletContext ctx)
        throws ServletException; 
}

所有实现这接口的类 它的*OnStratip()*方法
将在web应用容器启动的时候被调用

  • 例如Spring

在Srping中是SpringServletContainerInitializer
这个类实现了ServletContainerInitializer

所以容器 启动时会自动 加载SpringServletContainerInitializer的onStratup方法

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

    //入参 webAppInitializerClasses 即包括所有webAppInitializer类型的Class 
    通常都是Spring的Java形式的配置文件
    @Override
	public void onStartup(@Nullable Set> webAppInitializerClasses, 
	ServletContext servletContext)throws ServletException {

		List initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class waiClass : webAppInitializerClasses) {
				// 如果该Class 不是接口,不是抽象类,并且是WebApplicationInitializer类型的类
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
                        //实例化该类,并加入到initializers集合中
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
        //遍历initializers集合中的类
		for (WebApplicationInitializer initializer : initializers) {
            //调用其onStartup方法 启用其配置的内容
			initializer.onStartup(servletContext);
		}
	}
}

而 我们需要重写的Spring配置类 AbstractAnnotationConfigDispatcherServletInitializer

public abstract class AbstractAnnotationConfigDispatcherServletInitializer

就是一个WebAppInitializerClass的子类
在SpringServletContainerInitializer 启动的时候
就会被加载并调用其方法

这样 框架就被启动起来了

服务器推送

服务端推送 很显然 这是一个为HTTP/2准备的功能

所以这是Servlet 4.0提供的新特性

服务器推送使服务器能预测客户端请求的资源需求。然后,在完成请求处理之前,它可以将这些资源发送到客户端。

客户端发出一个针对该网页的请求。服务器然后分析所请求的页面,确定呈现它所需的资源,并主动将这些资源发送到客户端的缓存(比如大量的图片 视频等流媒体)。

在执行所有这些操作的同时,服务器仍在处理原始网页请求。客户端收到响应时,它需要的资源已经位于缓存中,注意是存放于浏览器缓存。

要推送资源 首先得获取推送器
Servlet通过推送器向客户端发出推送

@Override
protected void doGet(HttpServletRequest request, 
                     HttpServletResponse response) 
                     throws ServletException, IOException {
 
   PushBuilder pushBuilder = request.newPushBuilder();
 
}

每次调用 newPushBuilder() 方法时,都将返回 PushBuilder 的新实例。如果服务器推送不可用,newPushBuilder() 将返回null。在某些情况下,客户端可能会为请求事务拒绝服务器推送。如果客户端没有使用安全连接,服务器推送也不会起作用。因此,务必要在对 PushBuilder 实例调用方法之前,针对 null 返回值进行测试。

发送推送内容

PushBuilder pushBuilder = request.newPushBuilder();
 
if (pushBuilder != null) {
   pushBuilder.path("images/bigBackground.png").push();
   pushBuilder.path("video/demo.mp4").push();
}

你可能感兴趣的:(Java基础增强)