【Spring註解】——組件註冊

    本文將主要針對Spring註解,講解在組件註冊過程會用到一些註解,對於小編本身是基礎的回顧和鞏固,也希望能幫助到需要的朋友,講解偏頗之處,還請各位大神指正,小編不勝感激。

1、在容器中註冊組件

1.1@Configuration

    標註在類上,告訴spring這是一個配置類,代替spring的xml配置文件中<beans>。作用:配置spring容器(應用上下文)

優點

    1、使用純Java代碼,不在需要xml

    2、在配置中可以享受OO帶來的好處

    3、類型安全對重構也能提供良好的支持

    4、依舊能享受到所有SpringIOC容器提供的功能

1.2@Bean

    標註在方法上,相當於spring的xml配置文件中<bean>。作用:註冊bean對象bean類

注意:

    1、@Bean註解在返回實例的方法上,如果沒有通過@Bean指定bean的名稱,則默認與標註的方法名相同

    2、@Bean註解默認作用域爲單例singleton作用域,但是可以通過@Scope("prototype")設置爲原型作用域(ps:@Scope下文有講解)

    3、@Bean的作用註冊對象,也可以使用@Component,@Controller,@Service,@Repository等註解註冊Bean,但是需要配置@ComponentScan註解進行自動掃描

2、掃描組件

2.1@ComponentScan(value)

    標註在類上,相當於spring的xml配置文件中 <context:component-scan base-package="**"/>。作用:在定義掃描的路徑從中找到標識了需要裝配的類自動裝配到spring的容器中

2.2通過@ComponentScan指定掃描規則

    標註在@ComponentScan後面括號中,作用按照指定規則向spring容器中自動裝配bean

excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件,類似於spring中xml配置文件中

<context:component-scan base-package="**">
	<context:exclude-filter type="**" expression="**" />
</context:component-scan>

註解的使用方式

@ComponentScan(value="**",
excludeFilters = {@Filter(type=**,classes={**})}

includeFilters = Filter[] :指定掃描的時候只需要包含哪些組件,類似於spring中xml配置文件中

<!--use-default-filters="false" 使用只包含哪些組件,需要禁用默認的裝配規則-->
    <context:component-scan base-package="**" use-default-filters="false">
        <context:include-filter type="**" expression="**"
    </context:component-scan>

註解的使用方式

@ComponentScan(value="**",excludeFilters = {@Filter(type=**,classes={**})},useDefaultFilters = false)}

@Filter包含的過濾類型

FilterType.ANNOTATION:按照註解
FilterType.ASSIGNABLE_TYPE:按照給定的類型;
FilterType.ASPECTJ:使用ASPECTJ表達式
FilterType.REGEX:使用正則指定
FilterType.CUSTOM:使用自定義規則

當使用自定義規則的時候即@Filter=FilterType.CUSTOM,自定義的類需要實現TypeFilter接口,然後按自己的需要重寫過濾規則

Demo,過濾類名中包含er的類

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

public class MyTypeFilter implements TypeFilter {

	/**
	 * metadataReader:讀取到的當前正在掃描的類的信息
	 * metadataReaderFactory:可以獲取到其他任何類信息的
	 */
	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// TODO Auto-generated method stub
		//獲取當前類註解的信息
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		//獲取當前正在掃描的類的類信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		//獲取當前類資源(類的路徑)
		Resource resource = metadataReader.getResource();
		
		String className = classMetadata.getClassName();
		System.out.println("--->"+className);
		if(className.contains("er")){
			return true;
		}
		return false;
	}
}
//只裝配類名中包含er的類
@ComponentScan(value="**",
               excludeFilters = {@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})},
               useDefaultFilters = false)

2.3@ComponentScans,裝配多個@ComponentScan裝配規則

    在Java8以前不允許在一個類上定義兩個相同的註解,可以使用@ComponentScans,裝配多個@ComponentScan裝配規則

@ComponentScans(
		value = {@ComponentScan(value="**",
                excludeFilters ={@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})},useDefaultFilters = false)}
)

3、設置組件作用域

3.1@Scope(value)

    標註類上或者裝載類的方法上,spring容器中bean默認是單例的,但是可以通過@Scope調整作用域,相當於spring中xml配置文件中的

<bean class="**" scope="**">

註解使用方式

@Service("allusersService")
@Scope("prototype")
public class AllusersServiceImpl{}

作用域

prototype:多實例的:ioc容器啓動並不會去調用方法創建對象放在容器中。
	   每次獲取的時候纔會調用方法創建對象;
singleton:單實例的(默認值):ioc容器啓動會調用方法創建對象放到ioc容器中。
	    以後每次獲取就是直接從容器(map.get())中拿,
request:同一次請求創建一個實例
session:同一個session創建一個實例

4、設置組件加載方式

4.1@Lazy懶加載

    單實例bean:默認在容器啓動的時候創建對象;

    懶加載:容器啓動不創建對象。第一次使用(獲取)Bean創建對象,並初始化;

    標註類上或者裝載類的方法上,當第一次使用獲取Bean才創建對象並初始化,相當spring中xml配置文件中

//默認爲false,即立即加載,設置爲true則表示延遲加載
<bean class="***" lazy-init="true">

註解使用方式,延遲裝載

@Service("allusersService")
@Lazy
public class AllusersServiceImpl

5、按條件註冊bean

5.1@Conditional({Condition}) 

    按照一定的條件進行判斷,滿足條件給容器中註冊bean

    在spring4中引入,用到帶有@Bean註解的方法上,如果給定的條件計算結果爲true,則會創建這個bean,否則這個bean就會被忽略。

條件化的創建bean,例如判斷環境是爲linux,如果是則註冊

