Bean定义模式

在Spring的IOC实现中,对于Bean的定义有三种模式:

模式 应用类型 IOC容器启动方式
纯XML JavaSE ApplicationContext context = new ClassPathXMLApplicationContext("Beans.xml")
或者 new FileSystemXmlApplicationContext("/root/Bean.xml")
JavaWeb ContextLoaderListener监听器去加载XML
XML+注解 JavaSE ApplicationContext context = new ClassPathXMLApplicationContext("Beans.xml")
或者 new FileSystemXmlApplicationContext("/root/Bean.xml")
JavaWeb ContextLoaderListener监听器去加载XML
纯注解 JavaSE ApplicationContext context = new AnnotationContextApplicationContext(Config.class)
JavaWeb ContextLoaderListener监听器去加载注解配置类

BeanFactory与ApplicationContext

  BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范。
  ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。

  通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,⽐BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等。

img.png

纯XML方式

  虽然文件名是可以自定义,但是一般都命名为applicationContext.xml。实现Bean的三种方式:

  1. 使⽤⽆参构造函数
    在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。
    1
    2
    <!--配置service对象-->
    <bean id="userService" class="com.demo.service.impl.UserServiceImpl"></bean>
  2. 使⽤静态⽅法创建
    这种方法多用于将自定义的类加入到IOC容器。
    在实际开发中,使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程中会做很多额外的操作(比如初始化数据库连接要注册驱动,还要提供URL和凭证信息)。此时会提供⼀个创建对象的static修饰的⽅法,即是此种情况;
    1
    2
    <!--使⽤静态⽅法创建对象的配置⽅式-->
    <bean id="demoService" class="com.demo.factory.BeanFactory" factory-method="getDemoService"></bean>
  3. 使⽤实例化⽅法创建
    此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是类中的⼀个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。
    1
    2
    3
    <!--使⽤实例⽅法创建对象的配置⽅式-->
    <bean id="beanFactory" class="com.demo.factory.BeanFactory"></bean>
    <bean id="demoService" factory-bean="beanFactory" factorymethod="getDemoService"></bean>

Bean的作用范围

具体可以参考官网文档地

scope 描述
singleton 默认值,单例模式,Bean在IOC容器中只有一份
prototype 原型,每次使用该类的对象,都返回一个新的对象
request 一个请求对应一个对象
session 一个会话对应一个对象
application 一个ServletContext对应一个对象
websocket 一个WebSocket对应一个对象

多例/原型模式的bean对象,spring框架只负责创建,不负责销毁。

Bean常用标签

  • id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
  • class属性:⽤于指定创建Bean对象的全限定类名。
  • name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
  • factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
  • factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
  • scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
  • init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
  • destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。

依赖注入

按照注⼊的⽅式分类

  • 构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
    它的使⽤要求是,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹配。同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。
    在使⽤构造函数注⼊时,涉及的标签是constructor-arg ,该标签有如下属性:
    • name:⽤于给构造函数中指定名称的参数赋值。
    • index:⽤于给构造函数中指定索引位置的参数赋值。
    • value:⽤于指定基本类型或者String类型的数据。
    • ref:⽤于指定其他Bean类型的数据。写的是其他bean的唯⼀标识
  • set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
    在使⽤set⽅法注⼊时,需要使⽤property标签,该标签属性如下:
    • name:指定注⼊时调⽤的set⽅法名称。(注:不包含set这三个字⺟)
    • value:指定注⼊的数据。它⽀持基本类型和String类型。
    • ref:指定注⼊的数据。它⽀持其他bean类型。写的是其他bean的唯⼀标识。

按照注⼊的数据类型分类

  • 基本类型和String
    注⼊的数据类型是基本类型或者是字符串类型的数据。
  • 其他Bean类型
    注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
  • 复杂类型(集合类型)
    注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。

XML+注解方式

xml+注解结合模式,xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始。
一般是第三⽅jar中的bean定义在xml,⾃⼰开发的bean定义使⽤注解。

xml中标签与注解的对应:

