zoukankan      html  css  js  c++  java
  • Spring Boot自动配置

    1. Spring Boot自动配置简介

      Spring Boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。简单点说就是它会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。自动配置充分的利用了spring 4.0的条件化配置特性,能够自动配置特定的Spring bean,用来启动某项特性。Spring Boot关于自动配置的源码在spring-boot-autoconfigure-1.3.0.x.jar内。

    2. Spring Boot自动配置使用案例

    • 启动本地redis,端口为6379,通过keys * 查看
    1 "ttlMap"
    2 "test1"
    • 创建service,直接注入StringRedisTemplate
    1 @Service
    2 public class RedisExample {
    3     @Autowired
    4     StringRedisTemplate stringRedisTemplate;
    5 
    6     public Set<String> keys() {
    7         return stringRedisTemplate.keys("*");
    8     }
    9 }    
    • 创建测试类
     1 @RunWith(SpringRunner.class)
     2 @SpringBootTest
     3 public class SpringbootRedisDemoApplicationTests {
     4     @Autowired
     5     RedisExample redisExample;
     6 
     7     @Test
     8     public void testRedis() {
     9         redisExample.keys().forEach(key->System.out.println(key));
    10     }
    11 }
    • 输出结果为
    1 ttlMap
    2 test1

      从上面的步骤可以看出,没有任何地方配置读取Redis客户端、Server、Port等信息,那么StringRedisTemplate如何知道怎么访问Redis呢?如果在application.properties中直接配置spring.redis.host, spring.redis.port等信息就会生效。StringRedisTemplate是如何读取配置的呢?答案就是“自动配置”。

    3.条件注解(@Conditional)

      Spring Boot自动配置的实现就是基于Spring 4.x 根据条件的配置来创建Bean的能力。

      @Conditional根据满足某一个特定条件创建一个特定的Bean。比如当一个jar包在一个路径下的时候,自动配置一个或多个Bean;或者只有某个Bean被创建才会创建另外一个Bean。

      例子:根据运行的系统不同,创建不同的Bean完成不同的功能。比如是window系统则自动注入WindowsListService。

    LinuxCondition.java

     1 package com.suns.springboot.ch3.conditional;
     2 
     3 import org.springframework.context.annotation.Condition;
     4 import org.springframework.context.annotation.ConditionContext;
     5 import org.springframework.core.type.AnnotatedTypeMetadata;
     6 
     7 public class LinuxCondition implements Condition {
     8     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
     9         return conditionContext.getEnvironment().getProperty("os.name").contains("Linux");
    10     }
    11 }

    WindowsCondition.java

     1 package com.suns.springboot.ch3.conditional;
     2 
     3 import org.springframework.context.annotation.Condition;
     4 import org.springframework.context.annotation.ConditionContext;
     5 import org.springframework.core.type.AnnotatedTypeMetadata;
     6 
     7 public class WindowsCondition implements Condition {
     8     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
     9         return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
    10     }
    11 }

    ListService.java

    1 package com.suns.springboot.ch3.conditional;
    2 
    3 public interface ListService {
    4     String showListCmd();
    5 }

    LinuxListService.java

    1 package com.suns.springboot.ch3.conditional;
    2 
    3 public class LinuxListService implements ListService {
    4     public String showListCmd() {
    5         return "ls";
    6     }
    7 }

    WindowsListService.java

     1 package com.suns.springboot.ch3.conditional;
     2 
     3 import org.springframework.context.annotation.Condition;
     4 import org.springframework.context.annotation.ConditionContext;
     5 import org.springframework.core.type.AnnotatedTypeMetadata;
     6 
     7 public class WindowsCondition implements Condition {
     8     public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
     9         return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
    10     }
    11 }

    ConditionConfig.java

      如果WindowsCondition里面的match方法返回true则新建一个WindowsListService并通过@Bean注入到容器,如果LinuxCondition里面的match方法返回true则新建一个LinuxListService并通过@Bean注入到容器

     1 package com.suns.springboot.ch3.conditional;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 import org.springframework.context.annotation.Conditional;
     5 import org.springframework.context.annotation.Configuration;
     6 
     7 @Configuration
     8 public class ConditionConfig {
     9 
    10     @Bean
    11     @Conditional(WindowsCondition.class)
    12     public ListService windowsListService() {
    13         return new WindowsListService();
    14     }
    15 
    16     @Bean
    17     @Conditional(LinuxCondition.class)
    18     public ListService linuxListService() {
    19         return new LinuxListService();
    20     }
    21 }

    Main.java

     1 package com.suns.springboot.ch3.conditional;
     2 
     3 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
     4 
     5 public class Main {
     6     public static void main(String[] args) {
     7         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class);
     8         ListService listService = context.getBean(ListService.class);
     9         System.out.println(context.getEnvironment().getProperty("os.name") + "系统下的列表命令为: " + listService.showListCmd());
    10     }
    11 }

    4.组合注解与元注解 

      所谓元注解其实就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合注解具备元注解的功能。如果对注解也不清楚可以参考我另外一篇博客:Java注解

      在SpringBoot的开发过程中,配置类(config)会经常使用@Configuration和@ComponentScan,我们可以把这2个注解组成一个组合注解。

    SunsConfiguration.java

     1 package com.suns.springboot.ch3.combination_annotation;
     2 
     3 import org.springframework.context.annotation.ComponentScan;
     4 import org.springframework.context.annotation.Configuration;
     5 
     6 import java.lang.annotation.*;
     7 
     8 @Target(ElementType.TYPE)
     9 @Retention(RetentionPolicy.RUNTIME)
    10 @Documented
    11 @Configuration
    12 @ComponentScan
    13 public @interface SunsConfiguration {
    14 
    15     String[] value() default {};
    16 }
    
    

    DemoService.java

     1 package com.suns.springboot.ch3.combination_annotation;
     2 
     3 import org.springframework.stereotype.Service;
     4 
     5 @Service
     6 public class DemoService {
     7 
     8     public void outputResult() {
     9         System.out.println("从组合注解配置照样获得的bean");
    10     }
    11 }

    DemoConfig.java

    1 package com.suns.springboot.ch3.combination_annotation;
    2 
    3 @SunsConfiguration("com.suns.springboot.ch3.combination_annotation")
    4 public class DemoConfig {
    5 }

    Main.java

     1 package com.suns.springboot.ch3.combination_annotation;
     2 
     3 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
     4 
     5 public class Main {
     6     public static void main(String[] args) {
     7         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
     8         DemoService demoService = context.getBean(DemoService.class);
     9         demoService.outputResult();
    10 
    11         context.close();
    12     }
    13 
    14 }

    5. Spring Boot自动化配置详解

       Spring Boot最重要的注解@SpringBootApplication,它其实是一个组合元注解(组合了SpringBootConfiguration和EnableAutoConfiguration、ComponentScan):

     1 @Target({ElementType.TYPE})
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 @Inherited
     5 @SpringBootConfiguration
     6 @EnableAutoConfiguration
     7 @ComponentScan(
     8     excludeFilters = {@Filter(
     9     type = FilterType.CUSTOM,
    10     classes = {TypeExcludeFilter.class}
    11 ), @Filter(
    12     type = FilterType.CUSTOM,
    13     classes = {AutoConfigurationExcludeFilter.class}
    14 )}
    15 )
    16 public @interface SpringBootApplication {
    17     ......
    18 }

      

      @SpringBootConfiguration是Spring Boot配置类,进入SpringBootConfiguration注解,有个@Configuration是来标识某个类是配置类

    1 @Target({ElementType.TYPE})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 @Configuration
    5 public @interface SpringBootConfiguration {
    6 }

      

      其中@EnableAutoConfiguration是Spring Boot自动化配置原理的核心,是开启自动配置功能

     1 @Target({ElementType.TYPE})
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 @Inherited
     5 @AutoConfigurationPackage
     6 @Import({EnableAutoConfigurationImportSelector.class})
     7 public @interface EnableAutoConfiguration {
     8     Class<?>[] exclude() default {};
     9 
    10     String[] excludeName() default {};
    11 }

      

      @AutoConfigurationPackage是自动配置包,是找到@SpringBootApplication主类当前路径(见下图)。其中@Import是向容器中导入组件,能够导入Configuration配置类,也能够导入实现了ImportSelector接口的类,还有动态注册Bean。在Registrar类中,获取到@SpringBootApplication主类路径

    1 @Target({ElementType.TYPE})
    2 @Retention(RetentionPolicy.RUNTIME)
    3 @Documented
    4 @Inherited
    5 @Import({Registrar.class})
    6 public @interface AutoConfigurationPackage {
    7 }

      EnableAutoConfigurationImportSelector是将所有需要导入组件以全类名的方式返回,这些组件就会添加到Spring容器中。selectImports方法中获取configurations。List configurations = getCandidateConfigurations(metadata,attributes);

    public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {
        private ConfigurableListableBeanFactory beanFactory;
        private Environment environment;
        private ClassLoader beanClassLoader;
        private ResourceLoader resourceLoader;
    
        public EnableAutoConfigurationImportSelector() {
        }
    
        public String[] selectImports(AnnotationMetadata metadata) {
            try {
                AnnotationAttributes attributes = this.getAttributes(metadata);
                List<String> configurations = this.getCandidateConfigurations(metadata, attributes);
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(metadata, attributes);
                configurations.removeAll(exclusions);
                configurations = this.sort(configurations);
                this.recordWithConditionEvaluationReport(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var5) {
                throw new IllegalStateException(var5);
            }
        }
    
        .....
    }

      

      getCandidateConfigurations方法获取configurations的方法是:List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

     1 public class EnableAutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {
     2    
     3    ......     
     4 
     5     protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
     6         return SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
     7     }
     8 
     9     ......
    10 }

      

      我们看下loadFactoryNames方法,从这里可以看出Spring Boot在启动的时候从类路径下加载所有的META-INF/spring.factories,包括其他starter里面的spring.factories(比如mybatis-plus-boot-starter)。并从中获取org.springframework.boot.autoconfigure.EnableAutoConfiguration值,将这些值注入到Spring容器中。

     1 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
     2         String factoryClassName = factoryClass.getName();
     3 
     4         try {
     5             Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
     6             ArrayList result = new ArrayList();
     7 
     8             while(urls.hasMoreElements()) {
     9                 URL url = (URL)urls.nextElement();
    10                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
    11                 String factoryClassNames = properties.getProperty(factoryClassName);
    12                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    13             }
    14 
    15             return result;
    16         } catch (IOException var8) {
    17             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    18         }
    19     }

      我们看一下spring-boot-autoconfigure-1.5.4.RELEASE.jar中的META-INF/spring.factories

      1 # Initializers
      2 org.springframework.context.ApplicationContextInitializer=
      3 org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
      4 
      5 # Application Listeners
      6 org.springframework.context.ApplicationListener=
      7 org.springframework.boot.autoconfigure.BackgroundPreinitializer
      8 
      9 # Auto Configure
     10 org.springframework.boot.autoconfigure.EnableAutoConfiguration=
     11 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
     12 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
     13 org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
     14 org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,
     15 org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,
     16 org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
     17 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
     18 org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
     19 org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
     20 org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
     21 org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,
     22 org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,
     23 org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,
     24 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,
     25 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,
     26 org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,
     27 org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,
     28 org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,
     29 org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,
     30 org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,
     31 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
     32 org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,
     33 org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,
     34 org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,
     35 org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,
     36 org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,
     37 org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,
     38 org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,
     39 org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,
     40 org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,
     41 org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,
     42 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,
     43 org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,
     44 org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,
     45 org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,
     46 org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,
     47 org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,
     48 org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,
     49 org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,
     50 org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,
     51 org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,
     52 org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,
     53 org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,
     54 org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,
     55 org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,
     56 org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,
     57 org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,
     58 org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,
     59 org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,
     60 org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,
     61 org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,
     62 org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,
     63 org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,
     64 org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,
     65 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,
     66 org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,
     67 org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,
     68 org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,
     69 org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,
     70 org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,
     71 org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,
     72 org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,
     73 org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,
     74 org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,
     75 org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,
     76 org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,
     77 org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,
     78 org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,
     79 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,
     80 org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,
     81 org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,
     82 org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,
     83 org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,
     84 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,
     85 org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,
     86 org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,
     87 org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,
     88 org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,
     89 org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,
     90 org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,
     91 org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration
     92 
     93 # Template availability providers
     94 org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=
     95 org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,
     96 org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,
     97 org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,
     98 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,
     99 org.springframework.boot.autoconfigure.velocity.VelocityTemplateAvailabilityProvider,
    100 org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider

      

      最终返回的Configuration如下:

      这里我们再自己跟进去看下前面例子中使用到的Redis到底是如何完成自动配置。我们找到RedisAutoConfiguration,我们看到这里使用了们前面讲到的条件注解,@ConditionOnClass表示classpath中拥有这些类时才会构建这个bean,在我们在maven引入Redis相关依赖包后,Spring Boot就会把RedisAutoConfiguration注入到Spring容器;@ConditionOnMissingBean表示不存在这个Bean的时候,Spring Boot就会Spring Boot就会把RedisProperties注入到Spring容器。

     1 @Configuration
     2 @ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
     3 @EnableConfigurationProperties
     4 public class RedisAutoConfiguration {
     5     public RedisAutoConfiguration() {
     6     }
     7 
     8     @Bean(
     9         name = {"org.springframework.autoconfigure.redis.RedisProperties"}
    10     )
    11     @ConditionalOnMissingBean
    12     public RedisProperties redisProperties() {
    13         return new RedisProperties();
    14     }
    15   ...  
    16 }

      

      接着我们看下RedisProperties,这里就有默认加载Redis的IP、端口等信息。到这里我们Redis相关的自动配置就已经配置完成。

     1 @ConfigurationProperties(
     2     prefix = "spring.redis"
     3 )
     4 public class RedisProperties {
     5     private int database = 0;
     6     private String host = "localhost";
     7     private String password;
     8     private int port = 6379;
     9     private int timeout;
    10     private RedisProperties.Pool pool;
    11     private RedisProperties.Sentinel sentinel;
    12 
    13     public RedisProperties() {
    14     }
    15     ...
    16 
    17 }

    附件

    在spring-boot-autoconfigure-1.3.0.x.jar的org.springframework.boot.autoconfigure.condition下常用的条件注解
    名称 作用
    @ConditionalOnBean 当容器里有指定的Bean的条件下
    @ConditionalOnClass 当类路径下有指定的类条件下
    @ConditionalOnExpression 基于SpEl表达式作为判断条件
    @ConditionalOnJava 基于JVM版本作为判断条件
    @ConditionalOnJndi 在JNDI存在的条件下查找指定的位置
    @ConditionalOnMissingBean 当容器里没有指定Bean的情况下
    @ConditionalOnMissingClass 当类路径下没有指定的类的条件下
    @ConditionalOnNotWebApplication 当前项目不是Web项目的条件下
    @ConditionalOnProperty 指定的属性是否有指定的值
    @ConditionalOnResource 类路径是否有指定的值
    @ConditionalOnSingleCandidate 当指定Bean在容器中只有一个,或者虽然有多个但是指定首选的Bean
    @ConditionalOnWebApplication 当前项目为Web项目的条件下

  • 相关阅读:
    大文件上传
    zabbix接口
    Vue 在不同的环境使用不同的接口地址
    Vue发布流程
    RabbitMQ集群一些使用细节
    Watcher 系统整体流程图
    监控系统各个模块部署
    deepin安装node和npm最新
    google安装json插件
    数据库访问性能优化 Oracle
  • 原文地址:https://www.cnblogs.com/kesuns/p/12303720.html
Copyright © 2011-2022 走看看