@Conditional(LinuxCondition.class)
@Bean("linus")
public Person person02(){
	return new Person("linus", 48);
}

作爲條件的bean,在實現的時候需要實現spring提供的Condition接口

//判斷是否linux系統
public class LinuxCondition implements Condition {
	/**
	 * ConditionContext:判斷條件能使用的上下文(環境)
	 * AnnotatedTypeMetadata:註釋信息
	 */
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		//1、能獲取到ioc使用的beanfactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//2、獲取類加載器
		ClassLoader classLoader = context.getClassLoader();
		//3、獲取當前環境信息
		Environment environment = context.getEnvironment();
		//4、獲取到bean定義的註冊類
		BeanDefinitionRegistry registry = context.getRegistry();
		
		String property = environment.getProperty("os.name");
		
		//可以判斷容器中的bean註冊情況,也可以給容器中註冊bean
		boolean definition = registry.containsBeanDefinition("person");
		if(property.contains("linux")){
			return true;
		}	
		return false;
	}
}

Spring中@Profile註解根據環境註冊bean,本質上也是利用@Conditional註解實現

6、給容器中導入組件

6.1@Import

@Import導入組件,id默認是組件的全類名,相對於@Bean是一種更快捷的方式

@Configuration
//@Import導入組件,id默認是組件的全類名
@Import({Color.class,Red.class})
public class MainConfig{}

6.2@ImportSelector

    返回需要導入的組件全類名數組

    自定義的類需要使用@Import註解導入

自定義實現ImportSelector接口的方法

//自定義邏輯返回需要導入的組件
public class MyImportSelector implements ImportSelector {
	//返回值,就是到導入到容器中的組件全類名
	//AnnotationMetadata:當前標註@Import註解的類的所有註解信息
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		//importingClassMetadata
		//方法不要返回null值
		return new String[]{"com.jia.bean.Blue","com.jia.bean.Yellow"};
	}
}
@Configuration
@Import({MyImportSelector.class})
public class MainConfig{}

6.3@ImportBeanDefinitionRegistrar

    手動註冊bean到容器中,自定義邏輯實現bean的註冊

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
	/**
	 * AnnotationMetadata:當前類的註解信息
	 * BeanDefinitionRegistry:BeanDefinition註冊類;
	 * 		把所有需要添加到容器中的bean;調用
	 * 		BeanDefinitionRegistry.registerBeanDefinition手工註冊進來
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		
		boolean definition = registry.containsBeanDefinition("com.jia.bean.Red");
		boolean definition2 = registry.containsBeanDefinition("com.jia.bean.Blue");
		if(definition && definition2){
			//指定Bean定義信息;(Bean的類型,Bean。。。)
			RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
			//註冊一個Bean,指定bean名
			registry.registerBeanDefinition("rainBow", beanDefinition);
		}
	}
}
@Configuration
@Import({MyImportBeanDefinitionRegistrar.class})
public class MainConfig{}

7、使用FactoryBean容器註冊組件

創建一個spring定義的FactoryBean

import org.springframework.beans.factory.FactoryBean;

//創建一個Spring定義的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

	//返回一個Color對象,這個對象會添加到容器中
	@Override
	public Color getObject() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("ColorFactoryBean...getObject...");
		return new Color();
	}
	@Override
	public Class<?> getObjectType() {
		// TODO Auto-generated method stub
		return Color.class;
	}
	//是單例?
	//true:這個bean是單實例,在容器中保存一份
	//false:多實例,每次獲取都會創建一個新的bean;
	@Override
	public boolean isSingleton() {
		// TODO Auto-generated method stub
		return false;
	}
}
@Bean
public ColorFactoryBean colorFactoryBean(){
	return new ColorFactoryBean();
}
1)、默認獲取到的是工廠bean調用getObject創建的對象
2)、要獲取工廠Bean本身,我們需要給id前面加一個&
        &colorFactoryBean

//工廠Bean獲取的是調用getObject創建的對象
Object bean2 = applicationContext.getBean("colorFactoryBean");
Object bean3 = applicationContext.getBean("colorFactoryBean");
System.out.println("bean的類型:"+bean2.getClass());
System.out.println(bean2 == bean3);
		
Object bean4 = applicationContext.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());

8、加載spring的應用上下文

   AnnotationConfigApplicationContext實現基於Java的配置類加載spring的應用上下文。避免使用application.xml進行配置,相比xml配置,更加便捷

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

管理註解Bean定義的容器有兩個:

    AnnotationConfigApplicationContext和 AnnotationConfigWebApplicationContex。這兩個類是專門處理Spring註解方式配置的容器,直接依賴於註解作爲容器配置信息來源的IoC容器。 

    AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,兩者的用法以及對註解的處理方式幾乎沒有什麼差別,在後續的文章小編會進行更加詳細的講解。

總結

 給容器中註冊組件:

 1、包掃描+組件標註註解(@Controller/@Service/@Repository/@Component)[自己寫的類]

 2、@Bean[導入的第三方包裏面的組件]
 3、@Import[快速給容器中導入一個組件]
    1)、@Import(要導入到容器中的組件);容器中就會自動註冊這個組件,id默認是全類名
    2)、ImportSelector:返回需要導入的組件的全類名數組;
    3)、ImportBeanDefinitionRegistrar:手動註冊bean到容器中
4、使用Spring提供的 FactoryBean(工廠Bean);
    1)、默認獲取到的是工廠bean調用getObject創建的對象

    2)、要獲取工廠Bean本身,我們需要給id前面加一個&,即&colorFactoryBean

5、使用AnnotationConfigApplicationContext使用配置文件加載spring應用上下文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章