0
点赞
收藏
分享

微信扫一扫

springBoot面试题

什么是 Spring Boot?

为什么要用 Spring Boot?

  • 独立运行
  • 简化配置
  • 自动配置
  • 无代码生成和XML配置
  • 应用监控

Spring Boot 的核心配置文件有哪几个?它们的区别是什么?

  • Spring Boot 中有以下两种配置文件:
    • bootstrap (.yml 或者 .properties)
    • application (.yml 或者 .properties)
  • bootstrap/ application 的区别:
  • bootstrap/ application 的应用场景:

Spring Boot 的配置文件有哪几种格式?它们有什么区别?

  • .properties 和 .yml,它们的区别主要是书写格式不同。
    • 1).properties
      app.user.name = javastack
    • 2).yml
    app:
    user:
    name: javastack

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

  • 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
    • @SpringBootConfiguration:组合了 @Configuration注解,实现配置文件的功能。
    • @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
    • @ComponentScan:Spring组件扫描。

开启 Spring Boot 特性有哪几种方式?

  • 有以下两种方式:
  1. 继承spring-boot-starter-parent项目
<parent>  
<groupId>org.springframework.boot</groupId>   
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
  1. 导入spring-boot-dependencies项目依赖
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    <dependencies>
</dependencyManagement>
  • Spring Boot依赖注意点:
  1. 属性覆盖只对继承有效
<properties>
    <slf4j.version>1.7.25<slf4j.version>
</properties>

Spring Boot 需要独立的容器运行吗?

运行 Spring Boot 有哪几种方式?

  1. 打包用命令或者放到容器中运行
  2. 用 Maven/Gradle 插件运行
  3. 直接执行 main 方法运行

Spring Boot 自动配置原理是什么?

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);
 
    }
 
}
/**
 * The location to look for factories.
 * <p>Can be present in multiple JAR files.
 */
  • 再来看看数据源自动配置的实现注解


    @Configuration,@ConditionalOnClass就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。

Spring Boot 的目录结构是怎样的?

你如何理解 Spring Boot 中的 Starters?

  • Starters是什么:
  • Starters命名:
  • Starters分类:
  1. Spring Boot应用类启动器


  2. Spring Boot生产启动器


  3. Spring Boot技术类启动器


  4. 其他第三方启动器

如何在 Spring Boot 启动的时候运行一些特定的代码?

CommandLineRunner:启动获取命令行参数


ApplicationRunner:启动获取应用启动的时候参数


使用方式:


或者这样


启动顺序:

Spring Boot 有哪几种读取配置的方式?

  • 读取application文件:
    • 在application.yml或者properties文件中添加:
info.address=USA

info.company=Spring

info.degree=high

一、@Value注解读取方式:


二、@ConfigurationProperties注解读取方式:


读取指定文件:

db.username=root

db.password=123456

一、@PropertySource+@Value注解读取方式:


二、@PropertySource+@ConfigurationProperties注解读取方式:


三、Environment读取方式:

总结

Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?

  • 属性配置日志:
  • 参考配置:



    如:


  • 自定义日志文件:
  • 读取系统环境属性:


  • 读取当前应用Environment中的属性:


  • Spring Boot也支持通过springProfile来加载不同profiles下的配置:


SpringBoot 实现热部署有哪几种方式?

在Spring Boot实现代码热部署是一件很简单的事情,代码的修改可以自动部署并重新热启动项目。

一、引用devtools依赖:

二、自定义配置热部署:

三、Intellij Idea修改:

1、勾上自动编译或者手动重新编译

File > Settings > Compiler-Build Project automatically

2、注册

ctrl + shift + alt + / > Registry > 勾选Compiler autoMake allow when app running

  • 注意事项:
  • 下面是devtools自动配置的部分源码:


你如何理解 Spring Boot 配置加载顺序?

1、properties文件;

2、YAML文件;

3、系统环境变量;

4、命令行参数;

等等……

  • 配置属性加载的顺序如下:
1、开发者工具 `Devtools` 全局配置参数;
 
2、单元测试上的 `@TestPropertySource` 注解指定的参数;
 
3、单元测试上的 `@SpringBootTest` 注解指定的参数;
 
4、命令行指定的参数,如 `java -jar springboot.jar --name="Java技术栈"`;
 
5、命令行中的 `SPRING_APPLICATION_JSONJSON` 指定参数, 如 `java -Dspring.application.json='{"name":"Java技术栈"}' -jar springboot.jar`
 
6、`ServletConfig` 初始化参数;
 
7、`ServletContext` 初始化参数;
 
8、JNDI参数(如 `java:comp/env/spring.application.json`);
 
9、Java系统参数(来源:`System.getProperties()`);
 
10、操作系统环境变量参数;
 
11、`RandomValuePropertySource` 随机数,仅匹配:`ramdom.*`;
 
