【SpringMVC】零配置实现原理

1.配置方式的springMVC

官方地址

  • 初始化主要任务
    1. 初始化spring容器
    2. 添加Servlet
  • 在使用springMVC时通常我们是配置了web.xml,在里面通过ContextLoadLisener和DispatherServlet来加载我们的配置文件初始化spring容器,这样配置的原因主要是Tomcat会主动加载web.xml;在Servlet3.0以后可以不需要通过配置文件的方式完成,直接通过实现接口webApplicationInitalizer,tomcat也会主动执行该接口的方法;只要在该方法中向tomcat中添加servlet同样可以实现配置文件的效果

2.无xml SpringMVC实现

  • 此处用到了再tomcat启动时调用的一个接口, 以此来实现无配置的方式初始化容器以及添加Servlet

1.WebApplicationInitializer

  • 代码实现
    1. 初始化spring容器
    2. 添加Servlet
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

2.实现配置类AppConfig

  • 这里就相当于在以前的spring-mvc的配置文件中配置的包扫描和视图解析器
@Configuration
@ComponentScan("com.hgy")
public class AppConfig {

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

3.为什么tomcat会主动执行WebApplicationInitializer

3.1 spring源码
  • 根据调试可以发现调用webApplicationInitializer的startUp方法是SpringServletContainerInitializer, 而这个类又实现了ServletContainerInitializer接口至此我们大致明白tomcat真正执行的类是javax.servlet.ServletContainerInitializer; 如果你也去实现该接口你会发现不会执行你的代码
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(@Nullable 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)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).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);
            }

        }
    }
}
3.2 ServletContainerInitializer需要的配置
  1. 在resources下面添加META-INF/services文件夹
  2. 在文件夹中添加配置文件javax.servlet.ServletContainerInitializer
  3. 在配置文件中添加你的ServletContainerInitializer的实现类权限定类名
  4. 在实现类上添加上HandlesTypes注解,添加需要处理的类型

在这里插入图片描述

4. 实现自己的ServletContainerInitializer

  • 添加配置文件
    在这里插入图片描述

  • 代码实现(不加处理类型注解)

public class MyServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("==================");
    }
}
  • 代码实现(添加注解)
    1. Test必须是接口
    2. Test接口需要有实现类
@HandlesTypes(Test.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println(set.isEmpty());
    }
}

5.总结

  • ServletContainerInitializer这个接口能在tomcat启动的时候动态添加Servlet、Filter等组件;使用需要一定要在meta-inf下添加配置文件
  • 该接口的实现类还可以利用HandleTypes来自己扩展需要处理的接口,如Spring中利用SpringServletContainerInitializer实现该接口然后在扩展处理了WebApplicationInitializer,以此屏蔽了底层实现;调式时观察堆栈发现调用源头
  • 如果是通用功能可以写在一个jar中,这样就能动态的插拔jar实现功能的动态扩展,对代码毫无侵入性
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值