目录
组件注册
- 一、@Configuration
- 二、@Bean
- 三、@ComponentScan
- 四、@Scope、@Lazy、@PostConstruct、@PreDestroy
- 五、@Condition
- 六、@Import
- 七、使用FactoryBean注册组件
@Order 注解 : 主要用来控制配置类的加载顺序
@Order 及 Order接口
给容器中注册组件
包扫描+组件标注注解
(@Component、@Service、@Controller、@Repository,主要是自己写的类@Bean
[导入的第三方包里面的组件]@Import
[快速给容器中导入一个组件]Import(类名.class)
,容器中就会自动注册这个组件,id默认是组件的全名ImportSelector
:返回需要导入的组件的全类名的数组- ImportBeanDefinitionRegistrar:手动注册bean
- 使用Spring提供的FactoryBean(工厂bean)
- 默认获取到的是工厂bean调用getObject创建的对象
- 要获取到bean本身,需要给id前面加个&标识
@Conditional({Condition})
:按照一定的条件判断,满足条件给容器中注册bean@Scope
- prototype:多例的 ioc容器启动并不会去调用方法创建对象在容器中,而是每次获取时才会调用方法创建对象
- singleton:单例的(默认值) ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是从容器中拿
一、@Configuration
@Configuration
: 把一个类标记为spring的配置类,相当于之前的applicationContext.xml文件
1、看看之前通过applicationContext.xml
配置文件来创建类的实例
public class SomeBean {}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建SomeBean的实例,交给IoC来管理 -->
<bean id="somebean" class="com.zy._01_hello.SomeBean"/>
</beans>
测试方法
public class SomeBeanTest {
/*
Spring XML Config
*/
@Test
public void test(){
// 加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 从Spring容器中获取SomeBean的实例
SomeBean someBean = ctx.getBean("somebean", SomeBean.class);
System.out.println(someBean);
}
}
- 这种方式是通过
Spring XML Config
的方式来将类
交给Spring容器处理; 但是后来发现有很多这样的xml
不容易管理,形成了配置类
; 于是就有了后来的SpringBoot
在SpringBoot中几乎看不到配置文件
了,取而代之的是Spring Java Config
的配置类的形式!
2、创建配置类
// 把一个类标记为spring的配置类; (类名Config可以简单理解为XML中的 beans)
@Configuration
public class Config {
// <bean id="somebean" class="com.zy._01_hello.SomeBean"/>
@Bean // 给容器中注册一个Bean, 类型为返回值类型, id默认是方法名
public SomeBean somebean(){
return new SomeBean();
}
}
测试方法
public class SomeBeanTest {
/*
Spring Java Config
*/
@Test
public void test1(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
SomeBean someBean = ctx.getBean(SomeBean.class);
System.out.println(someBean);
}
}
- 这里要通过
AnnotationConfigApplicationContext(config.class)
来加载配置类
,然后拿到配置类中SomeBean的实例即可(将组件的创建交给Spring容器处理, 也就是将组件注册到容器中)!
二、@Bean
@Bean
相当于在配置文件中写的<bean id="" class="" />
, 将一个类的创建实例交给Spring IoC来处理;
在配置文件
中写的bean
<bean id="" class="" name="" init-method="" destory-method="" scope="">
<property name="" value=""/>
<property name="" ref=""/>
</bean>
在配置类
中写的bean
@Configuration
public class Config {
@Bean
public SomeBean someBean1() {
return new SomeBean();
}
@Bean
public SomeBean someBean2() {
return new SomeBean();
}
@Bean(name = {"sb", "sbb"})
public SomeBean someBean3() {
return new SomeBean();
}
}
1、在配置类中@Bean的含义
- 被@Bean标注的
方法的名字
—> bean的id - 方法的
返回值类型
—> bean的class类型 - 除了默认使用方法的名字作为id外, 还可以通过
@Bean(name={"xxx1", "xxx2"})
来指定多个id名; 然后通过name来获取其对象
@Test
public void test2() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
SomeBean someBean1 = ctx.getBean("someBean1", SomeBean.class); // bean的id去找
System.out.println(someBean1);
SomeBean someBean2 = ctx.getBean("someBean2", SomeBean.class);
System.out.println(someBean2);
SomeBean someBean3 = ctx.getBean("sb", SomeBean.class);
System.out.println(someBean3);
SomeBean someBean4 = ctx.getBean("sbb", SomeBean.class);
System.out.println(someBean4);
}
2、配置initMethod、destroyMethod的方法
构造(对象创建)
- 单例: 在容器
启动
(加载配置类/加载配置文件)的时候创建对象;- 容器启动先创建对象, 然后调用init(初始化方法);
- 多例: 在每次
获取bean
(getBean())的时候创建对象;- 容器创建完成之后, 才创建对象完成init(初始化);
- 方式一: 可以在@Bean中的属性
initMethod
,destroyMethod
来指定初始化,销毁方法
@Bean(name="sb", initMethod = "init", destroyMethod = "destory")
- 方式二:
@PostConstruct
,@PreDestroy
来指定初始化,销毁方法
public class SomeBean {
// 方式二: 配置init,destory @PostConstruct,@PreDestroy
@PostConstruct
public void init() {
System.out.println("SomeBean.init");
}
@PreDestroy
public void destory() {
System.out.println("SomeBean.destory");
}
}
测试方法
public class SomeBeanTest {
/*
Spring Java Config
*/
@Test
void test(){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
SomeBean someBean = ctx.getBean("sb", SomeBean.class);
System.out.println(someBean);
ctx.close(); // 非Spring Test,容器不会正常关闭,调用close方法才可以
}
}
3、@Bean依赖注入的方式
相当于之前在applicationContext.xml中各个bean之间通过<property标签进行进行赋值, ref等
public class OtherBean {
}
@Setter
@Getter
public class SomeBean {
private OtherBean otherBean;
}
- 方式一: 通过类似
内部bean
的方式
@Configuration
public class Config {
/*
方式一: 相当于内部Bean的形式
<bean id="" class="">
<property name="otherBean">
<bean class="" /> 内部bean的方式
</property>
</bean>
这种方式用的很少!
*/
@Bean
public SomeBean someBean() {
SomeBean sb = new SomeBean();
sb.setOtherBean(new OtherBean());
return sb;
}
}
- 方式二: 通过调用需要注入的Bean的
方式名()
即可
配置类
@Configuration
public class Config {
@Bean
public OtherBean otherBean() {
return new OtherBean();
}
@Bean
public SomeBean someBean(OtherBean otherBean) {
SomeBean sb = new SomeBean();
sb.setOtherBean(otherBean);
return sb;
}
// 上面的简化写法
@Bean
public SomeBean someBean() {
SomeBean sb = new SomeBean();
sb.setOtherBean(otherBean());
return sb;
}
}
- 方式三: 需要依赖的
Bean
, 放入到参数列表
中,会自动注入
;- 有多个OtherBean的实例时,可以使用
@Qualifier("bean的id")
来指定 - 有多个OtherBean时,在某个bean上添加
@Primary
, 会优先注入该bean - 有多个OtherBean时,在参数列表中通过
形参名称
来指定对应的bean
- 有多个OtherBean的实例时,可以使用
@Bean
//@Primary
public OtherBean otherBean() {
return new OtherBean("ob1");
}
@Bean
public OtherBean otherBean2() {
return new OtherBean("ob2");
}
@Bean
// public SomeBean someBean(@Qualifier("otherBean") OtherBean ob) {
public SomeBean someBean(OtherBean otherBean) {
SomeBean sb = new SomeBean();
sb.setOtherBean(otherBean);
return sb;
}
三、@ComponentScan
- 可以完成
Spring组件的自动扫描
(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类; (@Configuration/ @Component/ @Service等注解)
配置类Config
@Configuration
// 可以完成Spring组件的自动扫描(默认情况下,会去扫描被标注的类的对应的包(及其子包中)的所有的类
//@ComponentScan(basePackages = "com.zy._04_componentscan") // 也可以自己指定扫描的范围
@ComponentScan
public class Config {
}
OtherBean和SomeBean类
@Component //设置该类作为Spring管理的组件
public class OtherBean {
}
@Component // 相当于之前在xml中写的bean标签
public class SomeBean {
// 将Spring通过@Component创建好的OtherBean的实例,注入到下面的属性中;
// 该实例是代理对象
@Autowired
private OtherBean otherBean;
}
测试方法
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class SomeBeanTest {
@Autowired
private SomeBean someBean;
@Test
public void test(){
System.out.println(someBean.getOtherBean());
}
}
- 组件注册时的
过滤条件
- @ComponentScan value:指定要扫描的包
excludeFilters
=Filter[]:指定扫描包的时候按照什么规则排除
哪些组件includeFilters
=Filter[]:指定扫描包的时候要包含
哪些组件,需将useDefaultFilters
置false- FilterType.
ANNOTATION
:按照注解 - FilterType.
ASSIGNABLE_TYPE
:按照指定的类型 - FilterType.ASPECTJ: 切入点表达式
- FilterType.REGEX:使用正则指定
- FilterType.CUSTOM:使用自定义规则
自定义规则: 需要继承TypeFilter类, 来实现其接口; 接口方法中可以获取当前类注解信息AnnotationMetadata/正在扫描的类信息ClassMetadata;通过类信息可以判断哪些bean组件要被加载到Spring容器
// 配置类 == 配置文件
@Configuration // 告诉Spring这是一个配置类
//@ComponentScan(value = "com.zy") // 不写默认扫描当前类所在包(及其子包)下的所有类(指定要扫描的包)
//@ComponentScan(value = "com.zy", excludeFilters = {
// // excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
// @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
//})
@ComponentScan(value = "com.zy", includeFilters = {
// excludeFilters: FilterType是过滤条件(这里是根据注解来过滤); 将Controller,Service的bean过滤掉(不扫描它们)
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
}, useDefaultFilters = false)
// @ComponentScan value: 指定扫描的包
// excludeFilters = Filter[] : 指定扫描的时候按照什么规则排除哪些组件
// includeFilters = Filter[] : 指定扫描的时候只需要包含哪些组件
// FilterType.ANNOTATION: 按照注解作为过滤规则
// FilterType.ASSIGNABLE_TYPE: 按照给定的类型作为过滤规则
public class MainConfig {
// 给容器中注册一个Bean, 类型为返回值类型, id默认是方法名
@Bean("person")
public Person person(){
return new Person("lisi", 22);
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MainConfig.class)
public class IoCTest {
@Test
public void test1(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
// 看容器中有哪些bean,返回这些bean的名称
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
}
四、@Scope
- 用来表示bean的范围(是
单例
还是多例
) prototype
: 多例的 : IoC容器启动并不会去调用方法创建对象
放在容器中, 每次获取bean的时候才会调用方法创建对象;singleton
: 单例的 : IoC容器启动就会调用方法创建对象
放到IoC容器中;- request: 同一次请求创建一个实例
- session: 同一个session创建一个实例
@Scope("prototype")
//@Scope // 默认不写value就是singleton
@Bean
public Person person(){
System.out.println("给容器中添加Person...");
return new Person("张三", 22);
}
@Test
public void test2(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
// 看容器中有哪些bean,返回这些bean的名称
// String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
// for (String name : beanDefinitionNames) {
// System.out.println(name);
// }
// 默认是单例的
System.out.println("IoC容器创建完成...");
Object person1 = ctx.getBean("person");
Object person2 = ctx.getBean("person");
System.out.println(person1 == person2);
}
@Lazy
第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了) ;
//@Scope("prototype")
@Scope // 默认是单实例
@Lazy //第一次使用Bean的时候创建,不使用则不创建(即使IoC容器启动了)
@Bean
public Person person(){
System.out.println("给容器中添加Person...");
return new Person("张三", 22);
}
@PostConstruct、@PreDestroy
初始化相关方法: @PostConstruct
等同于实现: InitializingBean接口
xml方式: <bean init-method=""/>
销毁方法:@PreDestory
等同于实现: DisposableBean接口
xml方式: <bean destory-method=""/>
注意:
上述的两个注解并不是 Spring 提供的,由 JSR(JavaEE规范)520 提供
五、@Condition
@Conditional
: 按照一定的条件进行判断,满足条件给容器中注册bean- @Conditional 放在配置类上, 当前配置类满足条件, 该配置类中所有@Bean才生效
- @Conditional 放在@Bean方法上, 只对该方法生效
如果不满足Conditional, 则不会加入到Spring容器
MacOSXCondition
// 判断是否Mac系统
public class MacOSXConditaion implements Condition {
/**
*
* @param conditionContext :判断条件能使用的上下文(环境)
* @param annotatedTypeMetadata : 注释信息
* @return
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 是否Mac系统
//1. 能获取到IoC使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//2. 获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//3. 获取到bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
// -----------------------------------------------------------
//4. 获取当前环境信息(封住系统的的环境信息等,虚拟机等信息)
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Mac OS X"))
return true;
return false;
}
}
WindowsCondition
// 判断是否windows系统
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Windows"))
return true;
return false;
}
}
配置类
// ComponentScan 这里省略扫描组件
/*
@Conditional: 按照一定的条件进行判断,满足条件给容器中注册bean
如果系统是windows, 给容器中注册("bill")
如果系统是mac, 给容器中注册("linus")
*/
// 配置到这个方法上,只对这个方法作条件判断: 如果满足,则这个方法注册的bean才能生效
@Conditional({WindowsCondition.class})
@Bean("bill")
public Person person1(){
return new Person("Bill Gates", 62);
}
@Conditional({MacOSXConditaion.class})
@Bean("linus")
public Person person2(){
return new Person("linus", 48);
}
测试
@Test
public void test3(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
// 根据Person类型来获取容器中bean的名称
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
// 动态获取环境变量的值: Mac OS X
Environment environment = ctx.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property); // Max OS X
for (String name : beanNamesForType) {
System.out.println(name); // linus
}
Map<String, Person> persons = ctx.getBeansOfType(Person.class);
System.out.println(persons); // 只会输出linus的相关信息
}
六、@Import
- 作用
- 用于快速导入
其他的配置类
。 - 也可以导入一个需要注册的
组件
(类), id默认是全类名
;
- 用于快速导入
DataSource类
public class DataSource {
}
RedisTemplate类
public class RedisTemplate {
}
DataSourceConfig配置类
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource(){
return new DataSource();
}
}
RedisConfig配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(){
return new RedisTemplate();
}
}
AppConfig配置类
@Configuration
@Import({DataSourceConfig.class, RedisConfig.class})
public class AppConfig {
}
ImportTest测试类
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = {DataSourceConfig.class, RedisConfig.class})
@ContextConfiguration(classes = AppConfig.class)
public class ImportTest {
@Autowired
private DataSource ds;
@Autowired
private RedisTemplate rt;
@Test
public void test() {
System.out.println(ds);
System.out.println(rt);
}
}
1、ImportSelector接口
- 返回需要
导入的组件
的全类名
需要导入的组件
public class Blue {
}
public class Red {
}
自定义MyImportSelector
// 自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
/**
*
* @param annotationMetadata 当前标注@Import注解类的所有注解信息
* @return 返回要导入到容器中的组件的全类名
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.zy.beans.Blue","com.zy.beans.Red"};
}
}
配置类
@Configuration
@Import(MyImportSelector.class)
public class MainConfig2 {
}
测试
@Test
public void testImport(){
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
printBeans((AnnotationConfigApplicationContext) ctx);
Blue bean = ctx.getBean(Blue.class);
System.out.println(bean); // 这样就可以成功在Spring 容器中获取到bean了; 底层扔是反射获取对象
}
private void printBeans(AnnotationConfigApplicationContext atx){
String[] definitionNames = atx.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}
2、ImportBeanDefinitionRegistrar接口
自定义类来实现该接口, 在接口方法中; 可以手动注册一个bean到Spring容器中
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param annotationMetadata 当前注解信息
* @param beanDefinitionRegistry BeanDefinition注册类
* 把所有需要添加到容器的bean; 调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 简单写点逻辑(看容器中是否有Red和Blue的bean)
boolean definition1 = beanDefinitionRegistry.containsBeanDefinition("red");
boolean definition2 = beanDefinitionRegistry.containsBeanDefinition("blue");
// 如果存在上面两个bean, 则将rainbow也注册到Spring容器
if (definition1 && definition2)
beanDefinitionRegistry.registerBeanDefinition("rainBow", new RootBeanDefinition(RainBow.class));
}
}
@ComponentScan("com.baizhiedu.bean")
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig6 {
@Bean
public Red red() {
return new Red();
}
@Bean
public Blue blue() {
return new Blue();
}
}
@Test
public void test() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig6.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String name : beanDefinitionNames) {
System.out.println(name);
}
}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig6
customer
red
blue
rainBow
3、@ImportResource
@ImportResource
来引入xml配置文件
,使xml和javaconfig共同使用
public class OtherBean {
}
@Setter
@Getter
public class SomeBean {
private OtherBean otherBean;
}
配置类
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
@Bean
public SomeBean someBean(OtherBean otherBean){
SomeBean sb = new SomeBean();
sb.setOtherBean(otherBean);
return sb;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建SomeBean的实例,交给IoC来管理 -->
<bean id="otherBean" class="com.zy._01_hello.OtherBean"/>
</beans>
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AppTest {
@Autowired
private SomeBean someBean;
@Test
public void test() {
System.out.println(someBean.getOtherBean());
}
}
七、使用FactoryBean注册组件
// 创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean.getObject");
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 是否是单例: true, 在容器中只会保留一份
@Override
public boolean isSingleton() {
return true;
}
}
配置类
@Configuration
public class MainConfig{
// 实际返回的是getObject()方法返回的对象
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
}
测试
@Test
public void testImport() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig2.class);
printBeans((AnnotationConfigApplicationContext) ctx);
Blue bean = ctx.getBean(Blue.class);
System.out.println(bean);
// 工厂Bean获取的是调用getObject创建的对象;
// 因为Spring容器会帮我们对Bean进行加工, 然后返回代理对象
Object bean2 = ctx.getBean("colorFactoryBean");
Object bean3 = ctx.getBean("colorFactoryBean");
System.out.println("bean的类型:" + bean2.getClass());
System.out.println(bean2 == bean3);
// 获取ColorFactorybean的本身
Object bean4 = ctx.getBean("&colorFactoryBean");
System.out.println(bean4.getClass());
}
private void printBeans(AnnotationConfigApplicationContext atx) {
String[] definitionNames = atx.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}