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自动配置的原理和流程,里面的那些细节我们不在分析。同理,对于那些第三方或者我们自己写的starter,springboot启动的时候会扫描starter的jar包下 META-INF/spring.factories 这个文件,解析内容并加载里面对用的类。

  由此,我们总结出以下几点

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

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

 

posted @ 2019-10-17 18:43  李斯特王  Views(2118)  Comments(0Edit  收藏  举报