spring

springMVC零配置原理分析

2019-11-18  本文已影响0人  loveFXX

注解方式初始化

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启动

上一篇下一篇

猜你喜欢

热点阅读