1. 程式人生 > >SpringBoot自動注入分析

SpringBoot自動注入分析

  我們經常會被問到這麼一個問題: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的核心註解,也值得我們瞭解其用法和原理。