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