Bean是什么
JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制发现和操作这些JavaBean 的属性。
一个javaBean由三部分组成:属性、方法、事件
JavaBean的任务就是: “Write once, run anywhere, reuse everywhere”,即“一次性编写,任何地方执行,任何地方重用”。
Spring中Bean的配置
– 配置形式:基于 XML 文件的方式;基于注解的方式
– Bean 的配置方式:通过全类名(反射)、通过工厂方法(静态工厂方法 & 实例工厂方法)、FactoryBean
– IOC 容器 BeanFactory & ApplicationContext概述
– 依赖注入的方式:属性注入;构造器注入
– 注入属性值细节
– 自动转配
– bean 之间的关系:继承;依赖
– bean 的作用域:singleton;prototype;WEB 环境作用域s
– 使用外部属性文件
– spEL
– IOC 容器中 Bean 的生命周期
– Spring 4.x 新特性:泛型依赖注入
在 Spring 的 IOC 容器里配置 Bean
在 xml 文件中通过 bean 节点来配置 bean
<bean id="helloWorld" class="com.atguigu.spring.helloworld.HelloWorld">
bean>
id:Bean 的名称。
在 IOC 容器中必须是唯一的
若 id 没有指定,Spring 自动将权限定性类名作为 Bean 的名字
id 可以指定多个名字,名字之间可用逗号、分号、或空格分隔
Spring 容器
在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.
Spring 提供了两种类型的 IOC 容器实现.
BeanFactory: IOC 容器的基本实现.
ApplicationContext: 提供了更多的高级特性. 是 BeanFactory的子接口.
BeanFactory是 Spring 框架的基础设施,面向 Spring 本身;ApplicationContext面向使用 Spring 框架的开发者,几乎所有的应用场合都直接使用 ApplicationContext而非底层的 BeanFactory
无论使用何种方式, 配置文件是相同的.
ApplicationContext
ApplicationContext的主要实现类:
ClassPathXmlApplicationContext:从 类路径下加载配置文件
FileSystemXmlApplicationContext: 从文件系统中加载配置文件
ConfigurableApplicationContext扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close(), 让 ApplicationContext具有启动、刷新和关闭上下文的能力
ApplicationContext在初始化上下文时就实例化所有单例的 Bean。
WebApplicationContext是专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作
从 IOC 容器中获取 Bean
调用 ApplicationContext的 getBean() 方法
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld");
helloWorld.hello();
依赖注入的方式
Spring 支持 3 种依赖注入的方式
属性注入
构造器注入
工厂方法注入(很少使用,不推荐)
属性注入:
属性注入即通过 setter 方法注入Bean 的属性值或依赖的对象
属性注入使用 元素, 使用 name 属性指定 Bean 的属性名称,value 属性或 子节点指定属性值
属性注入是实际应用中最常用的注入方式
<property name="User" value="Starfish">property>
bean>
构造方法注入:
通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。
构造器注入在 元素里声明属性, 中没有 name 属性
按索引匹配入参:
按类型匹配入参:
字面值
字面值:可用字符串表示的值,可以通过 元素标签或 value 属性进行注入。
基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式
若字面值中包含特殊字符,可以使用
<bean id="address" class="com.starfish.spring.autowire.Address" p:city="BeiJing" p:street="HuoYing">bean>
<bean id="car" class="com.starfish.spring.autowire.Car" p:brand="Jeep" p:price="500000"> bean>
id="person" class="com.starfish.spring.autowire.Person" p:name="Starfish" autowire="byName">
依赖 Bean 配置
XML 配置里的 Bean 自动装配的缺点
•在 Bean 配置文件里设置 autowire属性进行自动装配将会装配 Bean 的所有属性. 然而, 若只希望装配个别属性时, autowire属性就不够灵活了.
•autowire属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之.
•一般情况下,在实际的项目中很少使用自动装配功能,因为和自动装配功能所带来的好处比起来,明确清晰的配置文档更有说服力一些
继承 Bean 配置
•Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean 的 Bean 称为子 Bean
•子 Bean 从父 Bean 中继承配置, 包括 Bean 的属性配置
•子 Bean 也可以覆盖从父 Bean 继承过来的配置
•父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean
•并不是 元素里的所有属性都会被继承. 比如: autowire, abstract 等.
•也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true
依赖 Bean 配置
•Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好
•如果前置依赖于多个 Bean,则可以通过逗号,空格或的方式配置 Bean 的名称
<bean id="address" class="com.starfish.spring.relation.Address"
p:city="BeiJing^" p:street="HuoYing" abstract="true">
bean>
<bean id="address1" class="com.starfish.spring.relation.Address"
parent="address" p:street="ZhiChunLu">
bean>
<bean id="car" class="com.starfish.spring.relation.Car"
p:brand="Jeep" p:price="500000" >bean>
<bean id="person" class="com.starfish.spring.relation.Person" p:name="starfish"
p:address-ref="address1" depends-on="car">bean>
Bean 的作用域
•在 Spring 中, 可以在 元素的 scope属性里设置 Bean 的作用域.
•默认情况下, Spring 只为每个在 IOC 容器里声明的 Bean 创建唯一一个实例, 整个 IOC 容器范围内都能共享该实例:所有后续的 getBean() 调用和 Bean 引用都将返回这个唯一的 Bean 实例.该作用域被称为 singleton, 它是所有 Bean 的默认作用域.
使用外部属性文件
•在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分离
•Spring 提供了一个 PropertyPlaceholderConfigurer的 BeanFactory后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 var的变量,PropertyPlaceholderConfigurer从属性文件里加载属性,并使用这些属性来替换变量.∙Spring还允许在属性文件中使用 {propName},以实现属性之间的相互引用。
注册 PropertyPlaceholderConfigurer
•Spring 2.0:
•Spring 2.5 之后: 可通过 元素简化:
– 中添加 context Schema 定义
–在配置文件中加入如下配置:
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}">property>
<property name="password" value="${jdbc.password}">property>
<property name="driverClass" value="${jdbc.driverClass}">property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}">property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}">property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}">property>
bean>
jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
Spring表达式语言:SpEL
•Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言。
•语法类似于EL:SpEL使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL
•SpEL为 bean 的属性进行动态赋值提供了便利
•通过 SpEL可以实现:
–通过 bean 的 id 对 bean 进行引用
–调用方法以及引用对象中的属性
–计算表达式的值
–正则表达式的匹配
SpEL:字面量
•字面量的表示:(为某一个bean的属性赋字面值)
–整数:
–小数:
–科学计数法:
–String可以使用单引号或者双引号作为字符串的定界符号:(必须有单引号)
<property name=“name” value="#{'Chuck'}"/> 或 <property name='name' value='#{"Chuck"}'/>
–Boolean:<property name="enabled" value="#{false}"/>
SpEL:引用 Bean、属性和方法
•引用其他对象 (其他bean)
•引用其他对象的属性
•调用其他方法,还可以链式操作
•调用静态方法或静态属性:通过 T() 调用一个类的静态方法,它将返回一个 Class Object,然后再调用相应的方法或属性:
SpEL支持的运算符号
•算数运算符:+, -, *, /, %, ^:
•加号还可以用作字符串连接:
•比较运算符(关系运算): <, >, ==, <=, >=, lt, gt, eq, le, ge
•逻辑运算符号: and, or, not, |
•if-else 运算符(条件运算):?: (ternary), ?: (Elvis)
•if-else 的变体
•正则表达式:matches
IOC 容器中 Bean 的生命周期方法
•Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.
•Spring IOC 容器对 Bean 的生命周期进行管理的过程:
–通过构造器或工厂方法创建 Bean 实例
–为 Bean 的属性设置值和对其他 Bean 的引用
–调用 Bean 的初始化方法
–Bean 可以使用了
–当容器关闭时, 调用 Bean 的销毁方法
•在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.
创建 Bean 后置处理器
•Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
•Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.
•对Bean 后置处理器而言, 需要实现 接口. 在初始化方法被调用前后, Spring 将把每个 Bean 实例分别传递给上述接口的以下两个方法:
添加 Bean 后置处理器后 Bean 的生命周期
•Spring IOC 容器对 Bean 的生命周期进行管理的过程:
–通过构造器或工厂方法创建 Bean 实例
–为 Bean 的属性设置值和对其他 Bean 的引用
–将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization方法
–调用 Bean 的初始化方法
–将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法
–Bean 可以使用了
–当容器关闭时, 调用 Bean 的销毁方法
通过工厂方法(静态工厂方法&实例工厂方法)配置Bean
通过调用静态工厂方法创建 Bean
•调用静态工厂方法创建 Bean是将对象创建的过程封装到静态方法中. 当客户端需要对象时, 只需要简单地调用静态方法, 而不同关心创建对象的细节.
•要声明通过静态方法创建的 Bean, 需要在 Bean 的 class属性里指定拥有该工厂的方法的类, 同时在 factory-method属性里指定工厂方法的名称. 最后, 使用 元素为该方法传递方法参数.
通过调用实例工厂方法创建 Bean
•实例工厂方法: 将对象的创建过程封装到另外一个对象实例的方法里. 当客户端需要请求对象时, 只需要简单的调用该实例方法而不需要关心对象的创建细节.
•要声明通过实例工厂方法创建的 Bean
–在 bean 的 factory-bean属性里指定拥有该工厂方法的 Bean
–在 factory-method属性里指定该工厂方法的名称
–使用 construtor-arg元素为工厂方法传递方法参数
<bean id="car1" class="com.starfish.spring.factory.StaticCarFactory" factory-method="getCars">
<constructor-arg value="audi">constructor-arg>
bean>
<bean id="carFactory" class="com.starfish.spring.factory.InstanceCarFactory">bean>
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="jeep">constructor-arg>
bean>
——————静态工厂方法——————————-
public class StaticCarFactory {
/* 静态工厂方法,直接调用某一个类的静态方法就可以返回bean的实例*/
private static Map
static{
cars.put("audi", new Car("audi",300000));
cars.put("jeep", new Car("jeep",500000));
}
//静态工厂方法
public static Car getCars(String name){
return cars.get(name);
}
}
——————实例工厂方法——————————-
//实例工厂方法:需要创建工厂本身,再调用工厂的实例方法来返回bean的实例
public class InstanceCarFactory {
private Map cars = new HashMap();
public InstanceCarFactory() {
cars = new HashMap();
cars.put("audi", new Car("audi",100000));
cars.put("jeep", new Car("jeep",100000));
}
public Car getCar(String brand){
return cars.get(brand);
}
}
———-Car———————————————–
private String brand;
private double price;
public Car(String brand, double price) {
super();
this.brand = brand;
this.price = price;
}
————–Text———————————-
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");
Car car2 = (Car) ctx.getBean("car2");
System.out.println(car2);
}
}
实现 FactoryBean接口在 Spring IOC 容器中配置 Bean
•Spring 中有两种类型的 Bean, 一种是普通Bean, 另一种是工厂Bean, 即FactoryBean.
•工厂 Bean 跟普通Bean不同, 其返回的对象不是指定类的一个实例, 其返回的是该工厂 Bean 的 getObject方法所返回的对象
通过注解的方式配置bean
在 classpath中扫描组件
•组件扫描(component scanning): Spring 能够从 classpath下自动扫描, 侦测和实例化具有特定注解的组件.
•特定组件包括:
–@Component: 基本注解, 标识了一个受 Spring 管理的组件
–@Respository: 标识持久层组件
–@Service: 标识服务层(业务层)组件
–@Controller: 标识表现层组件
•对于扫描到的组件, Spring 有默认的命名策略: 使用非限定类名, 第一个字母小写. 也可以在注解中通过 value 属性值标识组件的名称
•当在组件类上使用了特定的注解之后, 还需要在 Spring 的配置文件中声明 :
–base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包里及其子包中的所有类.
–当需要扫描多个包时, 可以使用逗号分隔.
–如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特定的类,示例:
– 子节点表示要包含的目标类
– 子节点表示要排除在外的目标类
– 下可以拥有若干个 和 子节点
• 和 子节点支持多种类型的过滤表达式:
组件装配
• 元素还会自动注册 AutowiredAnnotationBeanPostProcessor实例, 该实例可以自动装配具有 @Autowired和 @Resource 、@Inject注解的属性.
使用 @Autowired自动装配 Bean
•@Autowired注解自动装配具有兼容类型的单个 Bean属性
–构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired注解
–默认情况下, 所有使用 @Authwired注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某 一属性允许不被设置, 可以设置 @Authwired注解的 required 属性为 false
–默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter已指定注入 Bean 的名称
–@Authwired注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.
–@Authwired注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.
–@Authwired注解用在 java.util.Map上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值
使用 @Resource 或 @Inject 自动装配 Bean
•Spring 还支持 @Resource 和 @Inject 注解,这两个注解和 @Autowired注解的功用类似
•@Resource 注解要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称
•@Inject 和 @Autowired注解一样也是按类型匹配注入的 Bean, 但没有 reqired属性
•建议使用 @Autowired注解
泛型依赖注入
•Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用
整合多个配置文件
•Spring 允许通过 将多个配置文件引入到一个文件中,进行配置文件的集成。这样在启动 Spring 容器时,仅需要指定这个合并好的配置文件就可以。
•import 元素的 resource 属性支持 Spring 的标准的路径资源