1. xml方式实例化bean

  2. 注解方式实例化bean

  3. java方式实例化bean

  4. ClassPathXmlApplication和AnnotationConfigApplicationContext

  5. Bean存放到了哪里?

  6. 选取哪一种方式合适?

  7. bean的名称

    1. 默认的名称

    2. 设置名称

    3. 设置别名

  8. 延迟加载

  9. bean加载顺序

  10. bean范围

  11. bean的回调函数

  12. 组件扫描

    1. @Component @Controller @Service@Configuration

    2. @Bean@Configuration精简模式和完全模式

    3. 开启组件扫描

    4. 默认的扫描路径

    5. 扫描过滤

  13. 内部类实例化

  14. 静态工厂方法

  15. 实例工厂方法

  16. BeanPostProcessor

  17. BeanFactoryPostProcessor

  18. FactoryBean

  1. bean的继承

  2. 多个配置间互相拾取

    1. @Import java配置互相拾取

    2. <import> xml文件互相拾取

    3. @ImportResource java配置拾取xml

    4. <context:component-scan/> xml注解扫描

  3. spring5.0生成候选组件的索引

  4. 国际化

  5. @Profile环境抽象

  6. 事件监听

  7. ApplicationContext的父子关系

阅读须知

1 本文核心内容来自 https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html

2 在我自己阅读文档并写出test后,为了提高本文的可读性和连续性,我决定本文不按照spring文档目录顺序排版。将IOC部分拆分为 实例化bean,为bean注入属性,spring的其他特性三个方面。

3 文章中所包含的test都经过测试。git地址为 https://gitee.com/zumengjie/springcode-core-ioc

4 文章中对于spring的使用方式和使用技巧,仅代表我个人。如有错误,或不足,请留言。

5 文章环境为jdk8,springboot2.2.2.RELEASE

6 文章适用于使用过spring系列框架的朋友,或者准备学习spring的朋友。

7 最后,这篇博客从第一遍看spring文档到手稿,到整理,三周,一个个test敲出来。对我自身是有很大提升的,spring对于ioc部分的处理十分细腻,本文也只是根据我所看到的整理出来了。在原文中,还有很多东西没有被我挖掘出来。我觉得对于每一个学spring用spring的人来讲都应该去看一遍官方文档。在我看文档前,我担心自己英语不好,看不懂。在一篇博客上,有个前辈说,你看不懂的不是英文文档,而是文档。确实,一篇文档,如果没有一点根基,就算他是中文的,我们也不一定看一遍就懂,看一遍就记住。spring官方文档,值得我们耐着性子看,然后自己写test。只看文档和边看边写还是有很大差距的。下一章资源,会在后续更新。

实例化bean

1 xml方式实例化bean

spring-framework-core-iocContainer
<?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">
    <!-- class:哪个bean需要被实例化,就用哪个bean的路径
         id:告诉spring被实例化这个bean叫什么名字-->
    <bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
</beans>
View Code

2 注解方式实例化bean

spring-framework-core-iocContainer
package com.datang.springcode.annoRegisterBean;
import org.springframework.stereotype.Component;

//@Component标识这个类,需要被实例化
@Component
public class Pear {
}
View Code

3 java方式实例化bean

spring-framework-core-iocContainer
package com.datang.springcode.javaRegisterBean;

//需要被实例化的bean
public class Banana {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.javaRegisterBean;


import org.springframework.context.annotation.Bean;
//注意:此处RegisterBeanConfig类上我并未使用@Configuration注解,是因为我使用的AnnotationConfigApplicationContext对象时,
// 传递的是RegisterBeanConfig.class。否则,RegisterBeanConfig类必须也要使用xml方式或@Component配置成bean。
public class RegisterBeanConfig {