xml标签或属性 对应的注解形式
标签 @Component(“accountDao”),注解加在类上, bean的id属性内容直接配置在注解后⾯
如果不配置,默认定义个这个bean的id为类的类名⾸字⺟⼩写;
另外,针对分层代码开发提供了@Componenet的三种别名
@Controller、@Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,
这四个注解的⽤法完全⼀样,只是为了更清晰的区分⽽已
scope属性 @Scope(“prototype”),默认单例,注解加在类上
init-method属性 @PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法
destroy-method属性 @PreDestroy,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法

DI 依赖注⼊的注解:

注解 要求
@Autowired 推荐使用,采取的策略为按照类型注⼊,是Spring提供的注解,需要导⼊包org.springframework.beans.factory.annotation.Autowired
@Qualifier 当有多个bean值的时候,根据此注解决定要使用那个
@Resource 默认按照ByName⾃动注⼊,由J2EE提供,需要导⼊包javax.annotation.Resource
在Jdk11中已经移除,如果要使⽤,需要单独引⼊jar包

注意,对于@Resource注解:

  1. 如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不到则抛出异常。
  2. 如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异常。
  3. 如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个,都会抛出异常。
  4. 如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;

纯注解方式

  • @Configuration: 表明当前类是⼀个配置类;
  • @ComponentScan: 替代 context:component-scan;
  • @PropertySource: 引⼊外部属性配置⽂件
  • @Import: 引⼊其他配置类
  • @Value: 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
  • @Bean: 将⽅法返回对象加⼊ SpringIOC 容器

延迟加载

  ApplicationContext容器的默认⾏为是在启动服务器时将所有singleton bean提前进⾏实例化。即启动时创建并配置所有的singleton bean
  延迟加载对应的属性值为lazy-init,默认为false,例如:

1
2
3
<bean id="testBean" class="cn.demo.LazyBean" />
<!-- 该bean默认的设置为: -->
<bean id="testBean" calss="cn.demo.LazyBean" lazy-init="false" />

lazy-init="false",为⽴即加载,表示在spring启动时,⽴刻进⾏实例化。
设置lazy-inittrue的 bean,将不会在ApplicationContext启动时提前被实例化,⽽是第⼀次向容器通过getBean索取bean时实例化的。

如果⼀个设置了lazy-inittrue(⽴即加载)的 beanA,引⽤了⼀个lazy-initfalse(延迟加载)的beanB,那么beanA在容器启动时被实例化,⽽beanB由于被beanA引⽤,所以也被实例化。

如果⼀个bean的scope属性为scope="pototype"时,即使设置了lazy-init="false",容器启动时也不会实例化bean,⽽是调⽤getBean⽅法实例化的。

BeanFactory与FactoryBean

BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;

BeanFactory源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";

Object getBean(String var1) throws BeansException;

<T> T getBean(String var1, Class<T> var2) throws BeansException;

Object getBean(String var1, Object... var2) throws BeansException;

<T> T getBean(Class<T> var1) throws BeansException;

<T> T getBean(Class<T> var1, Object... var2) throws BeansException;

<T> ObjectProvider<T> getBeanProvider(Class<T> var1);

<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

boolean containsBean(String var1);

boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

String[] getAliases(String var1);
}

Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean)。
FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程

FactoryBean源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean创建的Bean实例,如果isSingleton()返回true,则该实例会放到Spring容器的单例对象缓存池中
T getObject() throws Exception;

@Nullable
// 返回FactoryBean创建的Bean类型
Class<?> getObjectType();

// 返回作⽤域是否单例
default boolean isSingleton() {
return true;
}
}

最终通过FactoryBean获取到的是具体的Bean,如果想要获取FactoryBean,只需要在获取时加”&”。

后置处理

Spring提供了两种后处理bean的扩展接⼝,分别为BeanPostProcessorBeanFactoryPostProcessor,两者在使⽤上是有所区别的。

  • BeanFactoryPostProcessor: 在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情;
  • BeanPostProcessor: 在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情;