01.Spring Framework 之組件註冊

1. Spring 組件注入

Spring 組件注入有 4 種方式

  • 包掃描 + 組件標註註解(@Controller@Service@Repository@Component),配合 @ComponentScan 註解使用,主要用於自己寫的類

  • @Bean(導入第三方的組件)

  • @Import:給容器中導入一個組件

    • @Import:向容器中註冊組件一個 class,id 默認是全類名

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

    • ImportBeanDefinitionRegistrar:手動註冊 bean 到容器中

  • FactoryBean(工廠 Bean)

1.1 @ComponentScan

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-componentscan 工程

1.1.1 使用方式

@ComponentScan:指定要掃描的包

  • excludeFilters=ComponentScan.Filter[],按照規則排除某些組件

  • includeFilters=ComponentScan.Filter[],按照規則包含某些組件,需要將 useDefaultFilters=false 纔會生效

1.1.2 環境搭建
1. SpringConfig
/**
 * <p>description : SpringConfig
 * 第一個 {@link ComponentScan} 排除了 {@link Repository} 和 {@link Controller} 註解,即只掃描了 {@link PersonService}
 * 第二個 {@link ComponentScan} 掃描了 {@link Controller} 註解類型和 {@link PersonDao} 類型,注意這裏要把 useDefaultFilters 屬性設置爲 false
 * 第三個 {@link ComponentScan} 根據 {@link CustomTypeFilter} 自定義加載 bean
 *
 * <p>
 * FilterType.ANNOTATION 按照註解匹配
 * FilterType.ASSIGNABLE_TYPE 按照指定的類型
 * FilterType.ASPECTJ 使用 ASPECTJ 表達式
 * FilterType.REGEX 使用正則表達式
 * FilterType.CUSTOM 使用自定義方式
 *
 * <p>blog : https://blog.csdn.net/masteryourself
 *
 * @author : masteryourself
 * @version : 1.0.0
 * @date : 2020/3/28 20:20
 */
@Configuration
@ComponentScan(
        value = "pers.masteryourself.tutorial.spring.framework.register",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Repository.class, Controller.class})}
)
@ComponentScan(
        value = "pers.masteryourself.tutorial.spring.framework.register",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = PersonDao.class)
        }, useDefaultFilters = false
)
@ComponentScan(value = "pers.masteryourself.tutorial.spring.framework.register", includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = CustomTypeFilter.class)
}, useDefaultFilters = false)
public class SpringConfig {
}

1.2 @Bean

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-bean 工程

1.2.1 使用方式

@Configuration:聲明是一個配置類,相當於 xml 配置文件

@Bean:給容器注入一個 bean,類型爲返回值的類型,id 默認是方法名作爲 id,可以通過 name 修改

1.2.2 環境搭建
1. SpringConfig
@Configuration
public class SpringConfig {

	@Bean(name = "personXxx")
	public Person person() {
		return new Person();
	}

}

1.3 @Import

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-import 工程

1.3.1 使用方式

@Import:向容器中註冊組件一個 class,id 默認是全類名

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

ImportBeanDefinitionRegistrar:手工註冊 bean 到容器中

1.3.2 環境搭建
1. ExtImportSelector
public class ExtImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"pers.masteryourself.tutorial.spring.framework.register.bean.Dog"};
    }

}
2. ExtImportBeanDefinitionRegistrar
public class ExtImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(Import.class.getName());
        // 獲取註解元數據:
        // {value=[
        // class pers.masteryourself.tutorial.spring.framework.register.bean.Cat,
        // class pers.masteryourself.tutorial.spring.framework.register.config.ExtImportSelector,
        // class pers.masteryourself.tutorial.spring.framework.register.config.ExtImportBeanDefinitionRegistrar]
        // }
        System.out.println("獲取註解元數據:" + annotationAttributes);
        // 指定 bean 定義信息
        BeanDefinition beanDefinition = new RootBeanDefinition(Mouse.class);
        // 手工註冊
        registry.registerBeanDefinition("mouse", beanDefinition);
    }

}
3. SpringConfig
@Import({Cat.class, ExtImportSelector.class, ExtImportBeanDefinitionRegistrar.class})
public class SpringConfig {
}

1.4 FactoryBean

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-factorybean 工程

1.4.1 使用方式

FactoryBean:使用 spring 提供的 FactoryBean(工廠 bean)

默認獲取到的是工廠 bean 調用 getObject() 方法創建的對象

