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

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值