spring配置類,即在類上加@Configuration注解,使用這種配置類來注冊bean,效果與xml文件是完全一樣的,只是創建springIOC容器的方式不同:
//通過xml文件創建springIOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-beans.xml"); //通過配置類創建springIOC容器 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
0. 傳統的xml配置文件方式注冊bean類
單個注冊
<!--單實例模式--> <bean id="person" class="cn.monolog.entity.Person" scope="singleton" /> <!--多實例模式--> <bean id="book" class="cn.monolog.entity.Book" scope="prototype" />
掃描批量注冊,只能注冊加了組件注解(@Repository、@Service、@Controller、@Component)的類
<!--通過掃描批量注冊加了組件注解的bean--> <context:component-scan base-package="cn.monolog.entity" />
1. 使用配置類單個注冊
①在配置類上加@Configuration注解
②在方法上加@Bean注解,bean的id默認為方法名,如果需要自定義id,則在該注解上加value屬性
③如果需要指定bean的作用域,則還要在方法上加@Scope注解,如果不加該注解,則默認為單例模式
該注解的value屬性有如下幾種常用取值(均為字符串格式)
單例模式(singleton): 創建IOC容器時即創建bean對象,以后每次取值都是取的這個bean對象
原型模式(protortype): 創建IOC容器時不創建bean對象,以后每次取值時再分別創建
另外還有request、session、global session
④對於單例模式,還可以加@Lazy注解,即懶加載,表示在創建IOC容器時並不創建bean對象,而是在第一次獲取bean對象時才創建,之后再獲取bean對象時不再創建,因此仍然是單實例
懶加載的作用:加快SpringIOC容器的啟動速度、解決循環依賴的問題
/** * springIOC配置容器類 * 用於代替spring-beans.xml,生成bean對象 */ @Configuration public class BeanConfig { //生成Person實例 @Bean(value = "person") public Person person() { String name = "張三"; int age = 29; return new Person(name, age); } /** * 使用單實例模式生成Book實例 * 這時,是在springIOC容器啟動時就創建了bean實例,然后每次獲取實例時,直接從容器中拿 */ @Bean(value = "singleBook") @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) public Book singleBook() { String name = "hello world"; double price = 29.00D; return new Book(name, price); } /** * 使用多實例模式生成Book實例 * 這時,啟動springIOC容器時並未創建bean實例,而是每次獲取bean實例時再調用該方法去新創建 */ @Bean(value = "multipleBook") @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Book multipleBook() { String name = "hello world"; double price = 29.00D; return new Book(name, price); } /** * 使用懶加載模式生成bean實例 * 懶加載只對單實例模式有效 * 本來,單實例模式,是在啟動springIOC容器時創建bean實例 * 使用懶加載后,在啟動springIOC容器時並不創建bean實例,而是在首次獲取bean時才創建 */ @Bean(value = "person") @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy public Person person() { return new Person("劉能", 53); } }
2. 使用配置類掃描批量注冊,只能注冊加了組件注解(@Repository、@Service、@Controller、@Component)的類
①在配置類上加@Configuration注解
②在配置類上加@ComponentScan注解,並在其basePackages屬性(字符串數組類型)中寫明要掃描的包,可以寫1個或多個包,如果想排除某些類,可以寫在excludeFilter屬性中
/** * 通過掃描批量注冊加了組件注解的bean * 並排除Dog類 */ @Configuration @ComponentScan(basePackages = {"cn.monolog.entity", "cn.monolog.service", "cn.monolog.dao"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Dog.class})}) public class BeanConfig { }
3. 按條件注冊
①在配置類上加@Configuration注解
②在方法上加@Bean注解,bean的id默認為方法名,如果需要自定義id,則在該注解上加value屬性
③在方法上加@Conditional注解,該注解的參數是字節碼數組,什么樣的類的字節碼呢?必須是實現了Condition接口的。使用時,需要自定義1個或多個Condition的實現類,並在其實現方法中定義生效條件,當滿足生效條件時,才會去注冊該bean類
import org.springframework.context.annotation.Condition; /** * springIOC條件注冊中的條件類 */ public class JuventusCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //獲取bean注冊器 BeanDefinitionRegistry registry = context.getRegistry(); //判斷該IOC容器中是否含有id為juventus的組件,如果是,則生效 if (registry.containsBeanDefinition("juventus")) { return true; } return false; } }
/** * 按條件注冊bean類 */ @Configuration public class BeanConfig { /** * 按條件生成名字為ronaldo的Person對象 * 條件為寫在JuventusCondition類中 */ @Bean(value = "ronaldo") @Scope(value = SCOPE_SINGLETON) @Conditional({JuventusCondition.class}) public Person ronaldo() { return new Person("ronaldo", 34); } }
4. 使用導入方式注冊
①在配置類上加@Configuration注解
②在配置類上加@Import注解,該注解的value屬性為字節碼數組,可以接收以下三種類的字節碼
要注冊的類;
實現了ImportSelector接口的自定義類,其實現方法的返回值為要注冊的類的全限定名數組;
實現了ImportBeanDefinitionRegistrar的自定義類,在其實現方法中,使用BeanDefinitionRegistry手動注冊,前面兩種方式注冊的bean的id只能是類的全限定名,只有這種方式可以自定義bean的id;
import org.springframework.context.annotation.ImportSelector; /** * 使用import注解注冊bean的選擇器 * 注意該方法可以返回空數組,但不能返回null,因為在后續源碼中會獲取返回值的長度 * 而且這里只能用類的全限定名 */ public class ColorImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { String[] results = {"cn.monolog.entity.Green", "cn.monolog.entity.Pink"}; return results; } }
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; /** * 使用import注解注冊bean時,自定義bean選擇器 * 在該類的實現方法中,使用bean注冊器手動注冊 * 注意,如果多次為同一個id注冊bean,后面的會覆蓋前面的 */ public class ColorDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //判斷該容器中是否已經存在id為yellow的組件,如果不存在,則手動注冊 String id = "yellow"; if (!registry.containsBeanDefinition(id)) { //bean的id String beanName = "yellow"; //bean的類型 BeanDefinition beanDefinition = new RootBeanDefinition(Yellow.class); registry.registerBeanDefinition(beanName, beanDefinition); } } }
/** * springIOC容器配置類 * 使用import注解注冊bean * 該注解可以接收三種參數 * 1.要注冊的類的字節碼 * 2.ImportSelector的自定義實現類的字節碼,在其實現方法中,將要注冊的類的全限定名寫入返回數組 * 3.ImportBeanDefinitionRegistrar的自定義實現類的字節碼,在其實現方法中,使用bean注冊器手動注冊 * 其中,前兩種方式注冊的bean的id只能是該類的全限定名 * 第三種方式注冊的bean類可以自定義id */ @Configuration @Import({Red.class, ColorImportSelector.class, ColorDefinitionRegistrar.class}) public class BeanConfig { }
5. 使用工廠模式注冊
工廠模式與第1條(單個注冊)的方法完全相同,唯一的特別之處在於,注冊的是一個"工廠類",即實現了FactoryBean<T>接口的自定義類,這種類的特點是,在IOC容器中直接按id獲取的並不是它本身的對象,而是它內部注冊的bean類的對象。要想獲取它本身的對象,要在id前面拼接"&"符號。
FactoryBean<T>接口有三個方法:
getObject——用於注冊真正的bean類
getObjectType——用於獲取bean類的類型,即泛型T
isSingleton——注冊的bean類是否為單例模式
import org.springframework.beans.factory.FactoryBean; /** * 工廠類 */ public class ColorFactoryBean implements FactoryBean<Color> { //注冊bean @Override public Color getObject() throws Exception { return new Color("#FFF"); } //獲取bean的類型 @Override public Class<?> getObjectType() { return Color.class; } //是否單實例模式 @Override public boolean isSingleton() { return true; } }