我是靠谱客的博主 繁荣枫叶,最近开发中收集的这篇文章主要介绍servlet 3.0 spi,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

不采用SPI

public static void main(String[] args) throws Exception {
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);

    Context appContext = tomcat.addWebapp("/", "D:\workspace\springmvc\src\main\webapp");
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Start.class);

    tomcat.addServlet(appContext,"dispatcherServlet",new DispatcherServlet(ac));
    appContext.addServletMapping("/","dispatcherServlet");

    tomcat.start();
    tomcat.getServer().await();

}

采用SPI

public static void main(String[] args) throws Exception {
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);

    tomcat.addWebapp("/", "D:\workspace\springmvc\src\main\webapp");

    tomcat.start();
    tomcat.getServer().await();

}

在采用SPI的情况下,Spring 如何得知Tomcat启动完成,将处理器放入容器中?

SPI服务发现

SPI ( Service Provider Interface),是JDK提供的一种服务发现机制。可发现并自动加载在ClassPath下的jar包中META-INF/services文件下以服务接口命名的文件内的全限定类名映射的类。当服务的提供者,提供了服务接口的一种实现之后,只需在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件即可被程序加载并使用。

SpringMVC利用Servlet3.0的SPI

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/*");
}
}

1、在项目的跟目录:也就是我们的resources目录下,创建META-INF/services目录,
2、在其下面创建javax.servlet.ServletContainerInitializer文件,
3、文件内容就为我们自定实现了javax.servlet.ServletContainerInitializer接口的实现类
4、容器启动会调用所有实现了ServletContainerInitializer接口的onStartup方法。

SpringMVC利用Servlet3.0的SPI原理

从servlet3.0开始,web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。

spring-web的jar定义了一个具体的实现类,SpringServletContainerInitializer,并且在META-INF/services/目录下定义了如下文件:

文件的具体的内容为:org.springframework.web.SpringServletContainerInitializer。

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@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);
	// 这里就会循环的调用其自定义的onStartup方法。
	for (WebApplicationInitializer initializer : initializers) {
		initializer.onStartup(servletContext);
	}
}

容器启动的时候,会通过ServiceLoader去加载value里面的所有接口或者抽象类的实现类Class,并且会将值通过参数的形式传递给onStartup的webAppInitializerClasses参数。

利用这个机制,若实现WebApplicationInitializer这个接口,我们就可以自定义的注入Servlet,或者Filter,即可以不再依赖web.xml的配置。

最后

以上就是繁荣枫叶为你收集整理的servlet 3.0 spi的全部内容,希望文章能够帮你解决servlet 3.0 spi所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部