我是靠谱客的博主 潇洒鼠标,最近开发中收集的这篇文章主要介绍springboot零配置与内嵌tomcat原理(附基于spring项目实现零配置与内嵌tomcat代码),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

传统spring项目所需xml配置:web.xml、application.xml、spring-mvc.xml

springboot怎么做到零配置与内嵌tomcat?

1、spring4开始就可以不用xml来配置了(注解/java config),用代码可完成上述三个xml的工作,替代xml。

最简单的spring web项目只需要两个依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.1.RELEASE</version>
        </dependency>

2、内嵌tomcat,tomcat是用java写的,tomcat加入到项目中,启动即可

引入jar包(第二个jar包避免报错,核心为第一个)

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>
            <version>8.5.43</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>8.5.45</version>
        </dependency>

此方案基于servlet3,spi机制

spi,即service privider interface,是jdk为厂商和插件提供的一种解耦机制。

spi的具体规范为:当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并通过反射机制实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader

在spring-web中,定义了javax.servlet.ServletContainerInitializer的实现类org.springframework.web.SpringServletContainerInitializer,其注释对spi做了简单说明

 

在SpringServletContainerInitializer中会拿到所有WebApplicationInitializer的实现类(HandlesTypes注解),执行onStartup方法

 

	@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与servlet对应关系

 

代码实践

spring对DispatcherServlet的注册与实例化例举了两种方式,一种为代码注册,一种为xml注册,所以使用代码注册即可替代web.xml。

https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/web.html#spring-web

拷贝官网代码,ac.refresh()可注释,因为在DispatcherServlet 的父类FrameworkServlet中initServletBean方法->initWebApplicationContext->configureAndRefreshWebApplicationContext方法最后会:wac.refresh(),会执行此方法,此方法为spring核心方法。

而不注释时,由于还没有初始化servlet,如果使用了某些servlet注解,ac.refresh()会报错。

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {
        System.out.println("init spring context");
        // 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);
        // 将DispatcherServlet注册到tomcat
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

而application.xml及spring-mvc.xml可用注解来替代,如:

@Configuration
@ComponentScan({"com.demo"})
public class AppConfig {
}

至此,三个xml都不需要了,实现了0配置。

tomcat内嵌则如上文,利用spi机制。模拟springboot启动方式,写SpringApplication 类如下

public class SpringApplication {
    public static void run(){
        System.out.println("init tomcat");
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        tomcat.addWebapp("/","d:\spring\");
        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }


    }
}

启动类

public class Application {
    public static void main(String[] args) {
        SpringApplication.run();
    }
}

简单写一个controller

@Controller
public class DemoController {

    @RequestMapping("/index")
    @ResponseBody
    public String index(){
        System.out.println("hello");
        return "hello";
    }
}

执行main方法启动,首先初始化tomcat,然后根据spi机制,会执行MyWebApplicationInitializer 的onStartup初始化spring环境和servlet,至此模拟springboot成功

访问http://localhost:8080/app/index,请求成功

 

最后

以上就是潇洒鼠标为你收集整理的springboot零配置与内嵌tomcat原理(附基于spring项目实现零配置与内嵌tomcat代码)的全部内容,希望文章能够帮你解决springboot零配置与内嵌tomcat原理(附基于spring项目实现零配置与内嵌tomcat代码)所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部