Spring全注解开发----Servlet 3.0

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring全注解开发----Servlet 3.0相关的知识,希望对你有一定的参考价值。

Servlet 3.0


servlet3.0-简介&测试

现在,我们来说说注解版的web,我们以前来写web的三大组件:Servlet、Filter、Listener,包括SpringMVC的前端控制器DispatcherServlet都需要在web.xml文件中来进行注册;而在Servlet3.0标准以后,就给我们提供了方便的注解的方式来完成我们这些组件的注册以及添加,提供了运行时的可插拔的插件能力;

说明:Servlet3.0及以上的标准是需要Tomcat7及以上的支持;

如果使用全注解开发,那么就可以去掉web.xml配置文件了,转用编码方式进行替代


使用前导入servlet相关的依赖

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>


    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.2.1</version>
      <scope>provided</scope>
    </dependency>

@WebServlet : 注册servlet ,以及servlet 3.0相关的注解说明,链接在下面:

@WebServlet("/hello")//指定当前servlet的请求映射路径
public class MyServlet  extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("大忽悠");
    }
}


同样,要注册Filter用@WebFilter注解、注册Listener用@WebListener注解;如果在注册的时候,需要一些初始化参数,我们就可以用@WebInitParam注解;

Servlet 3.0 注解 @WebServlet @WebFilter @WebListener

@WebFilter 的使用

@WebServlet的使用方法


小细节回顾: 解决tomcat响应中文乱码问题,通知浏览器使用uft-8编码来对数据进行解码

        resp.setContentType("text/html;charset=utf-8");

servlet3.0 ==> ServletContainerInitializer

Shared libraries(共享库) / runtimes pluggability(运行时插件能力)

1Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现

2、提供ServletContainerInitializer的实现类;

	必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
	
	文件的内容就是ServletContainerInitializer实现类的全类名;

总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面

META-INF/services/javax.servlet.ServletContainerInitializer

指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;


ServletContainerInitializer@HandlesTypes

第一步:我们写一个MyServletContainerInitializer 类实现ServletContainerInitializer 接口

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类或者子接口等)传递过来
//传入感兴趣的类型
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 在应用启动的时候,会运行onStartup方法;
     * Set<Class<?>> :感兴趣的类型的所有子类型;
     * ServletContext 代表当前的web应用的ServletContext对象,一个web应用相当于是一个ServletContext
     * @throws ServletException
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        set.forEach(System.out::println);
    }
}

第二步:在这个路径下新建一个文件


文件的内容就写我们实现ServletContainerInitializer 这个接口的类MyServletContainerInitializer 的全类名:



最后,我们运行起来,运行结果为:

感兴趣的类型:
class com.ldc.service.HelloServiceExt
class com.ldc.service.AbstractHelloService
class com.ldc.service.HelloServiceImpl

注意:从运行结果可以分析出,这里传递过来的所有感兴趣的类型是指定类型下面所有子类,不包括本身


servlet3.0-在ServletContext中利用编码的方式注册三大组件

首先我们写一个Servlet:

public class MyServlet  extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("大忽悠");
    }
}

再来一个Filter:

public class UserFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //过滤请求
        System.out.println("放行");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}

最后,再写一个Listener:

/**
 * 监听项目的启动和停止
 */
public class UserListener implements ServletContextListener {
    //监听ServletContextEvent的启动初始化
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("项目启动");
    }
    //监听ServletContextEvent销毁
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("项目停止");
    }
}

最后,我们来用ServletContext来进行注册三大组件:

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类或者子接口等)传递过来
//传入感兴趣的类型
@HandlesTypes(value = {HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 在应用启动的时候,会运行onStartup方法;
     * Set<Class<?>> :感兴趣的类型的所有子类型;
     * ServletContext 代表当前的web应用的ServletContext对象,一个web应用相当于是一个ServletContext
     * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
     * 2)、使用编码的方式,在项目启动的时候给ServletContext添加组件
     * 必须在项目启动的时候来添加
     *  (1)ServletContainerInitializer得到ServletContext对象来注册;
     *  (2)ServletContextListener的方法的参数里面的ServletContextEvent对象可以获取ServletContext对象
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感兴趣的类型:");
        set.forEach(System.out::println);

        //注册组件
        ServletRegistration.Dynamic servlet = servletContext.addServlet("userServlet", new MyServlet());
        //配置servlet的映射信息
        servlet.addMapping("/hello");

        //注册Listener
        servletContext.addListener(UserListener.class);

        //注册Filter
        FilterRegistration.Dynamic filter = servletContext.addFilter("userFilter", UserFilter.class);
        //拦截什么方式的请求,和要拦截的请求路径
        filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
    }
}

