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));
}
}