• 基于注解的springmvc开发


    原理简析

    1. 背景知识:org.springframework.web.ServletContainerInitializer接口

    在基于注解的servlet开发中,ServletContainerInitializer接口用于代替web.xml。它只有一个方法:onStartup,可以在其中注册servlet、拦截器(Filter)、监听器(Listener)这三大组件。另外,ServletContainerInitializer还可以使用@HandlesTypes在onStartup方法的参数列表中注入感兴趣的类。servlet容器启动时,会扫描每个jar包的项目根目录下的/META-INF/services/javax.servlet.ServletContainerInitializer文件,执行这个文件中指定的ServletContainerInitializer接口的实现类的onStartup方法。

     

    2. org.springframework.web包提供的ServletContainerInitializer实现类

    org.springframework.web包的/META-INF/services/javax.servlet.ServletContainerInitializer文件指定了ServletContainerInitializer接口的实现类:SpringServletContainerInitializer,首先来看一下这个类的spring源码:

    package org.springframework.web;
    
    @HandlesTypes(WebApplicationInitializer.class)  //(1)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
        @Override
        public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
    
            if (webAppInitializerClasses != null) {
                for (Class<?> waiClass : webAppInitializerClasses) {
                    // Be defensive: Some servlet containers provide us with invalid classes,
                    // no matter what @HandlesTypes says...
                    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                        try {
                            initializers.add((WebApplicationInitializer) waiClass.newInstance());  //(2)
                        }
                        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);
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);  //(3)
            }
        }
    
    }

    (1) 通过@HandlesType注解在onStartup方法的参数列表中注入感兴趣的类,即WebApplicationInitializer;

    (2) 将WebApplicationInitializer的每个实现类,都新建一个实例,并放入initializers列表中;

    (3) 遍历initializers列表,对每个WebApplicationInitializer实例执行其onStartup方法。

    那么问题来了:WebApplicationInitializer有哪些实现类,是用来干什么的?

    3. WebApplicationInitializer的实现类及其功能

    WebApplicationInitializer的实现类有很多,重点看一下AbstractAnnotationConfigDispatcherServletInitializer

    package org.springframework.web.servlet.support;
    
    public abstract class AbstractAnnotationConfigDispatcherServletInitializer
            extends AbstractDispatcherServletInitializer {
    
        @Override
        @Nullable
        protected WebApplicationContext createRootApplicationContext() {
            Class<?>[] configClasses = getRootConfigClasses();
            if (!ObjectUtils.isEmpty(configClasses)) {
                AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
                context.register(configClasses);
                return context;
            }
            else {
                return null;
            }
        }
    
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            Class<?>[] configClasses = getServletConfigClasses();
            if (!ObjectUtils.isEmpty(configClasses)) {
                context.register(configClasses);
            }
            return context;
        }
    
        @Nullable
        protected abstract Class<?>[] getRootConfigClasses();
    
        @Nullable
        protected abstract Class<?>[] getServletConfigClasses();
    
    }

    这个类提供了两个方法的实现,以及两个抽象方法供子类继承

    (1) createRootApplicationContext:创建根容器;

    (2) createServletApplicationContext:创建servlet容器;

    (3) getRootConfigClasses:抽象类,用于注册根容器的配置类,相当于spring.xml;

    (4) getServletConfigClasses:抽象的类,用于注册servlet容器的配置类,相当于springmvc.xml;

    另外,它还从AbstractDispatcherServletInitializer类继承了getServletMappings方法,用于注册servlet的映射。

    因此,我们可以自定义一个WebApplicationInitializer的实现类,继承AbstractAnnotationConfigDispatcherServletInitializer;在servlet容器启动时,会创建spring根容器和servlet容器,代替web.xml配置文件。同时,我们可以看到,在基于注解的springmvc开发中,真正用于代替web.xml的是WebApplicationInitializer,而并不是ServletContainerInitializer,这与本文开头提到的基于注解的servlet开发有些区别。

    4. 根容器和servlet容器

    根容器用于管理@Service、@Repository等业务逻辑层和数据库交互层组件;

    servlet容器用于管理@Controller、视图解析器、拦截器等跟页面处理有关的组件。

    使用步骤

    0. 导包或添加依赖:spring-web、spring-webmvc

    1. 编写数据库访问层、业务逻辑层、控制层等组件,这个跟基于配置文件的springmvc没有区别;

    2. 编写根容器和servlet容器的配置类,这里不需要添加@Configuration注解;

    3. 自定义WebApplicationInitializer,继承AbstractAnnotationConfigDispatcherServletInitializer;

    4. 在3的实现类中注册根容器和servlet容器的配置类,以及servlet映射;

    5. 在servlet容器中中注册视图解析器、拦截器等组件,用法是使servlet容器配置类实现WebMvcConfigurer接口,

        然后选择相应的方法进行注册,详见示例demo。

    示例Demo

    pom文件

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.6-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.6-SNAPSHOT</version> 
    </dependency>

    业务逻辑层组件

    package cn.monolog.annabelle.springmvc.service;
    
    import org.springframework.stereotype.Service;
    
    /**
     * 业务逻辑层组件
     * created on 2019-05-04
     */
    @Service
    public class BusinessService {
    
        public String resolve(String source) {
            return "hello " + source;
        }
    }

    控制层组件

    package cn.monolog.annabelle.springmvc.controller;
    
    import cn.monolog.annabelle.springmvc.service.BusinessService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * 控制层组件
     * created on 2019-05-04
     */
    @Controller
    @RequestMapping(value = "/business")
    public class BusinessController {
    
        //从容器中自动装配组件
        @Autowired
        private BusinessService businessService;
    
        @GetMapping(value = "/resolve")
        @ResponseBody
        public String resolve(String source) {
            String result = this.businessService.resolve(source);
            return result;
        }
    
    }

    自定义springmvc拦截器

    package cn.monolog.annabelle.springmvc.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 自定义的springmvc拦截器
     * created on 2019-05-12
     */
    public class CustomedInterceptor implements HandlerInterceptor {
    
        /**
         * 重写preHandle方法
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle正在执行...");
            return true;
        }
    
        /**
         * 重写postHandle方法
         * @param request
         * @param response
         * @param handler
         * @param modelAndView
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle正在执行...");
        }
    
        /**
         * 重写afterCompletion方法
         * @param request
         * @param response
         * @param handler
         * @param ex
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion正在执行...");
        }
    }

    根容器的配置类,用于管理数据库访问层、业务逻辑层等组件,相当于spring.xml

    package cn.monolog.annabelle.springmvc.config;
    
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * 根容器配置类
     * created on 2019-05-04
     */
    @Configuration @ComponentScan(basePackages = {"cn.monolog.annabelle.springmvc.service"}) public class RootConfig { }

    servlet容器的配置类,用于管理控制层、视图解析器等组件,相当于springmvc.xml

    可以在其中配置视图解析器、静态资源解析器、拦截器等组件

    package cn.monolog.annabelle.springmvc.config;
    
    import cn.monolog.annabelle.springmvc.interceptor.CustomedInterceptor;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.*;
    
    /**
     * servlet容器配置类
    * @EnableWebMvc相当于配置文件中的<mvc:annotation-drivern /> * created on 2019-05-04
    */ @Configuration @ComponentScan(basePackages = {"cn.monolog.annabelle.springmvc.controller"}) @EnableWebMvc public class ServletConfig implements WebMvcConfigurer { /** * 注册视图解析器 * @param registry */ @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/views/", ".jsp"); } /** * 注册静态资源解析器 * 将springmvc处理不了的请求交给tomcat * @param configurer */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } /** * 注册拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CustomedInterceptor()); } }

    自定义的WebApplicationInitializer,用于注册根容器、servlet容器、servlet映射、拦截器、监听器等,相当于web.xml

    package cn.monolog.annabelle.springmvc.initializer;
    
    import cn.monolog.annabelle.springmvc.config.RootConfig;
    import cn.monolog.annabelle.springmvc.config.ServletConfig;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    /**
     * 自定义web容器启动器
     * created on 2019-05-04
     */
    public class CustomerServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        /**
         * 注册根容器配置类
         * @return
         */
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{RootConfig.class};
        }
    
        /**
         * 注册servlet容器配置类
         * @return
         */
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{ServletConfig.class};
        }
    
        /**
         * 注册servlet的映射
         * @return
         */
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }

    测试代码

    <html>
    <head>
    <title>homepage</title>
    <style type="text/css">
        a {
            color: blueviolet;
            font-size: 20px;
        }
    </style>
    </head>
    <body>
    <a href="/annabelle/business/resolve?source=violet">start...</a>
    </body>
    </html>
  • 相关阅读:
    CF-911E.Stack Sorting(栈)
    随机算法 && CodeForces
    CF-579D."Or" Game(或运算)
    CF-242E.XOR on Segment(异或线段树)
    莫队 && 洛谷 P1494 [国家集训队]小Z的袜子
    洛谷 P4168 [Violet]蒲公英(分块)
    分块 && 洛谷 P2801 教主的魔法
    启发式合并 && U41492 树上数颜色
    使用mysqlbinlog server远程备份binlog的脚本
    mysqldump备份过程中都干了些什么
  • 原文地址:https://www.cnblogs.com/dubhlinn/p/10808879.html
Copyright © 2020-2023  润新知