美文网首页spring
springMVC零配置原理分析

springMVC零配置原理分析

作者: loveFXX | 来源:发表于2019-11-18 16:14 被阅读0次

    注解方式初始化

    springMVC零配置,使用注解方式初始化
    官网参考:
    https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet

    示例代码

    前言说明:使用内嵌tomcat,运行使用maven(tomcat7:run)

    pom.xml
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com</groupId>
        <artifactId>spring-mvc</artifactId>
        <version>1.0</version>
        <packaging>war</packaging>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.0.8.RELEASE</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
    <!--        <dependency>-->
    <!--        <groupId>org.apache.tomcat.embed</groupId>-->
    <!--        <artifactId>tomcat-embed-core</artifactId>-->
    <!--        <version>8.5.31</version>-->
    <!--        </dependency>-->
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>
    
    
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
        </dependencies>
        <build>
            <finalName>spring-mvc</finalName>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <port>80</port>
                        <path>/</path>
                    </configuration>
                </plugin>
                <!--tomcat7:run-->
            </plugins>
        </build>
    
    
    package com.mvc;
    public class MyWebApplicationInitializer implements WebApplicationInitializer {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            //初始化spring 容器
            AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
            ac.register( AppConfig.class);
    //        ac.setServletContext(servletCxt);
    //        ac.refresh();
            DispatcherServlet servlet = new DispatcherServlet(ac);
            ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
            registration.setLoadOnStartup(1);
    //        registration.setInitParameter("contextConfigLocation","spring mvc.xml 的地址");
            registration.addMapping("*.do");
        }
    }
    
    
    package com.mvc;
    @Controller
    public class TestController {
        @RequestMapping("/test.do")
        @ResponseBody                                                   //user 对象  Map
        public Object test(String name, HttpServletRequest request, HttpServletResponse response ){
            request.getParameter("name");
            Map hashMap = new HashMap();
            hashMap.put("key","value");
            return  hashMap;
        }
    
        @RequestMapping("/model.do")
    //    @ResponseBody
        //@ResponseBody 加注释访问静态资源 /app/index.html
        public String model(HttpServletRequest request,HttpServletResponse response){
            // /app/index.html
            return "index";
        }
    }
    
    package com;
    @Configuration
    @ComponentScan("com.mvc")
    @EnableWebMvc
    public class AppConfig implements WebMvcConfigurer{
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
    
        }
    
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    
        }
    
        @Override
        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    
        }
    
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    
        }
    
        @Override
        public void addFormatters(FormatterRegistry registry) {
    
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
        }
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
    
        }
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
    
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
    
        }
    
           //视图解析
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp( "/app/",".html" );
        }
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    
        }
        @Override
        public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        }
    
           //消息转换
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add( new FastJsonHttpMessageConverter() );
        }
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    
        }
        @Override
        public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    
        }
        @Override
        public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
    
        }
        @Override
        public Validator getValidator() {
            return null;
        }
        @Override
        public MessageCodesResolver getMessageCodesResolver() {
            return null;
        }
    }
    
    webapp/app/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>index1</h1>
    </body>
    </html>
    

    与XML比较

    注解方式与web.xml配置同样的效果
    listener作用,在web应用启动的时候能够加载spring环境
    contextConfigLocation表示资源位置
    servlet 初始化DispatcherServlet类环境

    前缀后缀及消息转换器

    实现类

    只需要在AppConfig类中,以实现WebMvcConfigurer接口。具体使用查看示例代码AppConfig类的configureViewResolvers、configureMessageConverters方法

           //视图解析
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp( "/app/",".html" );
        }
           //消息转换
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add( new FastJsonHttpMessageConverter() );
        }
    
    @Bean方式

    只需要在AppConfig配置类中

    @Bean
        public InternalResourceViewResolver internalResourceViewResolver(){
            InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
            internalResourceViewResolver.setPrefix("/");
            internalResourceViewResolver.setSuffix(".jsp");
            return internalResourceViewResolver;
        }
    

    SPI技术

    为什么web容器启动时,会调用实现类MyWebApplicationInitializer的onStartup方法
    在WebApplicationInitializer接口类的依赖包
    META-INF/services/javax.servlet.ServletContainerInitializer文件夹有SpringServletContainerInitializer(springweb容器初始化类)


    spring-web.png

    SpringServletContainerInitializer实现了jdk的ServletContainerInitializer接口
    ServletContainerInitializer是servlet规范,例如tomcat也实现了这个规范

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
        /**
         * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
         * implementations present on the application classpath.
         * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
         * Servlet 3.0+ containers will automatically scan the classpath for implementations
         * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
         * such types to the {@code webAppInitializerClasses} parameter of this method.
         * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
         * this method is effectively a no-op. An INFO-level log message will be issued notifying
         * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
         * no {@code WebApplicationInitializer} implementations were found.
         * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
         * they will be instantiated (and <em>sorted</em> if the @{@link
         * org.springframework.core.annotation.Order @Order} annotation is present or
         * the {@link org.springframework.core.Ordered Ordered} interface has been
         * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
         * method will be invoked on each instance, delegating the {@code ServletContext} such
         * that each instance may register and configure servlets such as Spring's
         * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
         * or any other Servlet API componentry such as filters.
         * @param webAppInitializerClasses all implementations of
         * {@link WebApplicationInitializer} found on the application classpath
         * @param servletContext the servlet context to be initialized
         * @see WebApplicationInitializer#onStartup(ServletContext)
         * @see AnnotationAwareOrderComparator
         */
        @Override
        public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            List<WebApplicationInitializer> initializers = new LinkedList<>();
    
            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)
                                    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);
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);
            }
        }
    

    所以tomcat启动时,将会调用到web应用的实现类SpringServletContainerInitializer的onStartup方法


    SpringServletContainerInitializer.png

    在onStartup方法内:对传过来的WebApplicationInitializer(web应用初始化)集合遍历及其执行实现类onStartup方法,最终会调用到自定义实现的MyWebApplicationInitializer方法
    所以,SpringServletContainerInitializer便是使用SPI技术,通过配置文件加进去的

    自定义SPI实现

    说明:使用内嵌tomcat,运行使用maven(tomcat7:run)

    package com.service;
    import javax.servlet.ServletContext;
    public interface MyWebInit {
        void onStartup(ServletContext context);
    }
    
    
    package com.service;
    @HandlesTypes(MyWebInit.class)
    public class MyServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
            List<MyWebInit> list = new ArrayList<>();
            for (Class<?> aClass : set) {
                try {
                    list.add((MyWebInit) aClass.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            for (MyWebInit webInit : list) {
                webInit.onStartup( servletContext);
            }
        }
    }
    
    
    package com.mvc;
    public class MyWebApplicationInitializer implements MyWebInit {
    
        public void onStartup(ServletContext servletContext)  {
            //初始化spring 容器  以注解的方式
            AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
            ac.register( AppConfig.class);
    //        ac.setServletContext(servletCxt);
    //        ac.refresh();
            DispatcherServlet servlet = new DispatcherServlet(ac);
            ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
            registration.setLoadOnStartup(1);
    //        registration.setInitParameter("contextConfigLocation","spring mvc.xml 的地址");
            registration.addMapping("*.do");
        }
    }
    webapp/META-INF/services/javax.servlet.ServletContainerInitializer
    值是com.service.MyServletContainerInitializer
    ![servletContainerInitializer.png](https://img.haomeiwen.com/i7310356/64941ed7f317d662.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    MyServletContainerInitializer实现ServletContainerInitializer。META-INF目录的javax.servlet.ServletContainerInitializer文件包含MyServletContainerInitializer类。HandlesTypes中自定义一个接口,MyWebApplicationInitializer实现这个接口。就可以实现相同的执行效果。
    

    springboot实现

    不使用内嵌的tomcat
    pom.xml中tomcat-embed-core注释去掉

    package com;
    import org.apache.catalina.Context;
    import org.apache.catalina.LifecycleListener;
    import org.apache.catalina.startup.Tomcat;
    public class AppMVC  {
        public static void main(String[] args) throws Exception  {
            Tomcat tomcat = new Tomcat();
            tomcat.setPort(80);
            //
            Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
            //只会去初始化一个 context的资源目录 并不会加载 web的生命周期
            // webapps
            // .war   文件夹
            //        tomcat.addWebapp("/","C:\\Program Files\\pro\\public-luban-project\\spring-mvc\\src\\main\\webapp");
            context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
            tomcat.start();
    
            tomcat.getServer().await();
        }
    }
    

    springboot通过main方法启动,设置tomcat,初始化Context,添加声明周期
    tomcat启动

    相关文章

      网友评论

        本文标题:springMVC零配置原理分析

        本文链接:https://www.haomeiwen.com/subject/pmavictx.html