12、JAR包外面的配置文件参数(`application-{profile}.properties(YAML)`)
 
13、JAR包里面的配置文件参数(`application-{profile}.properties(YAML)`)
 
14、JAR包外面的配置文件参数(`application.properties(YAML)`)
 
15、JAR包里面的配置文件参数(`application.properties(YAML)`)
 
16、`@Configuration`配置文件上 `@PropertySource` 注解加载的参数;
 
17、默认参数(通过 `SpringApplication.setDefaultProperties` 指定);
  • 数字小的优先级越高,即数字小的会覆盖数字大的参数值,我们来实践下,验证以上配置参数的加载顺序。

1、在主应用程序中添加 Java 系统参数



2、在 application.properties 文件中添加属性



3、在 application-dev.properties 文件中添加属性


4、添加测试类


  • 运行 test 单元测试,程序输出:


Spring Boot 如何定义多套不同环境配置?

applcation.properties

application-dev.properties

application-test.properties

application-prod.properties
  • 然后在applcation.properties文件中指定当前的环境spring.profiles.active=test,这时候读取的就是application-test.properties文件。
  • 基于yml文件类型
  • 基于Java代码
  • 指定Profile

Spring Boot 可以兼容老 Spring 项目吗,如何做?

保护 Spring Boot 应用有哪些方法?

Spring Boot 2.X 有什么新特性?与 1.X 有什么区别?

JavaBean是什么时候创建的?

@Controller
public class TestController {
    public TestController(){
        System.out.println("TestController 创建了");
    }
}

1.直接启动,通过日志可以看到TestController 被创建了

2018-07-02 17:38:58.083  INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-02 17:38:58.088  INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-02 17:38:58.088  INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-02 17:38:58.089  INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-02 17:38:58.089  INFO 7076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
TestController 创建了
2018-07-02 17:38:58.483  INFO 7076 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@78dd667e: startup date [Mon Jul 02 17:38:55 CST 2018]; root of context hierarchy
2018-07-02 17:38:58.558  INFO 7076 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-07-02 17:38:58.559  INFO 7076 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-07-02 17:38:58.587  INFO 7076 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-02 17:38:58.587  INFO 7076 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]

2.设置断点进行调试

  • 断点一:启动位置
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}
  • 断点二: run 方法内部
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            analyzers = new FailureAnalyzers(context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }
  • 该方法就是Spring Boot项目启动时 所要执行的代码,根据阅读可以分为以下步骤:
  1. 加载环境变量
  2. 创建上下文 ConfigurableApplicationContext 对象
  3. 准备上下文 prepareContext()
  4. 刷新上下文 refreshContext()
  5. 刷新之后的处理 afterRefresh()

TestController 创建了

  • 断点三:
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
 
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
 
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
 
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
 
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
 
                // Initialize message source for this context.
                initMessageSource();
 
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
 
                // Initialize other special beans in specific context subclasses.
                onRefresh();
 
                // Check for listener beans and register them.
                registerListeners();
 
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
 
                // Last step: publish corresponding event.
                finishRefresh();
            }

TestController 创建了

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }
 
        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }
 
        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }
 
        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);
 
        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();
 
        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }
通过调试我们可以发现,执行beanFactory.preInstantiateSingletons();这个方法之后,TestController 被创建了,那么我接下来要做的就是看这个方法具体怎么实现的,调试过程中,我们通过shift+alt+F7 强制进入代码,可以看到其具体实现如下:

@Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }
 
        // Iterate over a copy to allow for init methods which in turn register new bean definitions.
        // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
 
        // Trigger initialization of all non-lazy singleton beans...
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
 
        // Trigger post-initialization callback for all applicable beans...
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        @Override
                        public Object run() {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

总结

SpringApplication.run(App.class,args) 
refreshContext(context) 
finishBeanFactoryInitialization(beanFactory) 
beanFactory.preInstantiateSingletons() 
getBean(beanName) 
doGetBean()

SpringBoot框架中,JavaBean都是单例的吗?多例怎么设置?

  • singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;

  • prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

====下面是在web项目下才用到的===

  • request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听;

  • session:每次会话,同上;

  • global session:全局的web域,类似于servlet中的application。

为什么spring要默认是单例呢?原因有二:

1、为了性能:这个不用废话了,单例不用每次都new,当然快了。

2、不需要多例:不需要多例会让很多人迷惑,因为spring mvc官方也没明确说不可以多例。

@Controller
public class MultViewController {   
    private int index = 0;//非静态
    @RequestMapping("/show")
    public String toShow(ModelMap model) {
        System.out.println(++i);
        return"show";
    }
    @RequestMapping("/test")
    public String test() {
        System.out.println(++i);
        return"test";
    }
}

最佳实践:

1、不要在controller中定义成员变量。

2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式。

举报

相关推荐

0 条评论