Spring Boot核心特性之元件自動裝配
spring boot能夠根據依賴的jar包自動配置spring boot的應用,例如: 如果類路徑中存在DispatcherServlet
類,就會自動配置springMvc相關的Bean。spring boot的自動裝配來源於spring的裝配,功能也是隨時spring的不斷升級不斷完善的,spring boot正是在spring的基礎上實現的自動裝配。
spring模式註解裝配
模式註解介紹
模式註解是應用程式中用來標註元件的註解,例如:@Repository
是spring框架中用來標註資料訪問物件(DAO)的註解。@Component
是用來標註被spring管理的通用的元件,@Component
@Component
元註解的的註解也能被spring掃描到,比如@Service
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
下面是spring中常用的模式註解
spring註解 | 使用場景 | 起始版本 |
---|---|---|
@Repository |
資料倉儲模式註解 | 2.0 |
@Component |
通用元件模式註解 | 2.5 |
@Service |
服務模式註解 | 2.5 |
@Controller |
Web 控制器模式註解 | 2.5 |
@Configuration |
配置類模式註解 | 3.0 |
裝配方式
spring中通過配置掃描的包 ,就能掃描到註解的元件,有兩種配置的方式:
XML配置,通過
context:component-scan
標籤的base-package
屬性,配置需要掃描的包<context:component-scan base-package="com.laoliangcode.service,com.laoliangcode.dao"/>
註解方式裝配
@ComponentScan(basePackages = "com.laoliangcode.service,com.laoliangcode.dao")
自定義模式註解
可以通過在自定義註解上加上元註解的方式,自定義模式註解。例如:@UserRepository
註解上加上元註解@Repository
,這樣@UserRepository
也是模式註解了。這是由於註解具有派生性的特點,@UserRepository
派生至@Repository
,@Repository
派生至@Component
。
@Repository
public @interface UserRepository {
String value() default "";
}
spring @Enable模組裝配
spring3.1開始支援@Enable
模組裝配,所謂模組是指,把具有相同功能的元件組合在一起,引入到專案中。比如@EnableWebMvc
註解,就是把spring MVC相關的配置引入到專案中,而不需要其他配置,方便使用spring MVC。這種裝配方式是通過@Import
註解引入其他配置類來實現的,@EnableWebMvc
通過引入DelegatingWebMvcConfiguration
配置類,實現spring MVC的自動配置。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
引入的配置類有兩種實現形式,一種是直接使用模式註解@Configuration
的類,另一種是實現ImportSelector
介面的selectImports
方法,來引入配置類。
註解方式
@EnableWebMvc
就是這種實現方式。
下面列舉User
模組的裝配來具體說明實現的方式。可以看出EnableUserConfig
是通過直接匯入UserConfiguration
來裝配User
模組的。
UserConfiguration
配置類@Configuration public class UserConfiguration { @Bean public UserService userService(UserDao userDao) { return new UserService(userDao); } @Bean public UserDao userDao() { return new UserDao(); } }
EnableUserConfig
註解@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfiguration.class) public @interface EnableUserConfig { }
使用啟動類
@EnableUserConfig public class EnableUserConfigBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EnableUserConfigBootstrap.class); UserService userService = context.getBean("userService", UserService.class); System.out.println("EnableUserConfigBootstrap.main" + userService.findBId(1)); context.close(); } }
ImportSelector
介面方式
spring中的EnableCaching
就是這種實現方式。這種方式是通過UserConfigurationSelector
來引入User
的配置類UserConfiguration
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
}
下面列舉User
模組的裝配來具體說明實現的方式。
UserConfigurationSelector
類用來匯入UserConfiguration
配置
public class UserConfigurationSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {UserConfiguration.class.getName()};
}
}
EnableUserSelector
註解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfigurationSelector.class)
public @interface EnableUserSelector {
}
- 使用啟動類
@EnableUserSelector
public class EnableUserSelectorBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(EnableUserSelectorBootstrap.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println("EnableUserSelectorBootstrap.main" + userService.findBId(1));
context.close();
}
}
spring條件裝配
spring3.1開始,spring引入了@Profile
註解,可以根據環境Environment
的不同引入不同的配置。spring4.0開始,Conditional
註解可以更靈活的根據不同條件引入不同的配置。
@Profile
註解方式的條件裝配
使用User
模組的不能dao
裝配來說明@Profile
的條件裝配。
UserProfileConfiguration
配置
@Configuration
public class UserProfileConfiguration {
@Bean
public UserServiceForProfile userServiceForProfile(IUserDao userDao) {
return new UserServiceForProfile(userDao);
}
@Bean
@Profile("mysql")
public IUserDao userMysqlDao() {
return new UserMysqlDao();
}
@Bean
@Profile("oracle")
public IUserDao userOracleDao() {
return new UserOracleDao();
}
}
Environment
啟用使用,通過context.getEnvironment().setActiveProfiles("oracle");
的方式,來啟用不同的Profile
public class ProfileBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext();
context.register(UserProfileConfiguration.class);
context.getEnvironment().setActiveProfiles("oracle");
context.refresh();
UserServiceForProfile userService = context.getBean("userServiceForProfile",
UserServiceForProfile.class);
System.out.println("ProfileBootstrap.main" + userService.findBId(1));
context.close();
}
}
@Conditional
註解方式的條件裝配
@Conditional
註解方式,通過引入實現Condition
介面的類,來判斷條件是否成立,從而確定是否引入某個元件。這個類是通過實現matches
方法來判斷條件是否成立。spring4.0開始,由於引入了@Conditional
註解,Profile
也是通過@Conditional
來實現的。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
spring boot中大量使用了@Conditional
註解的方式,來自動裝配不同的元件。@ConditionalOnClass
用來表示類路徑存在某些類時載入;@ConditionalOnMissingBean
用來判斷某些類的例項不存在時載入;ConditionalOnWebApplication
用來判斷某種應用型別時載入。例如webmvc
的自動載入配置WebMvcAutoConfiguration
:
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}
下面的例子是根據系統變數的值來決定是否裝配UserDao
OnSystemPropertyCondition
實現Condition
介面的matches
方法,用來判斷是否符合條件public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName()); String propertyName = String.valueOf(annotationAttributes.get("name")); String propertyValue = String.valueOf(annotationAttributes.get("value")); String systemPropertyValue = System.getProperty(propertyName); return propertyValue.equals(systemPropertyValue); } }
ConditionalOnSystemProperty
註解@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnSystemPropertyCondition.class) public @interface ConditionalOnSystemProperty { String name(); String value(); }
啟動類
public class ConditionalOnSystemPropertyBootstrap {
@ConditionalOnSystemProperty(name = "user.name", value = "Administrator")
@Bean
public UserDao userDao() {
return new UserDao();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(ConditionalOnSystemPropertyBootstrap.class);
UserDao userDao = context.getBean("userDao", UserDao.class);
System.out.println("ConditionalOnSystemPropertyBootstrap.main" + userDao.findBId(1));
context.close();
}
}
- 如果
value
指定錯誤,就會報錯:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userDao' available
spring boot自動裝配
spring boot的自動裝配結合了上面介紹的spring模式註解、@Enable模組裝配和條件裝配。另外,spring boot自身還使用了工廠載入機制,用SpringFactoriesLoader
來裝載配置類。
實現方法
實現自動自動裝配的類
在
META-INF/spring.factories
檔案中配置第一步中的類@EnableAutoConfiguration
註解啟用配置下面以
User
模組的自動裝配為例,來介紹具體的實現步驟
- 實現裝配類
UserAutoConfiguration
,這裡用到了前面介紹的@Enable模組裝配和條件裝配
@EnableUserSelector
@ConditionalOnSystemProperty(name = "user.name", value = "Administrator")
public class UserAutoConfiguration {
}
- 在
META-INF/spring.factories
檔案中新增配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.laoliangcode.configuration.UserAutoConfiguration
- 啟動類中新增
@EnableAutoConfiguration
註解啟用配置
@EnableAutoConfiguration
public class AutoConfigurationBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AutoConfigurationBootstrap.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println("AutoConfigurationBootstrap.main" + userService.findBId(1));
context.close();
}
}
如果在第一步中,讓條件裝配不符合條件,就會報錯:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userService' available
文中詳細程式碼在github上
https://github.com/laoliangcode/springboot-demo/tree/master/autoconfigure