赞
踩
具体的差异
参考官方:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide
中文版
这里只是稍微记录一下我学习看到的
一.@SpringBootApplication
修饰主程序类。可以通过main方法启动项目
单个注释可用于启用这三个功能,即:@SpringBootApplication
@EnableAutoConfiguration:启用弹簧启动的自动配置机制
@ComponentScan: 在应用程序所在的包上启用扫描
@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
下面展示1.5.9与2.0的区别
SpringBootApplication修饰的类的,这里仅以web为例 1.5.9版本的,自动配置类96,最后过程为20 2.1.8版本的,自动配置类117,最后过滤为22 加载的时候,是先加载自动配置类,再获取主程序main所在的包; 1.5.9的 采用的java逐注解,替代了配置文件; 4.@SpringBootApplication中 @SpringBootConfiguration @Configuration(spring的注解) @Component组件 @EnableAutoConfiguration 通过这个注解名就可以直接找到spring.factories中的EnableAutoConfiguration对应的组件 @AutoConfigurationPackage @Import(AutoConfigurationPackages.Registrar.class) Registrar作用:将主配置类@SpringBootApplication修饰的类所在的包及子包里面的所有组件扫描到spring容器中; 所以需要将controller或者service类要放在主配置类所在包内才能生效; @Import({EnableAutoConfigurationImportSelector.class}) 作用:获取当前环境需要哪些自动组件,全部把这些自动配置类导入容器中;一开始是获取所有的96个自动配置类全名,最后过滤为20个(当前应用场景) 具体是在META-INF/spring.factories这个配置文件下;EnableAutoConfiguration/中 位置: (AutoConfigurationImportSelector.selectImports().中的getCandidateConfigurations()) 对应spring-boot-autoconfigure-1.5.9.RELEASE.jar; import:spring注解,给容器导入组件 @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) 该注解标识的类, 会被 Spring 自动扫描并且装入bean容器 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } 传入的是EnableAutoConfiguration.class; protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException var8) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8); } } 2.0以后的 2.1.9最新 在boot2.0之前.获取配置文件中的自动配置类是没有缓存的,在2.0加入了缓存 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
二.日志框架
spring默认的是jcl+log4j
而spring boot是slf4+logback,
在spring 5的时候.jcl不在放在核心包中,而是独立了出来,并且默认排除了log4j;
这样在spring boot2.0引入日志包的时候,引用是不一样的;
三.静态资源访问
资源管理:
1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;
http://localhost:8080/webjars/jquery/3.4.1/jquery.js
2)、"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
“classpath:/META‐INF/resources/”,
“classpath:/resources/”,
“classpath:/static/”,
“classpath:/public/”
“/”:当前项目的根路径
相同的资源,访问优先级"classpath:/resources/", “classpath:/static/”, “classpath:/public/” }
在spring boot 1.5.x中,resources/static等目录下的静态资源可以直接访问,并且访问路径上不用带static; * 如: * http://localhost:8080/asserts/css/signin.css * http://localhost:8080/webjars/bootstrap/4.3.1/css/bootstrap.css * * 在2.0中需要手动添加静态资源拦截路径 * SpringBoot2+中要排除静态资源路径, 因访问时默认会加/static, * * 解决方法1:因静态资源的默认目录为resources,static,public等目录 * 那么在写除外路径的时候,不需要带上这个目录前缀static,页面引用资源链接访问也不需要加这个目录static; * @Override public void addInterceptors(InterceptorRegistry registry) { //springmvc需要手动处理 registry.addInterceptor(new loginHandleInterceptor()).addPathPatterns("/**") .excludePathPatterns("/","/index.html","/user/login") .excludePathPatterns("/asserts/**","/webjars/**"); //静态资源在2.0需要是手动排除 //排除登陆页面的请求连接 } * * 解决方法2: *自定义静态资源目录 配置spring.mvc.static-path-pattern=/static/** * 这样就写除外路径的时候就可以直接写/static/**,但是资源链接访问的是需要加/static目录的 * @Override public void addInterceptors(InterceptorRegistry registry) { //springmvc需要手动处理 registry.addInterceptor(new loginHandleInterceptor()).addPathPatterns("/**") .excludePathPatterns("/","/index.html","/user/login") .excludePathPatterns("/static/**"); //静态资源在2.0需要是手动排除 //排除登陆页面的请求连接 }
四.异常处理
package com.ysy.component; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.stereotype.Component; import org.springframework.web.context.request.WebRequest; import java.util.Map; /*在1.5.9版本,org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;中 * @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>(); errorAttributes.put("timestamp", new Date()); addStatus(errorAttributes, requestAttributes); addErrorDetails(errorAttributes, requestAttributes, includeStackTrace); addPath(errorAttributes, requestAttributes); return errorAttributes; } * */ //在2.0版本, org.springframework.boot.web.servlet.error.DefaultErrorAttributes;中 /* * @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<>(); errorAttributes.put("timestamp", new Date()); addStatus(errorAttributes, webRequest); addErrorDetails(errorAttributes, webRequest, includeStackTrace); addPath(errorAttributes, webRequest); return errorAttributes; } * */ //给容器中加入我们自己定义的ErrorAttributes @Component public class MyErrorAttributes extends DefaultErrorAttributes { /* //返回值的map就是页面和json能获取的所有字段 //1.5.9版本 @Override public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace); map.put("company","atguigu"); //我们的异常处理器携带的数据 Map<String,Object> ext = (Map<String, Object>) requestAttributes.getAttribute("ext", 0); map.put("ext",ext); return map; }*/ //2.1.8版本 @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); map.put("company","atguigu"); //我们的异常处理器携带的数据 Map<String,Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0); map.put("ext",ext); return map; } }
五.内置容器如tomcat的配置修改
配置文件的定义没有变化,但是通知定制器修改发生了变化
//Spring Boot 1.x: /* @Bean //一定要将这个定制器加入到容器中,对于所有容器生效 public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer() { return new EmbeddedServletContainerCustomizer() { //定制嵌入式的Servlet容器相关的规则 @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.setPort(8083); } }; }*/ //在2.x版本改为实现 WebServerFactoryCustomizer 接口的 customize 方法 @Bean public WebServerFactoryCustomizer webServerFactoryCustomizer() { return new WebServerFactoryCustomizer() { @Override public void customize(WebServerFactory factory) { ConfigurableWebServerFactory serverFactory = (ConfigurableWebServerFactory)factory; //配置优先级高于配置文件 serverFactory.setPort(8081); } }; } //优化写法 @Bean public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){ //WebServerFactoryCustomizer<ConfigurableWebServerFactory> factory = f -> f.setPort(8085); //return factory; //最简写法 配置优先级高于配置文件 return f -> f.setPort(8085); };
再看看定制器
1.5.9 @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties implements EmbeddedServletContainerCustomizer, EnvironmentAware, Ordered { /** * Server HTTP port. */ private Integer port; 在2.0.x 去掉了EmbeddedServletContainerCustomizer,取而代之的是WebServerFactoryCustomizer(函数式接口) @FunctionalInterface public interface WebServerFactoryCustomizer<T extends WebServerFactory> { /** * Customize the specified {@link WebServerFactory}. * @param factory the web server factory to customize */ void customize(T factory); } @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { /** * Server HTTP port. */ private Integer port;
再看看2个版本的目录结构也不一样:
1.5.9的定制器EmbeddedServletContainerCustomizer目录
这里比如具体的tomcat配置:
基于EmbeddedServletContainerFactory接口
TomcatEmbeddedServletContainerFactory
自动配置类:EmbeddedServletContainerAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
/**
* Nested configuration if Tomcat is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
2.1.8的
定制器WebServerFactoryCustomizer的目录
这里比如具体的tomcat配置:,基于WebServerFactory接口
TomcatServletWebServerFactory
自动配置类应该是:ServletWebServerFactoryAutoConfiguration
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
六.默认的连接池发生了变化
性能方面 HikariCP>Druid>tomcat-jdbc>dbcp>c3p0
1.5.9的是tomcat的,2.0.x的是HikariCP
我这里使用的oracle 12c,注意
1.5.9版本的跟2.0.x之后的驱动名不一样:
spring:
datasource:
username: xiaoming
password: 123456
url: jdbc:oracle:thin:@localhost:1521:orcltest
2.0.x的是 driver-class-name: oracle.jdbc.OracleDriver
#1.5.9的是 driver-class-name: oracle.jdbc.driver.OracleDriver
依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc8</artifactId> <version>12.2.0.1</version> </dependency> 测试代码 @RunWith(SpringRunner.class) @SpringBootTest public class Springboot159ApplicationTests { Logger log= LoggerFactory.getLogger(getClass()); @Autowired DataSource dataSource; @Test public void contextLoads() throws SQLException { System.out.println(dataSource.getClass()); Connection connection = dataSource.getConnection(); System.out.println(connection); connection.close(); } } 展示: 1.5.9 2019-10-05 15:16:28.664 INFO 4536 --- [ main] com.ysy.Springboot159ApplicationTests : info class org.apache.tomcat.jdbc.pool.DataSource ProxyConnection[PooledConnection[oracle.jdbc.driver.T4CConnection@4364863]] 2.1.9的 HikariProxyConnection@1139915666 wrapping oracle.jdbc.driver.T4CConnection@3727f0ee
1.5.9的依赖
2.1.9的依赖
七,启动配置原理
关键的几个类
ApplicationContextInitializer
SpringApplicationRunListener
ApplicationRunner
CommandLineRunner
1.5.9的
public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } 然后调用SpringApplication.initialize方法 private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } 然后调用SpringApplication的 public ConfigurableApplicationContext run(String... args) {
2.1.9的,类似
SpringApplication下
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
SpringApplication下
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。