Spring Boot 面试,一个问题就干趴下了!
最近栈长面试了不少人,其中不乏说对 Spring Boot 非常熟悉的,然后当我问到一些 Spring Boot 核心功能和原理的时候,没人能说得上来,或者说不到点上,可以说一个问题就问趴下了!
这是我的问题:
我看你上面写了熟悉 Spring Boot,那你能讲下为什么我们要用 Spring Boot 吗?
下面我列几个最常见的三个回答:
A:Spring Boot 最主要是不用 XML 配置,可以用 Java 来配置 bean,省去了许多配置文件。
我又问:Spring 本身就可以用 Java 配置代替 XML 配置,和 Spring Boot 有什么关系呢?
然后对方就吱吱唔唔了……
B:Spring Boot 我们用来做 Spring Cloud 微服务。
我又问:微服务和 Spring Boot 有什么关系?不用 Spring Boot 行不行?
然后对方就吱吱唔唔了……
C:Spring Boot 可以打 jar 包部署,内部集成了Tomcat。
这个确实是 Spring Boot 的特色,但是我还是觉得没有答到关键点上。
然后我继续问,如果不考虑打 jar 包部署呢,然后就没然后了……
为什么我们要用 Spring Boot,显然上面三个求职者没有答到关键点上,Spring Boot 最重要的功能是:自动配置。
为什么说是自动配置?
Spring Boot 的开启注解是:@SpringBootApplication,其实它就是由下面三个注解组成的:
- @Configuration
- @ComponentScan
- @EnableAutoConfiguration
上面三个注解,前面两个都是 Spring 自带的,和 Spring Boot 无关,所以说上面的回答的不是在点上。具体请看这篇文章:Spring Boot 最核心的 3 个注解详解。
所以说 Spring Boot 最最核心的就是这个 @EnableAutoConfiguration
注解了,它能根据类路径下的 jar 包和配置动态加载配置和注入bean。
举个例子,比如我在 lib 下放一个 druid 连接池的 jar 包,然后在 application.yml 文件配置 druid 相关的参数,Spring Boot 就能够自动配置所有我们需要的东西,如果我把 jar 包拿掉或者把参数去掉,那 Spring Boot 就不会自动配置。
这样我们就能把许多功能做成公共的自动配置的启动器(starters),其实 druid 连接池就是这么做的,它提供了针对 Spring Boot 的启动器:druid-spring-boot-starter。
有了这个自动配置的启动器,我们就能非常简单的使用它,
先添加 jar 包依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
再添加相关参数:
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
……
如果是传统的项目,我们要自己手动写一大堆的配置,而且还不灵活,有了这个启动器,我们就可以做到简单集成。具体大家可以看 druid-spring-boot-starter 是怎么实现的,也可以参考之前写的文章:Spring Boot自动配置原理、实战。
所以,这才是 Spring Boot 的核心,这才是我们为什么使用 Spring Boot 的原因。如果答不到这个关键点,那真没有掌握到 Spring Boot 的核心所在。
Spring Boot自动配置原理、实战
Spring Boot自动配置原理
Spring Boot的自动配置注解是@EnableAutoConfiguration, 从上面的@Import的类可以找到下面自动加载自动配置的映射。
-
org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames(Class<?>, ClassLoader)
-
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
-
String factoryClassName = factoryClass.getName();
-
try {
-
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
-
lassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
-
List<String> result = new ArrayList<String>();
-
while (urls.hasMoreElements()) {
-
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 ex) {
-
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
-
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
-
}
-
}
这个方法会加载类路径及所有jar包下META-INF/spring.factories配置中映射的自动配置的类。
-
/**
-
* The location to look for factories.
-
* <p>Can be present in multiple JAR files.
-
*/
-
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
查看Spring Boot自带的自动配置的包: spring-boot-autoconfigure-1.5.6.RELEASE.jar,打开其中的META-INF/spring.factories文件会找到自动配置的映射。
-
# Auto Configure
-
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
-
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
-
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
-
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
-
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
-
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
-
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
-
...
再来看看数据源自动配置的实现注解
-
@Configuration
-
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
-
@EnableConfigurationProperties(DataSourceProperties.class)
-
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
-
public class DataSourceAutoConfiguration {
-
...
@Configuration,@ConditionalOnClass就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
自动配置实战
所以,了解了自动配置的原理,来自己实现一个自动配置的玩意其实很简单。
添加配置类:
-
import org.slf4j.Logger;
-
import org.springframework.context.EnvironmentAware;
-
import org.springframework.core.env.Environment;
-
-
import com.oceanpayment.common.utils.logger.LoggerUtils;
-
-
public class EnvConfig implements EnvironmentAware {
-
-
private final Logger logger = LoggerUtils.getLogger(this);
-
-
private Environment env;
-
-
public String getStringValue(String key) {
-
return env.getProperty(key);
-
}
-
-
public Long getLongValue(String key) {
-
String value = getStringValue(key);
-
try {
-
return Long.parseLong(value);
-
} catch (Exception e) {
-
logger.error("字符串转换Long失败:{} = {}", key, value);
-
}
-
return 0L;
-
}
-
-
public int getIntValue(String key) {
-
return getLongValue(key).intValue();
-
}
-
-
@Override
-
public void setEnvironment(Environment environment) {
-
this.env = environment;
-
}
-
-
}
添加自动配置类:
-
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.core.env.PropertyResolver;
-
-
@Configuration
-
@ConditionalOnClass(PropertyResolver.class)
-
public class EnvAutoConfig {
-
-
@Bean
-
public EnvConfig envConfig() {
-
return new EnvConfig();
-
}
-
-
}
创建META-INF/spring.factories文件,添加自动配置映射:
-
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
-
com.oceanpayment.common.config.env.EnvAutoConfig
这样就搞定了。
查看自动配置报告
怎么查看自己加的自动配置类有没有被加载,或者查看所有自动配置激活的和未激活的可以通过以下几种试查看。
-
spring-boot:run运行的在对话框Enviroment中加入debug=true变量
-
java -jar xx.jar --debug
-
main方法运行,在VM Argumanets加入-Ddebug
-
直接在application文件中加入debug=true
-
如果集成了spring-boot-starter-actuator监控,通过autoconfig端点也可以查看。
启动后会在控制台看到以下自动配置报告信息:
-
=========================
-
AUTO-CONFIGURATION REPORT
-
=========================
-
-
-
Positive matches:
-
-----------------
-
-
AopAutoConfiguration matched:
-
- @ConditionalOnClass found required classes 'org.springframework.context.annotation.EnableAspectJAutoProxy', 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
-
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
-
-
...
-
-
EnvAutoConfig matched:
-
- @ConditionalOnClass found required class 'org.springframework.core.env.PropertyResolver'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
-
-
ErrorMvcAutoConfiguration matched:
-
- @ConditionalOnClass found required classes 'javax.servlet.Servlet', 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
-
- @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
-
-
ErrorMvcAutoConfiguration#basicErrorController matched:
-
- @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.web.ErrorController; SearchStrategy: current) did not find any beans (OnBeanCondition)
-
-
...
-
-
-
Negative matches:
-
-----------------
-
-
ActiveMQAutoConfiguration:
-
Did not match:
-
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
-
-
AopAutoConfiguration.JdkDynamicAutoProxyConfiguration:
-
Did not match:
-
- @ConditionalOnProperty (spring.aop.proxy-target-class=false) found different value in property 'proxy-target-class' (OnPropertyCondition)
-
-
ArtemisAutoConfiguration:
-
Did not match:
-
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory' (OnClassCondition)
-
-
BatchAutoConfiguration:
-
Did not match:
-
- @ConditionalOnClass did not find required class 'org.springframework.batch.core.launch.JobLauncher' (OnClassCondition)
-
-
...
Positive matches:已经启用的自动配置
Negative matches:未启用的自动配置
从报告中看到自己添加的EnvAutoConfig已经自动配置了。
从https://www.cnblogs.com/javastack/p/10635333.html转载