Spring 註解程式設計IOC
Bean 註冊
註冊Bean的常用註解有@Component、@Service、@Controller、@Repository,通過掃描包的方式對這些註解進行解析註冊Bean。
註解ApplicationContext:AnnotationConfigApplicationContext
常用註解
@Configuration
宣告Bean Difinition的配置檔案,相當於一個xml檔案
@Bean
宣告Bean的元件
@Configuration public class CustomConfig { @Bean public Person person() { return new Person(); } }
相當於xml bean內容
<beans>
<bean id="person" class="top.felixfly.entity.Person"/>
</beans>
bean的名稱預設為方法名稱,也可以通過@Bean(value=“person”)或者@Bean(“person”)進行指定
@ComponentScan
指定掃描路徑
@Configuration @ComponentScan("top.felixfly.spring.annotation") public class ScanConfiguration { }
相當於xml component-scan
<beans>
<context:component-scan package="top.felixfly.spring.annotation"/>
</beans>
@ComponentScans
多個掃描路徑,值為ComponentScan的陣列,1.8以後可以用多個@ComponentScan代替此註解
@Scope
指定Bean的作用域,預設為singleton
- singleton org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_SINGLETON
- prototype org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_PROTOTYPE
- request org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
- session org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
@Configuration
public class CustomConfig {
@Bean
@Scope("singleton")
public Person person() {
return new Person();
}
}
相當於xml中bean中scope屬性
<beans>
<bean id="person" class="top.felixfly.entity.Person" scope="singleton"/>
</beans>
@Lazy
懶載入,針對singleton Bean進行懶載入,預設情況下單例項Bean直接載入
@Configuration
public class CustomConfig {
@Bean
@Lazy
public Person person() {
return new Person();
}
}
相當於xml中bean的lazy-init屬性
<beans>
<bean id="person" class="top.felixfly.entity.Person" lazy-init="true"/>
</beans>
@DependsOn
依賴關係註解
@Configuration
public class CustomConfig {
@Bean
@DependsOn("person")
public Manager manager(){
return new Manager();
}
@Bean
public Person person(){
return new Person();
}
}
相當於xml中bean的depends-on屬性
<beans>
<bean id="manager" class="top.felixfly.entity.Manager" depends-on="person"/>
</beans>
@Order
Bean的排序,或者說是優先順序,兩個介面org.springframework.core.Ordered以及org.springframework.core.PriorityOrdered,主要使用優先順序的內容
- org.springframework.beans.factory.config.BeanPostProcessor
- org.springframework.http.converter.HttpMessageConverter
@Conditional
條件裝配Bean
- 實現org.springframework.context.annotation.Condition介面
public class CustomCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// true 進行裝配,false不進行裝配
return false;
}
}
- Bean上配置@Conditional(Condition.class)
@Configuration
public class CustomConfig {
@Conditional(CustomCondition.class)
@Bean
public Person person() {
return new Person();
}
}
當matches方法返回true的時候進行註冊當前@Bean,否則不註冊。該註解也可以放到配置類上,matches方法返回true的時候進行註冊當前配置類,否側不註冊。
@Profile
環境註解,底層使用的是@Conditional
@Import
快捷註冊Bean,預設名稱為類的全路徑
- 直接匯入類
@Configuration
@Import(Person.class)
public class CustomConfig {
}
- 匯入實現org.springframework.context.annotation.ImportSelector類
public class CustomImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{Person.class.getName()};
}
}
@Configuration
@Import(CustomImportSelector.class)
public class CustomConfig {
}
- 匯入實現org.springframework.context.annotation.ImportBeanDefinitionRegistrar類
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
// 自行註冊BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(Person.class);
registry.registerBeanDefinition("person",beanDefinition);
}
}
@Configuration
@Import(CustomImportBeanDefinitionRegistrar.class)
public class CustomConfig {
}
@ImportResource
匯入資源xml檔案
資原始檔名稱spring/application-spring.xml
<beans>
<bean class="top.felixfly.spring.annotation.entity.Person">
<constructor-arg index="0" value="張三"/>
<constructor-arg index="1" value="27"/>
</bean>
</beans>
@Configuration
@ImportResource("classpath:/spring/application-spring.xml")
public class CustomConfig {
}
常見問題
@Configuration、其他註解與@Bean結合使用有什麼不同
答:@Configuration註解使用的其實也是一個Bean,但本身是BeanFatory,是經過CGLIB進行增強的Bean,其他註解(@Component、@Service、@Controller、@Repository)使用的就是一個簡單的Bean
Bean 依賴注入
常用註解
@Autowired
Spring自帶的自動注入,註解的屬性required來支援是否必須要進行依賴注入。根據以下規則進行查詢進行注入
1、 根據型別查詢,只查詢一個直接返回
2、 根據名稱查詢
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
}
可以結合以下註解進行使用
- @Qualifier
指定名稱進行依賴注入
@Service
public class PersonService {
@Autowired
@Qualifier("personMapper")
private PersonMapper personMapper;
}
- @Primary
指定優先進行依賴注入
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
}
@Configuration
@ComponentScan({"top.felixfly.spring.annotation.mapper","top.felixfly.spring.annotation.service"})
public class CustomConfig {
// 優先注入
@Bean("personMapper2")
@Primary
public PersonMapper personMapper(){
return new PersonMapper();
}
}
只有一個有參構造器時,@Autowired可以省略,可以自動進行注入
@Resource
Java規範(JSR250)的註解,預設按照屬性的名稱進行依賴查詢匹配,也可以用屬性name進行強制指定,但不支援與@Primary註解結合使用和required是否必須要進行依賴注入
@Service
public class PersonService {
@Resource
private PersonMapper personMapper;
}
@Service
public class PersonService {
// 強制指定Bean
@Resource(name="personMapper2")
private PersonMapper personMapper;
}
@Inject
Java規範的註解(JSR330),功能與@Autowired一樣,但不支援required是否必須要進行依賴注入。需要引入javax.inject
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Service
public class PersonService {
@Inject
private PersonMapper personMapper;
}
注入方式
構造器注入
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
// 構造器注入
return new BeanOne(beanTwo());
}
@Bean
public BeanOne beanThree(BeanTwo beanTwo) {
// 構造器注入
return new BeanOne(beanTwo);
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
Setter方法注入
public class BeanTwo {
@Autowired
public void setBeanOne(BeanOne beanOne) {
this.beanOne = beanOne;
}
}
Aware介面
自定義元件注入Spring底層的元件,比如ApplicationContext,這些Aware介面一般通過Processor進行處理。ApplicationContextAwareProcessor處理EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
ApplicationContextAware | ApplicationContext |
---|---|
ApplicationEventPublisherAware | ApplicationContext事件釋出器 |
BeanClassLoaderAware | 類載入器 |
BeanFactoryAware | Bean 工廠 |
BeanNameAware | Bean 名稱 |
BootstrapContextAware | BootstrapContext |
MessageSourceAware | 國際化管理 |
NotificationPublisherAware Spring | JMX通知釋出器 |
ResourceLoaderAware | 資源載入器 |
EmbeddedValueResolverAware | @Value解析器 |
EnvironmentAware | 環境變數 |
常見問題
迴圈依賴的問題
答:迴圈依賴的產生,BeanA依賴BeanB,BeanB依賴BeanC,而BeanC又依賴於BeanA,這時候就會產生迴圈依賴的問題,單例Bean中通過構造器注入會產生迴圈依賴的問題,會產生BeanCurrentlyInCreationException,通過Setter方法注入不會產生異常,可以解決迴圈依賴問題。原型@Bean通過Setter方法注入依然會產生BeanCurrentlyInCreationException,沒辦法解決迴圈依賴問題。
Bean 生命週期
Bean的生命週期包含例項化–>初始化–>銷燬,單例項Bean例項化在容器建立的時候進行例項化以及初始化,銷燬在容器關閉的時候進行呼叫;多例項Bean在獲取Bean的時候進行例項化以及初始化,銷燬需要自行進行呼叫。
初始化和銷燬常用方法
- @Bean指定initMethod和destroyMethod
@Configuration
public class CustomConfig {
@Bean(initMethod = "init",destroyMethod = "destroy")
public Person person(){
return new Person();
}
}
相當於xml中配置init-method和destroy-method屬性
<beans>
<bean class="top.felixfly.spring.annotation.entity.Person" init-method="init" destroy-method="destroy"/>
</beans>
- 實現InitializingBean和DisposableBean
public class Person implements InitializingBean, DisposableBean {
public Person() {
}
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public void destroy() throws Exception {
}
}
- 使用@PostConstruct和@PreDestroy
註解使用InitDestroyAnnotationBeanPostProcessor進行解析處理,父類CommonAnnotationBeanPostProcessor
public class Person {
public Person() {
}
@PostConstruct
public void postConstruct(){
}
@PreDestroy
public void preDestroy(){
}
}
BeanPostProcessor
- postProcessBeforeInitialization 初始化之前執行方法
- postProcessAfterInitialization 初始化之後執行方法
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
@Configuration
@Import(CustomBeanPostProcessor.class)
public class CustomConfig {
@Bean
public Person person(){
return new Person();
}
}
執行方法若是返回null值,後續的BeanPostProcessor不會進行執行,原始碼執行如下:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
常見問題
生命週期執行方法順序、
答:初始化方法執行順序
- @PostConstruct
- 實現InitializingBean介面的方法
- @Bean指定initMethod
銷燬方法執行順序
- @PreDestroy
- 實現DisposableBean介面的方法
- @Bean指定destroyMethod
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
- Methods annotated with @PostConstruct
- afterPropertiesSet() as defined by the InitializingBean callback interface
- A custom configured init() method
Destroy methods are called in the same order:
- Methods annotated with @PreDestroy
- destroy() as defined by the DisposableBean callback interface
- A custom configured destroy() method
資源屬性賦值
常用註解
@Value
屬性進行賦值,可以有如下三種寫法
- 直接賦值
public class Person {
@Value("張三")
private String name;
}
- SpEL表示式 #{}
public class Person {
@Value("#{20-2}")
private String age;
}
- ${} 檔案屬性賦值(通常在環境變數Enviroment中),要配合@PropertySource使用
public class Person {
@Value("${person.age}")
private String age;
}
@PropertySource
引入配置檔案,配置檔案下根路徑下person.properties
@PropertySource("classpath:/person.properties")
public class CustomConfig {
}
相當於xml中的context:property-placeholder
<context:property-placeholder location="classpath:person.properties"/>
@PropertySources
多個配置檔案引入,值為PropertySource的陣列,1.8以後可以用多個@PropertySource代替此註解
常見問題
配置檔案屬性亂碼
答:註解@PropertySource通過屬性encoding進行配置檔案編碼,該配置在4.3版本引入;xml配置檔案中通過屬性file-encoding配置檔案編碼
原文連結:https://gper.club/articles/7e7e7f7ff7g5fgccg67