SpringBoot自动注入分析

  • 2019 年 10 月 17 日
  • 筆記

  我们经常会被问到这么一个问题:SpringBoot相对于spring有哪些优势呢?其中有一条答案就是SpringBoot自动注入。那么自动注入的原理是什么呢?我们进行如下分析。

  1:首先我们分析项目的启动类时,发现都会加上@SpringBootApplication这个注解,我们分析这个继续进入这个注解会发现,它是由多个注解组成的,如下

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

View Code

  2:服务启动会扫描 org.springframework.boot.autoconfigure下的 META-INF/spring.factories 这个文件,这个文件中保存着springboot 启动时默认会自动注入的类,部分如下

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

View Code

  3:你是不是在其中发现了自己常用的redis,mysql等相关的类?没错,springboot会尝试加载这些类,我们以 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 这个类为例,进去看一下它的源码,部分示例如下

 1 @Configuration   2 @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })   3 @EnableConfigurationProperties(RedisProperties.class)   4 public class RedisAutoConfiguration {   5   6     /**   7      * Redis connection configuration.   8      */   9     @Configuration  10     @ConditionalOnClass(GenericObjectPool.class)  11     protected static class RedisConnectionConfiguration {  12  13         private final RedisProperties properties;  14  15         private final RedisSentinelConfiguration sentinelConfiguration;  16  17         private final RedisClusterConfiguration clusterConfiguration;  18  19         public RedisConnectionConfiguration(RedisProperties properties,  20                 ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,  21                 ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {  22             this.properties = properties;  23             this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();  24             this.clusterConfiguration = clusterConfiguration.getIfAvailable();  25         }  26  27         @Bean  28         @ConditionalOnMissingBean(RedisConnectionFactory.class)  29         public JedisConnectionFactory redisConnectionFactory()  30                 throws UnknownHostException {  31             return applyProperties(createJedisConnectionFactory());  32         }

View Code

  我们能看到这个类上加了这个注解 @ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class }) 意思就是如果你的classpath中没有这些类的话,那么这个类就不能被加载,那么这些被依赖的类在哪出现呢?没错,就在我们在pom.xml中引入的依赖所对应的包里。

  看到这里你因该就明白了,META-INF/spring.factories 文件中被列出来的那些类都会被springboot去尝试加载,但是有些模块我们没引入相关的依赖,那么这个类就会加载失败。即这个模块没有被成功加载。

  4:我们通过上面的redis的自动加载类时,看到上面还有个 @EnableConfigurationProperties(RedisProperties.class) 注解,这个注解来注入关于redis的配置信息,这个信息都在 RedisProperties.class 中保存,我们看下 RedisProperties的源码

  1 @ConfigurationProperties(prefix = "spring.redis")    2 public class RedisProperties {    3    4     /**    5      * Database index used by the connection factory.    6      */    7     private int database = 0;    8    9     /**   10      * Redis url, which will overrule host, port and password if set.   11      */   12     private String url;   13   14     /**   15      * Redis server host.   16      */   17     private String host = "localhost";   18   19     /**   20      * Login password of the redis server.   21      */   22     private String password;   23   24     /**   25      * Redis server port.   26      */   27     private int port = 6379;   28   29     /**   30      * Enable SSL.   31      */   32     private boolean ssl;   33   34     /**   35      * Connection timeout in milliseconds.   36      */   37     private int timeout;   38   39     private Pool pool;   40   41     private Sentinel sentinel;   42   43     private Cluster cluster;   44   45     public int getDatabase() {   46         return this.database;   47     }   48   49     public void setDatabase(int database) {   50         this.database = database;   51     }   52   53     public String getUrl() {   54         return this.url;   55     }   56   57     public void setUrl(String url) {   58         this.url = url;   59     }   60   61     public String getHost() {   62         return this.host;   63     }   64   65     public void setHost(String host) {   66         this.host = host;   67     }   68   69     public String getPassword() {   70         return this.password;   71     }   72   73     public void setPassword(String password) {   74         this.password = password;   75     }   76   77     public int getPort() {   78         return this.port;   79     }   80   81     public void setPort(int port) {   82         this.port = port;   83     }   84   85     public boolean isSsl() {   86         return this.ssl;   87     }   88   89     public void setSsl(boolean ssl) {   90         this.ssl = ssl;   91     }   92   93     public void setTimeout(int timeout) {   94         this.timeout = timeout;   95     }   96   97     public int getTimeout() {   98         return this.timeout;   99     }  100  101     public Sentinel getSentinel() {  102         return this.sentinel;  103     }  104  105     public void setSentinel(Sentinel sentinel) {  106         this.sentinel = sentinel;  107     }  108  109     public Pool getPool() {  110         return this.pool;  111     }  112  113     public void setPool(Pool pool) {  114         this.pool = pool;  115     }  116  117     public Cluster getCluster() {  118         return this.cluster;  119     }  120  121     public void setCluster(Cluster cluster) {  122         this.cluster = cluster;  123     }  124  125     /**  126      * Pool properties.  127      */  128     public static class Pool {  129  130         /**  131          * Max number of "idle" connections in the pool. Use a negative value to indicate  132          * an unlimited number of idle connections.  133          */  134         private int maxIdle = 8;  135  136         /**  137          * Target for the minimum number of idle connections to maintain in the pool. This  138          * setting only has an effect if it is positive.  139          */  140         private int minIdle = 0;  141  142         /**  143          * Max number of connections that can be allocated by the pool at a given time.  144          * Use a negative value for no limit.  145          */  146         private int maxActive = 8;  147  148         /**  149          * Maximum amount of time (in milliseconds) a connection allocation should block  150          * before throwing an exception when the pool is exhausted. Use a negative value  151          * to block indefinitely.  152          */  153         private int maxWait = -1;  154  155         public int getMaxIdle() {  156             return this.maxIdle;  157         }  158  159         public void setMaxIdle(int maxIdle) {  160             this.maxIdle = maxIdle;  161         }  162  163         public int getMinIdle() {  164             return this.minIdle;  165         }  166  167         public void setMinIdle(int minIdle) {  168             this.minIdle = minIdle;  169         }  170  171         public int getMaxActive() {  172             return this.maxActive;  173         }  174  175         public void setMaxActive(int maxActive) {  176             this.maxActive = maxActive;  177         }  178  179         public int getMaxWait() {  180             return this.maxWait;  181         }  182  183         public void setMaxWait(int maxWait) {  184             this.maxWait = maxWait;  185         }  186  187     }  188  189     /**  190      * Cluster properties.  191      */  192     public static class Cluster {  193  194         /**  195          * Comma-separated list of "host:port" pairs to bootstrap from. This represents an  196          * "initial" list of cluster nodes and is required to have at least one entry.  197          */  198         private List<String> nodes;  199  200         /**  201          * Maximum number of redirects to follow when executing commands across the  202          * cluster.  203          */  204         private Integer maxRedirects;  205  206         public List<String> getNodes() {  207             return this.nodes;  208         }  209  210         public void setNodes(List<String> nodes) {  211             this.nodes = nodes;  212         }  213  214         public Integer getMaxRedirects() {  215             return this.maxRedirects;  216         }  217  218         public void setMaxRedirects(Integer maxRedirects) {  219             this.maxRedirects = maxRedirects;  220         }  221  222     }  223  224     /**  225      * Redis sentinel properties.  226      */  227     public static class Sentinel {  228  229         /**  230          * Name of Redis server.  231          */  232         private String master;  233  234         /**  235          * Comma-separated list of host:port pairs.  236          */  237         private String nodes;  238  239         public String getMaster() {  240             return this.master;  241         }  242  243         public void setMaster(String master) {  244             this.master = master;  245         }  246  247         public String getNodes() {  248             return this.nodes;  249         }  250  251         public void setNodes(String nodes) {  252             this.nodes = nodes;  253         }  254  255     }  256  257 }

View Code

  发现里面的配置项基本都是有默认值的,通过上面的注解可以明白,如果配置文件中存在 spring.redis 开头的配置项,则使用配置文件中的,如果没有的话则使用文件中默认写死的配置。你是不是想到了springboot的另外一个优势:约定大于配置。

  这里我们大概了解了SpringBoot自动配置的原理和流程,里面的那些细节我们不在分析。由此,我们总结出以下几点

  1:SpringBoot的自动配置是如何实现的

  2:有关的那些注解,如@EnableAutoConfiguration, @ConditionalOnClass, @Configuration等也是SpringBoot的核心注解,也值得我们了解其用法和原理。