    //这个注解才是真正的java实例化bean的关键,可以指定多个value值,第一个往后,为别名
    @Bean(value = {"banana", "banana2", "banana3"})
    public Banana getBanana() {
        return new Banana();
    }
}
View Code

4 ClassPathXmlApplication和AnnotationConfigApplicationContext

spring-framework-core-iocContainer
    @Test
    public void t1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:xmlRegisterBean.xml");
        Computer computer = context.getBean("computer", Computer.class);
    }

    @Test
    public void t2() {
        ApplicationContext context = new AnnotationConfigApplicationContext(Pear.class);
        Pear bean = context.getBean(Pear.class);

    }

    @Test
    public void t3() {
        ApplicationContext context = new AnnotationConfigApplicationContext(RegisterBeanConfig.class);
        Object getBanana1 = context.getBean("getBanana");
    }
View Code

ClassPathXmlApplicationContext对象用来加载xml配置文件,AnnotationConfigApplicationContext用来加载一个类。spring通过抽象抽取了一个用户使用的对象ApplicationContext,此次ioc部分我们最常用的方法就是getBean()

ClassPathXmlApplicationContext有多个构造函数重载,这里提一种常见的 ClassPathXmlApplicationContext(String... configLocations)参数接收可变长度的字符串,也就是说,ClassPathXmlApplicationContext可以同时加载多个xml配置文件。

AnnotationConfigApplicationContext也有多个构造函数重载,最常用的是AnnotationConfigApplicationContext(Class<?>... componentClasses)参数接收可变长度的Class类对象,可以同时加载多个配置对象类。

getBean()方法的可重载方式很多,接收一个字符串根据bean的id查询,接收Class根据类型查询,还可以同时接收id和class。

5 Bean存放到了哪里?

上边介绍的三种方式无论哪一种最终都会将pojo实例化到spring容器中。那么我们经常说的spring容器到底是什么?通过debug查看ApplicationContext对象。在spring上下文中有一个beanFactory对象,该对象实现类DefaultListableBeanFactory。依次再找父类AbstractAutowireCapableBeanFactory-->AbstractBeanFactory-->FactoryBeanRegistrySupport-->DefaultSingletonBeanRegistry中有一个Map,registeredSingletons这个Map就是用来

存放所有单例模式的bean。关于bean的模式后边内容会说到。

spring-framework-core-iocContainer

spring-framework-core-iocContainer

6 选取哪一种方式合适?

以上三种实例化bean的方式,无论使用哪一种,最终的bean都会存到到registeredSingletons这个Map中。我们可以三种方式混着用,也可以只用一种方式。使用过springmvc框架的朋友一般都有过这样的配置。

spring-framework-core-iocContainer
 <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
 <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
View Code

ContextLoaderListener实现了ServletContextListener接口,所以能够监听Tomcat启动和停止。而在ContextLoaderListener中则读取了。classpath:applicationContext.xml。在这个xml中我们是不是了实例化bean,例如dataSource。

而使用springboot的朋友一般极少使用xml实例化的方式,就算有需要配置的也会在application.properties中配置。如果是我们用户自己的pojo,那使用注解方式实例化bean最方便,如果有一些系统pojo需要统一规范的配置,那就选取xml方式实例化bean,便于管理和修改,而且不容易配破坏,大家都不会随便修改xml的内容嘛。最后如果你使用的是第三方的jar,那此时使用java的实例化方式是最合适的了,毕竟你也无法在源码上加@Component注解。

7 bean的名称

7.1 默认名称

无论哪种方式实例化bean,被实例化到map中的bean都有一个唯一的名称。我们通过这个名称来找到bean,如getBean(),包括在注入属性时,我们也需要使用到bean的名称来找到bean。以上test中使用xml实例化bean时,我们使用id属性来指定bean的名称,不设置id也有默认的名称。而使用java方式和注解方式时,我们则没有指定bean的名称。为了演示方便我们将xml中实例化bean的id属性去掉。

spring-framework-core-iocContainer
package com.datang.springcode.javaRegisterBean;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
//扫描包下的注解
@ComponentScan(value = "com.datang.springcode")
//加载xml
@ImportResource(value = "classpath:xmlRegisterBean.xml")
public class RegisterBeanConfig {

