1. 程式人生 > 其它 >Spring 註解程式設計IOC

Spring 註解程式設計IOC

技術標籤:併發程式設計javaspringioc

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

ApplicationContextAwareApplicationContext
ApplicationEventPublisherAwareApplicationContext事件釋出器
BeanClassLoaderAware類載入器
BeanFactoryAwareBean 工廠
BeanNameAwareBean 名稱
BootstrapContextAwareBootstrapContext
MessageSourceAware國際化管理
NotificationPublisherAware SpringJMX通知釋出器
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;
	}

常見問題

生命週期執行方法順序、

答:初始化方法執行順序

  1. @PostConstruct
  2. 實現InitializingBean介面的方法
  3. @Bean指定initMethod

銷燬方法執行順序

  1. @PreDestroy
  2. 實現DisposableBean介面的方法
  3. @Bean指定destroyMethod

Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:

  1. Methods annotated with @PostConstruct
  2. afterPropertiesSet() as defined by the InitializingBean callback interface
  3. A custom configured init() method

Destroy methods are called in the same order:

  1. Methods annotated with @PreDestroy
  2. destroy() as defined by the DisposableBean callback interface
  3. 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