Spring注解驱动开发(三)servlet3.0以及整合SpringMVC

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定制功能

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值