要獲取工廠 bean 本身,需要在 id 前面加一個 &

1.4.2 環境搭建
1. PersonFactoryBean
public class PersonFactoryBean implements FactoryBean<Person> {

    @Override
    public Person getObject() throws Exception {
        System.out.println("調用了 PersonFactoryBean.getObject() 方法");
        return new Person();
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}
2. SpringConfig
@Configuration
public class SpringConfig {

    @Bean(name = "person")
    public PersonFactoryBean personFactoryBean() {
        return new PersonFactoryBean();
    }

}
3. FactoryBeanTest
public class FactoryBeanApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 調用了 PersonFactoryBean.getObject() 方法
        Person person1 = context.getBean("person", Person.class);
        Person person2 = context.getBean("person", Person.class);
        // true
        System.out.println(person1 == person2);
        // class pers.masteryourself.tutorial.spring.framework.register.bean.Person
        System.out.println(person1.getClass());
        PersonFactoryBean personFactoryBean1 = context.getBean(PersonFactoryBean.class);
        PersonFactoryBean personFactoryBean2 = context.getBean("&person", PersonFactoryBean.class);
        // true
        System.out.println(personFactoryBean1 == personFactoryBean2);
        // class pers.masteryourself.tutorial.spring.framework.register.config.PersonFactoryBean
        System.out.println(personFactoryBean1.getClass());
    }

}

2. 其他註解

2.1 @Scope

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-scope 工程

2.1.1 使用方式

@Scope:指定作用域範圍

  • prototype:多實例,ioc 容器啓動並不會去調用方法創建對象放在容器中,每次獲取的時候纔會調用方法創建對象

  • singleton:單實例(默認值),ioc 容器啓動會調用方法創建對象放到 ioc 容器中,以後獲取就是直接從容器中獲取

  • request:同一個請求創建一個實例

  • session:同一個 session 創建一個實例

2.1.2 環境搭建
1. SpringConfig
@Configuration
public class SpringConfig {

    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean(name = "person")
    public Person person() {
        return new Person("張三");
    }

    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    @Bean(name = "person2")
    public Person person2() {
        return new Person("李四");
    }

}
2. ScopeTest
public class ScopeApplication {

    public static void main(String[] args) {
        // 【李四】 用戶創建成功
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        // ioc 容器創建完成
        System.out.println("ioc 容器創建完成");
        // 【張三】 用戶創建成功
        Person person1 = context.getBean("person", Person.class);
        // 【張三】 用戶創建成功
        Person person2 = context.getBean("person", Person.class);
        // false
        System.out.println(person1 == person2);
        context.getBean("person2", Person.class);
    }
    
}

2.2 @Lazy

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-lazy 工程

2.2.1 使用方式

@Lazy:懶加載,只針對於單實例(因爲單實例 bean 默認在容器啓動時候加載),在第一次使用的時候創建對象

2.2.2 環境搭建
1. SpringConfig
@Configuration
public class SpringConfig {

    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    @Bean(name = "person")
    @Lazy
    public Person person() {
        return new Person();
    }

}
2. LazyTest
public class LazyApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        // ioc 容器創建完成
        System.out.println("ioc 容器創建完成");
        // 創建 peron 對象
        context.getBean("person", Person.class);
        context.getBean("person", Person.class);
    }

}

2.3 @Conditional

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-framework/tutorial-spring-framework-register/tutorial-spring-framework-register-conditional 工程

2.3.1 使用方式

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

2.3.2 環境搭建
1. BatCondition
public class BatCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		LocalDateTime now = LocalDateTime.now();
		int hour = now.getHour();
		return hour >= 18 || hour <= 6;
	}

}
2. DogCondition
public class DogCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		LocalDateTime now = LocalDateTime.now();
		int hour = now.getHour();
		return hour > 6 && hour < 18;
	}

}
3. SpringConfig
@Configuration
public class SpringConfig {

    @Bean
    @Conditional(value = DogCondition.class)
    public Animal dog() {
        return new Animal("小狗");
    }

    @Bean
    @Conditional(value = BatCondition.class)
    public Animal bat() {
        return new Animal("蝙蝠");
    }
}
4. ConditionalTest
public class ConditionalApplication {

    public static void main(String [] args){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        Map<String, Animal> beansOfType = context.getBeansOfType(Animal.class);
        // Animal{name='蝙蝠'}
        beansOfType.forEach((name,animal) -> System.out.println(animal));
    }

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