在前面使用SSM集成時,我們可以使用注解實現無配置化注入,但是這種依賴被進行“人工干預了的”,換句話就是說我們手動進行裝配,那么此時還沒有達到SpringBoot這種自動裝配的效果,那么究竟SpringBoot如何進行自動裝配的呢?下面我們就一探究竟
一。SpringBoot中創建對象的注解擴充
其實說白了,SpringBoot並不屬於一種新的技術,只不過Spring-Boot-Starter-xxxx的啟動器幫我們配置了若干個被Spring管理的bean,當我們的項目依賴這些jar並啟動Spring應用時,Spring的Container容器已經把jar包下的對象加以創建及管理了,我們請看下面的例子:
該例子是spring-boot-autoconfigure-2.0.0.M7.jar包的內容,我們找到如下類: DispatcherServletAutoConfiguration ,我貼出源代碼:

/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure.web.servlet; import java.util.Arrays; import java.util.List; import javax.servlet.MultipartConfigElement; import javax.servlet.ServletRegistration; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.servlet.DispatcherServlet; /** * {@link EnableAutoConfiguration Auto-configuration} for the Spring * {@link DispatcherServlet}. Should work for a standalone application where an embedded * web server is already present and also for a deployable application using * {@link SpringBootServletInitializer}. * * @author Phillip Webb * @author Dave Syer * @author Stephane Nicoll * @author Brian Clozel */ @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) @EnableConfigurationProperties(ServerProperties.class) public class DispatcherServletAutoConfiguration { /* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; /* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration"; @Configuration @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { private final WebMvcProperties webMvcProperties; public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) { this.webMvcProperties = webMvcProperties; } @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest( this.webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound( this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; } } @Configuration @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { private final ServerProperties serverProperties; private final WebMvcProperties webMvcProperties; private final MultipartConfigElement multipartConfig; public DispatcherServletRegistrationConfiguration( ServerProperties serverProperties, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfigProvider) { this.serverProperties = serverProperties; this.webMvcProperties = webMvcProperties; this.multipartConfig = multipartConfigProvider.getIfAvailable(); } @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>( dispatcherServlet, this.serverProperties.getServlet().getServletMapping()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; } } @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DefaultDispatcherServletCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage .forCondition("Default DispatcherServlet"); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); List<String> dispatchServletBeans = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(message.found("non dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (dispatchServletBeans.isEmpty()) { return ConditionOutcome .match(message.didNotFind("dispatcher servlet beans").atAll()); } return ConditionOutcome.match(message .found("dispatcher servlet bean", "dispatcher servlet beans") .items(Style.QUOTE, dispatchServletBeans) .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } } @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class DispatcherServletRegistrationCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory); if (!outcome.isMatch()) { return outcome; } return checkServletRegistration(beanFactory); } private ConditionOutcome checkDefaultDispatcherName( ConfigurableListableBeanFactory beanFactory) { List<String> servlets = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); boolean containsDispatcherBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(startMessage().found("non dispatcher servlet") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } return ConditionOutcome.match(); } private ConditionOutcome checkServletRegistration( ConfigurableListableBeanFactory beanFactory) { ConditionMessage.Builder message = startMessage(); List<String> registrations = Arrays.asList(beanFactory .getBeanNamesForType(ServletRegistrationBean.class, false, false)); boolean containsDispatcherRegistrationBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); if (registrations.isEmpty()) { if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome .match(message.didNotFind("servlet registration bean").atAll()); } if (registrations .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("servlet registration bean") .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome.match(message.found("servlet registration beans") .items(Style.QUOTE, registrations).append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } private ConditionMessage.Builder startMessage() { return ConditionMessage.forCondition("DispatcherServlet Registration"); } } }
該代碼是創建SpringMvc的典型示例 我想@Bean @Configuration 這個大家在熟悉不過了,下面我們來看幾個比較陌生的注解,我簡單解釋一下:
1.spring-boot利用Conditional來確定是否創建Bean實例,這個注解我們可以理解為滿足一定條件我們才創建Bean
2.這些注解我們可以在spring-boot-autoconfigure-2.0.0.M7.jar下 org.springframework.boot.autoconfigure.condition下找到
3.常見的注解解釋:
3.1 @ConditionalOnBean :匹配給定的class類型或者Bean的名字是否在SpringBeanFactory中存在
3.2 @ConditionalOnClass:匹配給定的class類型是否在類路徑(classpath)中存在
3.3 @ConditionalOnExpression : 匹配給定springEL表達式的值返回true時
3.4 @ConditionalOnJava :匹配JDK的版本,其中range屬性是枚舉類型有兩個值可以選擇
3.4.1 EQUAL_OR_NEWER 不小於
3.4.2 OLDER_THAN 小於
3.4.3 value屬性用於設置jdk版本
3.5 ConditionalOnMissingBean:spring上下文中不存在指定bean時
3.6 ConditionalOnWebApplication:在web環境下創建
@ConditionalOnBean is evaluated after all configuration classes have been processed, i.e you can't use it to make a whole configuration class conditional on the presence of another bean. You can, however, use it where you have to make all of the configuration's beans conditional on the presence of another bean. 注意:Conditional 只有在所有配置類被加載完的時候被評估是否要創建,因此Conditional不能在配置類里根據其他創建的方法進行判斷
@Bean
@ConditionalOnBean({Teacher.class})
public Student student(StudentProperties studentProperties) {
Student student = new Student();
student.setStuName(studentProperties.getName());
return student;
}
@Bean
@ConditionalOnMissingBean
public static Teacher teacher() {
return new Teacher();
}
@Bean
@ConditionalOnMissingBean
public static School school() {
return new School();
}
比如上述代碼 Student是不會被創建的,如果非要@Bean和@Conditional使用,則可以借助於@Import方式實現
@Configuration
@EnableConfigurationProperties(StudentProperties.class)
@Import(TeacherAutoConfiguration.class)
public class MyTestAutoConfiguration {
@Bean
@ConditionalOnBean(Teacher.class)
public Student student(StudentProperties studentProperties) {
Student student = new Student();
student.setStuName(studentProperties.getName());
return student;
}
}
@Configuration
public class TeacherAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public static Teacher teacher() {
return new Teacher();
}
}
二。實現簡單的SpringBoot示例
1.我們先創建兩個類分別為 Teacher Student,項目結構圖
注意圖中標圈的文件:spring-configuration-metadata.json文件,我們配置這個文件后,可以在idea或者spring sts中配置application.yml得到相關智能提示
json數據如下

{ "hints":[], "groups":[], "properties": [ { "sourceType": "com.bdqn.lyrk.springboot.study.configuration.MyProperties", "name": "my.loginName", "description": "登錄名", "type": "java.lang.String" } ] }
2.MyObjectAutoConfiguration類代碼:

package com.bdqn.lyrk.springboot.study.configuration; import com.bdqn.lyrk.springboot.study.pojo.School; import com.bdqn.lyrk.springboot.study.pojo.Student; import com.bdqn.lyrk.springboot.study.pojo.Teacher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 自動創建對象示例,例子中我們創建Teacher與Student對象。 * 當項目打成jar包依賴到其他Spring容器中,這些對象我們可以自動進行注入 */ @Configuration @EnableConfigurationProperties(MyProperties.class) public class MyObjectAutoConfiguration { @Configuration static class TeacherAutoConfiguration { @Bean @ConditionalOnClass({Teacher.class, School.class}) public static Teacher teacher() { return new Teacher(); } } @Configuration static class StudentAutoConfiguration { @Bean @ConditionalOnMissingBean public Student student(@Autowired MyProperties myProperties) { return new Student(myProperties.getLoginName()); } } }
3.MyProperties代碼:

package com.bdqn.lyrk.springboot.study.configuration; import org.springframework.boot.context.properties.ConfigurationProperties; /** * 用於實現讀取application.yml中的配置 */ @ConfigurationProperties(prefix = MyProperties.PREFIX) public class MyProperties { public static final String PREFIX = "my"; private String loginName; public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } }
4.application.yml配置:

my: loginName: test spring: main: web-environment: false
5.Application代碼:

package com.bdqn.lyrk.springboot.study; import com.bdqn.lyrk.springboot.study.pojo.Student; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class Application { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args); Student student = applicationContext.getBean(Student.class); System.out.println(student.getLoginName()); } }
當運行成功時:我們可以看到輸出Student的loginName屬性是test
6.在META-INF/spring.factories編寫如下代碼:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bdqn.lyrk.springboot.study.configuration.MyTestAutoConfiguration
注意:此配置文件非常重要,當我們這個jar包被SpringBoot依賴時,spring會讀取org.springframework.boot.autoconfigure.EnableAutoConfiguration所定義的配置類並加載