    //这个注解才是真正的java实例化bean的关键。
    @Bean
    public Banana getBanana(){
        return new Banana();
    }
}
View Code

在上边代码中,使用@ComponentScan和@ImportResource注解分别扫描包中的注解和加载xml,为的就是我们能在一个Map中看到三个bean。xml方式实例化bean默认的id是类的全路径拼接#和编号。而@Component注解方式则是使用类名,首字母换成了小写。使用@Bean方式实例化bean时,bean的名称是@Bean所注释的方法名。

spring-framework-core-iocContainer

7.2 设置名称

spring-framework-core-iocContainer
package com.datang.springcode.javaRegisterBean;
import org.springframework.context.annotation.Bean;
public class RegisterBeanConfig {

    //这个注解才是真正的java实例化bean的关键。value指定bean的名称
    @Bean(value = "banana")
    public Banana getBanana(){
        return new Banana();
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.annoRegisterBean;
import org.springframework.stereotype.Component;

//@Component标识这个类,需要被实例化.value指定bean的名称
@Component(value = "pear")
public class Pear {
}
View Code
spring-framework-core-iocContainer
<?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">
    <!-- class:哪个bean需要被实例化,就用哪个bean的路径
         id:告诉spring被实例化这个bean叫什么名字-->
    <bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
</beans>
View Code

通过以上代码我们给每一个bean设置了名称。

spring-framework-core-iocContainer

7.3 设置别名

spring-framework-core-iocContainer
package com.datang.springcode.javaRegisterBean;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;

//扫描包下的注解
@ComponentScan(value = "com.datang.springcode")
//加载xml
@ImportResource(value = "classpath:xmlRegisterBean.xml")
public class RegisterBeanConfig {

    //这个注解才是真正的java实例化bean的关键,可以指定多个value值,第一个往后,为别名
    @Bean(value = {"banana", "banana2", "banana3"})
    public Banana getBanana() {
        return new Banana();
    }
}
View Code
spring-framework-core-iocContainer
<?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">
    <!-- class:哪个bean需要被实例化,就用哪个bean的路径
         id:告诉spring被实例化这个bean叫什么名字
         通过name设置别名,可以用 , 号分割-->
    <bean class="com.datang.springcode.xmlRegisterBean.Computer" ></bean>
    <!-- alias也可以给bean起别名,但是一个alias标签仅能指定一个别名 -->
    <alias name="computer" alias="computer4"/>
</beans>
View Code

spring-framework-core-iocContainer

通过别名也可以获取到bean。另外别名不可以重复,否则会被替换。在beanFactory下有aliasMap,记录的就是当前所有bean的别名。@Component注解实例化bean无法设置别名。

spring-framework-core-iocContainer

8 延迟加载

bean的延迟加载也就是说,在spring上下文启动时,不会立刻将bean实例化到registeredSingletonsMap中,而是在第一次使用getBean()时才会实例化。

spring-framework-core-iocContainer
@Component
@Lazy
public class Bookrack {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.lazyBean;
public class Schoolbag {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.lazyBean;
public class Stool {

}
View Code
spring-framework-core-iocContainer
<?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">

    <bean class="com.datang.springcode.lazyBean.Stool" ></bean>
</beans>
View Code
spring-framework-core-iocContainer
package com.datang.springcode.lazyBean;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Lazy;

@ComponentScan(value = "com.datang.springcode.lazyBean")

public class LazyConfig {
    @Bean(value = "schoolbag")
    @Lazy
    public Schoolbag getSchoolbag(){
        return new Schoolbag();
    }
}
View Code

以上代码通过三种方式实例化bean,并且通过三种方式实行懒加载。结果如下。在spring启动成功后,并singletonObects中并没有出现。Bookrack,Schoolbag,Stool三个类的实例对象。

spring-framework-core-iocContainer

但实际开发中我们又不会直接使用getBean()方法,多数会使用属性注入。比如@Autowried。如果Abean不是lazy,Bbean是lazy。Abean需要注入Bbean,那么在实例化Abean时也会把Bbean实例化了。

9 bean加载顺序

假设一个场景,我们有一个全局配置类sitting,里边有默认的几个静态全局变量。同时我们在程序的各个地方调用通过static.的方式调用。但是,这里的参数有些在项目启动后需要从数据库重新取值,对此我们创建了一个专门重新赋值的刷新类,该类初始化过程中,从数据库取值,然后在重新赋值到sitting类中。在此场景中,散布在程序各处的类和从数据库取值的刷新类没有明显依赖注入关系,但是有弱依赖关系。也就是说,spring必须先把刷新类实例化到Map中,然后才能实例化其他各个业务类。

spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

import org.springframework.stereotype.Component;

@Component(value = "cap")
public class Cap {
    public Cap(){
        System.out.println("cap");
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

@Component
@DependsOn(value = {"cap"})
public class Clothes {
    public Clothes(){
        System.out.println("clothes:我依赖于cap");
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

import org.springframework.stereotype.Component;

@Component(value = "bed")
public class Bed {
    public Bed(){
        System.out.println("bed");
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;
public class Quilt {
    public Quilt(){
        System.out.println("quilt:我依赖于bed");
    }
}
View Code
spring-framework-core-iocContainer
<?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">
    <bean class="com.datang.springcode.depends_on.Quilt" ></bean>
</beans>
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

public class Phone {
    public Phone() {
        System.out.println("phone:我依赖于charger");
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

public class Charger {
    public Charger() {
        System.out.println("charger");
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.depends_on;

import org.springframework.context.annotation.*;

@Configuration
@ImportResource(value = {"classpath:depends_on.properties.xml"})
@ComponentScan(value = {"com.datang.springcode.depends_on"})
public class DependsOnConfig {

    @Bean(value = {"phone"})
    @DependsOn(value = {"charger"})
    public Phone getPhone() {
        return new Phone();
    }

    @Bean(value = {"charger"})
    public Charger getCharger() {
        return new Charger();
    }

}
View Code

以上代码,演示了三种实例化bean的方式分别使用不同的方式指定bean的加载顺序。xml实例化方式使用depends-on属性,java实例化方式和注解实例化方式使用@DependsOn注解。两种方式都可以指定多个值。

spring-framework-core-iocContainer

10 bean范围

在实例化bean时,我们可以选择bean的活动范围。spring默认实例化单例bean,我们可以自定义选择注入prototype原型bean(有会讲多实例bean)。在web环境还有request,session,application和websocket。

spring-framework-core-iocContainer
package com.datang.springcode.scope;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "prototype")
public class Refrigerator {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scope;
public class Television {
}
View Code
spring-framework-core-iocContainer
<?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">

    <bean class="com.datang.springcode.scope.Television" />
</beans>
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scope;
public class Spoon {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scope;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Scope;

@ComponentScan(value = {"com.datang.springcode.scope"})
@ImportResource(value = {"classpath:scope.xml"})
public class ScopeConfig {

    @Bean(value = "spoon")
    @Scope(value = "prototype")
    public Spoon getSpoon(){
        return new Spoon();
    }
}
View Code
spring-framework-core-iocContainer
    @Test
    public void t6(){
        ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);

        Refrigerator refrigerator = context.getBean(Refrigerator.class);
        Refrigerator refrigerator1 = context.getBean(Refrigerator.class);
        Assert.assertFalse(refrigerator==refrigerator1);

        Television television = context.getBean(Television.class);
        Television television1 = context.getBean(Television.class);
        Assert.assertFalse(television==television1);

        Spoon spoon = context.getBean(Spoon.class);
        Spoon spoon1 = context.getBean(Spoon.class);
        Assert.assertFalse(spoon==spoon1);

    }
View Code

上边测试使用三种实例化bean的方式,实例化了三个原型bean。在singletonObjectsMap中并没有上边所实例化的三个单例bean。

spring-framework-core-iocContainer

另外我们也可以在。beanFactory下的beanDefinitionMap中查看所有bean的描述信息。在spring实例化bean的过程中,有一个步骤会将收集到的每个pojo信息存到到beanDefinitionMap中,等到以后使用。例如在实例化时,会先查看这个pojo是否是singleton,如果不是则不会实例化。

spring-framework-core-iocContainer

11 bean的回调函数

在spring实例化bean后,和销毁bean之前有两个钩子函数。spring有三种方式实现这两个钩子函数。分别时xml配置,注解,和实现接口。

spring-framework-core-iocContainer
package com.datang.springcode.beanCallBack;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//此处标记类需要被扫描
@Component
public class House implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("接口形式的回调after");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("接口形式的回调destroy");
    }
    
    public House() {
        System.out.println("house的构造函数");
    }

    public void xmlInit() {
        System.out.println("xml形式初始化回调");
    }

    public void xmlDes() {
        System.out.println("xml形式销毁回调");
    }

    @PostConstruct
    public void annInit() {
        System.out.println("注解形式的初始化回调");
    }

    @PreDestroy
    public void annDes() {
        System.out.println("注解形式的销毁回调");
    }

 


}
View Code
spring-framework-core-iocContainer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描包,注意导入命名空间 -->
    <context:component-scan base-package="com.datang.springcode.beanCallBack"/>
    <bean class="com.datang.springcode.beanCallBack.House" />
</beans>
View Code

spring-framework-core-iocContainer

注意这三种方式同时使用时的顺序。注解优先,实现接口其次,xml配置最后。

12 组件扫描

12.1 @Component @Controller @Service @Repository @Configuration

@Component只能被标记在类上,被@Component标记表示这个类需要被spring实例化。

spring-framework-core-iocContainer

spring-framework-core-iocContainer
package com.datang.springcode.scanPack;

import org.springframework.stereotype.Component;

@Component
public class Milk {

}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scanPack;
public class Fruit {
}
View Code
spring-framework-core-iocContainer
@ComponentScan(value = {"com.datang.springcode.scanPack"})
public class ScanPackConfig {

}
View Code

spring-framework-core-iocContainer

上边代码片段,Fruit类没有被@Component标记,所以没有被实例化到singletonObjects这个Map中。@Controller @Service @Repository @Configuration 注解都是和@Component组合的注解。也就是说,这4个注解和@Component有同等的效果。其中 @Controller @Service @Repository 在语义化上代表服务层,业务层,持久层。而 @Configuration则一般和@Bean使用。也只有@Configuration是有特殊的功能。

spring-framework-core-iocContainer

spring-framework-core-iocContainer

spring-framework-core-iocContainer

spring-framework-core-iocContainer

12.2 @Bean@Configuration的精简模式和完全模式

略微修改Milk类。

spring-framework-core-iocContainer
package com.datang.springcode.scanPack;

public class Milk {
    
    public Fruit fruit;
    //使其构造函数需要持有一个Fruit实例
    public Milk(Fruit fruit) {
        this.fruit = fruit;
    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scanPack;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan(value = {"com.datang.springcode.scanPack"})
@Configuration
public class ScanPackConfig {

    @Bean(value = "milk")
    public Milk getMilk() {
        //这种写法完全没有问题。构造函数需要一个Fruit而getFruit()方法返回的就是一个fruit
        return new Milk(getFruit());
    }

    @Bean(value = "fruit")
    public Fruit getFruit() {
        return new Fruit();
    }
}
View Code

spring-framework-core-iocContainer

看结果,两个fruit是同一个对象。这种结果就是完全模式。再看@Configuration

spring-framework-core-iocContainer

默认是true。我们修改成false试试。

spring-framework-core-iocContainer
package com.datang.springcode.scanPack;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan(value = {"com.datang.springcode.scanPack"})
@Configuration(proxyBeanMethods = false)
public class ScanPackConfig {

    @Bean(value = "milk")
    public Milk getMilk() {
        //这种写法完全没有问题。构造函数需要一个Fruit而getFruit()方法返回的就是一个fruit
        return new Milk(getFruit());
    }

    @Bean(value = "fruit")
    public Fruit getFruit() {
        return new Fruit();
    }
}
View Code

spring-framework-core-iocContainer

现在发现两个fruit就不是同一个对象了。但是在singletonObjects中只有一个fruit

spring-framework-core-iocContainer

这就是@Bean的精简模式。也是@Configuration和@Component的区别。

12.3 开启组件扫描

spring提供两种方式的组件扫描,注意组件扫描只会扫描类上标记有@Component注解或和@Component的组合注解如@Service

spring-framework-core-iocContainer
package com.datang.springcode.scanPack2;

import org.springframework.stereotype.Component;

@Component
public class Reality {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scanPack2;

import org.springframework.stereotype.Component;

@Component
public class Exist {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scanPack2;

public class Dream {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.scanPack2;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(value = "com.datang.springcode.scanPack2")
public class ScanPack2Config {
}
View Code

使用@ComponentScan注解扫描Dream类没有标记@Component所以没有被实例化出来。

spring-framework-core-iocContainer

spring-framework-core-iocContainer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描包,注意导入命名空间 -->
    <context:component-scan base-package="com.datang.springcode.scanPack2"/>
</beans>
View Code

使用xml方式配置扫描器,结果也是一样的。

spring-framework-core-iocContainer

12.4默认的扫描路径

即使我们使用@ComponentScan注解时,不指定value或者basePackages属性,spring会默认扫描,该配置类的平级以及自包中所有@Component。

spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Clerk extends Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Manager extends Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;

import org.springframework.stereotype.Component;

@Component
public class Smoke {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1.child;

import org.springframework.stereotype.Component;

@Component
public class Mask {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;

import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class FilterScan1Config {
}
View Code

spring-framework-core-iocContainer

spring-framework-core-iocContainer

同级中的@Component被扫描到,子包中的@Component也可以被扫描到。这也就是springboot启动类以下的包可以被扫描到的原因。spring-framework-core-iocContainer

spring-framework-core-iocContainer

12.5 扫描过滤

在组件扫描过程中,除了扫描@Component标记的类,我们也可以额外的设置扫描规则。

spring-framework-core-iocContainer

在@ComponentScan注解的属性中,有includeFilters()和excludeFilters()两个属性,分别是包含和排除,而这两个属性可接收多个Filter。我们来看看Filter中的type()属性,有哪些可选值。

spring-framework-core-iocContainer

FilterType下有五种匹配模式,顺序依次是。注解匹配,类型匹配,AspectJ表达式匹配,正则表达式匹配,自定义匹配器。

spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Manager extends Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;
public class Clerk extends Employee {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;

import org.springframework.stereotype.Component;

@Component
public class Smoke {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1.child;

import org.springframework.stereotype.Component;

@Component
public class Mask {
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;

@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Employee.class})})
public class FilterScan1Config {
}
View Code

以上代码片段,Employee Clerk Manager 三个类没有被@Component注解。但我们在扫描组件时使用 includeFilters 并且按照 FilterType.ASSIGNABLE_TYPE Class类型 值是 Employee.class 如此Employee类及其子类可以被spring实例化。

spring-framework-core-iocContainer

下边我们在试试excludeFilters,此时@Component标记的类没有被注入。

spring-framework-core-iocContainer
package com.datang.springcode.filterScan1;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Component;

@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {Employee.class})},
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Component.class)})
public class FilterScan1Config {
}
View Code

spring-framework-core-iocContainer

使用xml配置方式也可以做到过滤扫描,需要注意的是,在xml中assignable代表class类型,和Filter中的ASSIGNABLE_TYPE作用一样。<context:include-filter/>必须在<context:exclude-filter/>之前

spring-framework-core-iocContainer
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描包,注意导入命名空间 -->
    <context:component-scan base-package="com.datang.springcode.filterScan1">
        <!-- 加载Employee类 -->
        <context:include-filter type="assignable" expression="com.datang.springcode.filterScan1.Employee"/>
        <!-- 排除配置类 -->
        <context:exclude-filter type="assignable" expression="com.datang.springcode.filterScan1.FilterScan1Config"/>
        <!-- 排除@Component注解-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
</beans>
View Code

spring-framework-core-iocContainer

13 内部类实例化

在使用xml实例化bean时,内部类必须是static的,否则报错

spring-framework-core-iocContainer
package com.datang.springcode.innerBean;
public class Teacher {
     static class  Student {

    }
}
View Code
spring-framework-core-iocContainer
<?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">
    <bean class="com.datang.springcode.innerBean.Teacher$Student" />
</beans>
View Code

spring-framework-core-iocContainer

@Component方式,外部类必须要是一个springbean,内部类必须是静态的。

spring-framework-core-iocContainer
package com.datang.springcode.innerBean;

import org.springframework.stereotype.Component;
@Component
public class Family {
    @Component
    static class Person{

    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.innerBean;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = {"com.datang.springcode.innerBean"})
public class InnerBeanConfig {

}
View Code

spring-framework-core-iocContainer

java方式实例化bean则比较灵活

spring-framework-core-iocContainer
package com.datang.springcode.innerBean;

public class Food {
    class Bread {

    }
}
View Code
spring-framework-core-iocContainer
package com.datang.springcode.innerBean;

import org.springframework.context.annotation.Bean;

public class InnerBeanConfig2 {
    @Bean
    public Food.Bread getBread() {
        return new Food().new Bread();
    }
}
View Code

spring-framework-core-iocContainer

14 静态工厂方法

spring-framework-core-iocContainer
package com.datang.springcode.staticFactoryBean;

public class Conditioner {

    private Conditioner() {
    }

    private static Conditioner conditioner = new Conditioner();

    public static Conditioner getConditioner() {
        return conditioner;
    }
}
View Code
spring-framework-core-iocContainer
<?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">

    <bean class="com.datang.springcode.staticFactoryBean.Conditioner" />
</beans>
View Code

注意,静态工厂,本身无法实例化,需要提供一个静态的方法,用来返回bean。其实静态工厂方法,如果您需要的是单例对象。不使用springbean完全可以。

spring-framework-core-iocContainer

延用刚才的pojo。我们用@Bean注入,其实这种做法就有点脱裤子放屁的感觉了,感受下。

spring-framework-core-iocContainer
package com.datang.springcode.staticFactoryBean;

import org.springframework.context.annotation.Bean;

public class StaticFactoryBeanConfig {

    @Bean
    public Conditioner getConditioner() {
        return Conditioner.getConditioner();
    }
}
View Code

spring-framework-core-iocContainer

@Component注解方式无法创建静态工厂bean。因为构造方法私有化。

15 实例工厂方法 

只有xml配置支持实例工厂方法,要配置由实例工厂返回的pojo,需要先配置工厂类。

spring-framework-core-iocContainer
<?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">

    <bean class="com.datang.springcode.newBeanFacotry.Mask" id="mask"/>

    <bean id="glove" factory-bean="mask" factory-method="getGlove"/>
</beans>
View Code

相关文章:

  • 2021-08-03
  • 2022-12-23
  • 2021-09-18
  • 2019-11-11
  • 2022-12-23
  • 2022-01-22
猜你喜欢
  • 2022-12-23
  • 2022-01-04
  • 2022-02-22
  • 2022-12-23
  • 2021-11-14
  • 2022-12-23
  • 2018-10-24
相关资源
相似解决方案