我们启动,然后再浏览器上进行访问:



使用编码方式注册三大组件的小总结

1.使用ServletContext注册Web组件(Servlet、Filter、Listener)

2.使用编码的方式,在项目启动的时候给ServletContext添加组件, 必须在项目启动的时候来添加

2.1 ServletContainerInitializer得到ServletContext对象来注册;

2.2 ServletContextListener的方法的参数里面的ServletContextEvent对象可以获取ServletContext对象,然后在项目启动的时候进行注册,即利用监听器进行组件注册


servlet3.0-与SpringMVC整合分析

首先导入springMVC相关的依赖

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.11.RELEASE</version>
    </dependency>

原理

1、web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、加载这个文件指定的类SpringServletContainerInitializer
3、spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
4、并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
	1)、AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext()2)、AbstractDispatcherServletInitializer:
			创建一个web的ioc容器;createServletApplicationContext();
			创建了DispatcherServletcreateDispatcherServlet();
			将创建的DispatcherServlet添加到ServletContext中;
				getServletMappings();
	3)、AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器
			创建根容器:createRootApplicationContext()
					getRootConfigClasses();传入一个配置类
			创建web的ioc容器: createServletApplicationContext();
					获取配置类;getServletConfigClasses();
	
总结:
	以注解方式来启动SpringMVC;继承AbstractAnnotationConfigDispatcherServletInitializer;
实现抽象方法指定DispatcherServlet的配置信息;


下面我们找到了SpringServletContainerInitializer 继承ServletContainerInitializer 的实现类源码如下:

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

    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)waiClass.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

        }
    }
}


springmvc-整合

1.创建web初始化器

//Web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //获取父容器的配置类:(Spring的配置文件) --->作为父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    //获取web容器的配置类(SpringMVC配置文件) --->作为一个子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{AppConfig.class};
    }

    //获取DispatcherServlet的映射信息
    //  /:拦截所有请求(包括静态资源(xx.js,xx.png),但是不包括*.jsp)
    //  /*:拦截所有请求,连*.jsp页面都拦截;jsp页面是tomcat引擎解析的
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}


2.父子配置类,spring是父配置类,要排除扫描controller注解,而springmvc是子配置类,只负责扫描controller注解

spring是父配置类:

//Spring的容器不扫描Controller,父容器
@ComponentScan(value = {"com.ldc."},excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class RootConfig
{}

springmvc是子配置类:

//SpringMVC只扫描Controller,子容器
//useDefaultFilters = false 禁用默认的过滤规则
@ComponentScan(value = {"com.ldc"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class AppConfig {

}


3.我们再来写一个Controller和Service进行测试

@Controller
public class HelloController {

    @Autowired
    private HelloService helloService;

    @ResponseBody
    @RequestMapping("hello")
    public String hello() {
        String hello = helloService.sayHello("tomcat");
        return hello;
    }
}
@Service
public class HelloService {

    public String sayHello(String name) {
        return "Hello," + name;
    }
}


springmvc-定制与接管SpringMVC

以前进行的xml配置:

将springmvc处理不了的请求交给tomcat的模板引擎解析,这样静态资源就可以访问
<mvc:default-servlet-handler/>
springmvc高级功能开启
<mvc:annotation-driven/>
配置拦截器
<mvc:interceptors><mvc:interceptors/>
配置视图映射(发送的请求不想通过controller,只想直接地跳转到目标页面)
<mvc:view-controller path="/hello" view-name="hello"></mvc:view-controller>
...

注解来定制SpringMVC: @EnableWebMvc

定制SpringMVC1)、@EnableWebMvc:开启SpringMVC定制配置功能;
	<mvc:annotation-driven/>2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
	通过一个配置类来实现接口:以上是关于Spring全注解开发----Servlet 3.0的主要内容,如果未能解决你的问题,请参考以下文章

使用全注解配置Spring MVC

Spring 注解驱动WEB 注解开发

Spring注解驱动开发-----servlet3.0springmvc

Spring全注解开发---常用注解笔记整理

Spring--SSH--全注解开发

Spring注解驱动开发-AOPTx和Servlet3.0