Spring核心容器
一. IOC和DI基础
IOC-Inversion of Control,译为控制反转,是一种遵循依赖倒置原则的代码设计思想。
所谓依赖倒置,就是把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。
而控制反转就是把传统程序中需要实现对象的创建、代码的依赖,反转给一个专门的"第三方"即容器来实现,即将创建和查找依赖对象的控制权交给容器,由容器将对象进行组合注入,实现对象与对象的松耦合,便于功能的复用,使程序的体系结构更灵活。
DI-Dependency Injection,译为依赖注入,实际上是对Ioc的另一种称呼。2004年,大师级人物Martin Fowler为“控制反转”取了一个更合适的名字“依赖注入”。给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
理解IOC和DI的关键:
控制反转,谁控制了谁?--IOC容器控制了对象;控制了什么?--控制了外部资源的获取(对象,文件等);哪些方面的控制被反转了?--获得依赖对象的过程被反转了。
依赖注入,谁依赖谁?--程序依赖IOC容器;为什么需要依赖?--程序需要IOC容器来提供外部资源;谁注入谁?--IOC容器向程序注入;注入了什么?--注入某个对象所需要的外部资源。
依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。
二. Spring容器与Bean
1. spring核心容器
spring的核心容器包括了:Beans、Core、Context、SpEL
① core和beans模块提供了整个框架最基础的部分,包括了IOC和DI。
② Context建立在Core和Beans模块提供的基础之上:他提供了框架式访问对象的方式
③ core、beans、context构成了Spring的骨架
④ SpEL:提供了一种强大的用于在运行时操作对象的表达式语言
org.springframework.beans.factory.BeanFactory是Spring容器的实际代表者,容器负责容纳此前所描述的bean,并对bean进行管理。
2. spring与bean的关系
Spring容器是Spring的核心,一切SpringBean都存储在Spring容器内。可以说bean是spring核心中的核心。Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息来创建Bean实例,并调用Bean实例的方法来完成“依赖注入”,可以把Spring容器理解成一个大型工厂,Bean就是该工厂的产品,工厂(Spirng容器)里能生产出来什么样的产品(Bean),完全取决于配置文件中的配置。而这个配置是由开发人员创建和维护的。
3. Bean的作用域
spring的作用域有singleton,prototype,request,session,globle session。
这里只讨论常用的singleton和prototype两种作用域
对于singleton作用域,每次请求同id的Bean都将获得相同的实例,spring容器负责跟踪Bean实例状态,负责维护Bean实例的生命周期。
对于prototype作用域,程序每次请求同id的Bean会获得新的实例,spring容器只负责创建,一旦创建成功就撒手不管,不再管理Bean实例的生命周期,也不会维护Bean实例的状态。
4. 容器中Bean的生命周期
Spring可以管理singleton作用域Bean的生命周期,Spring可以精确地知道singleton域bean何时被创建,何时初始化完成,以及容器何时准备销毁Bean实例。对于singleton作用域的Bean,管理Bean的生命周期行为主要有两个时机:注入依赖关系后,销毁实例之前;具体的管理方法如下:
Spring提供两种方式在Bean全部属性设置成功后执行特定行为
使用init-method 属性(代码污染小)
在类中编写一个方法,在属性中指定该方法在依赖关系设置完成后自动执行。
实现InitializingBean接口(耦合较高)
编写afterPropertiesSet()方法的具体实现
同理,若在Bean销毁之前,执行特定的方法,只需要使用 destroy-method属性或实现DisposableBean接口(实现destroy()方法)
对于prototype作用域的Bean,Spring容器只负责Bean的创建,当容器创建实例完成后,Bean将完全交给客户端代码管理,容器不再负责其生命周期,Spring容器本身也不知道自己创建了多少个实例,更无从知道这些实例什么时候才会被销毁。
5. Bean标签属性
① id属性:Bean的名称在IOC容器中必须是唯一的。
② class属性:指定Bean对应实现类的全类名,即包名加类名,必须有无参构造。
③ scope属性: 前面提到过的作用域属性,不指定默认为singleton,即单实例模式。
④ name属性:设置<bean>标签的别名,多个别名之间用逗号或空格分开。
⑤
parent属性:子类Bean定义它所引用它的父类Bean。子类Bean定义它所引用它的父类Bean。这时前面的class属性失效。子类Bean会继承父类Bean的所有属性,子类Bean也可以覆盖父类Bean的属性。注意:子类Bean和父类Bean是同一个Java类。
⑥ abstract属性:用来定义Bean是否为抽象Bean。它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类Bean主要是供子类Bean继承使用。
⑦ singleton属性:作用同③,默认为true
⑧ lazy-init属性:用来定义这个Bean是否实现懒初始化。如果为“true”,它将在BeanFactory启动时初始化所有的SingletonBean。反之,如果为“false”,它只在Bean请求时才开始创建SingletonBean。
⑨
autowire属性:它定义了Bean的自动装载方式。“no”:不使用自动装配功能。“byName”:通过Bean的属性名实现自动装配。“byType”:通过Bean的类型实现自动装配。“constructor”:类似于byType,但它是用于构造函数的参数的自动组装。“autodetect”:通过Bean类的反省机制(introspection)决定是使用“constructor”
⑩
dependency-check属性:它用来确保Bean组件通过JavaBean描述的所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。none:不进行依赖检查objects:只做对象间依赖的检查。simple:只做原始类型和String类型依赖的检查。all:对所有类型的依赖进行检查。它包括了前面的objects和simple。
⑪ depends-on属性:这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。
⑫ init-method属性:用来定义Bean的初始化方法,它会在Bean组装之后调用。它必须是一个无参数的方法。
⑬ destroy-method属性:用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于singletonBean。
⑭ factory-method属性:定义创建该Bean对象的工厂方法。它用于下面的“factory-bean”,表示这个Bean是通过工厂方法创建。此时,“class”属性失效。
⑮ factory-bean属性:定义创建该Bean对象的工厂类。如果使用了“factory-bean”则“class”属性失效。
三. spring中bean配置
bean配置有三种方法:
1、配置形式:
①基于xml文件 ②基于注解 ③基于java类
2、配置方式:
①通过全类名(反射)
②通过工厂方法(静态工厂方法、实例工厂方法)
③FactoryBean
3、依赖注入方式:
①属性注入 ②构造器注入
1. 基于xml 全类名 属性注入
<bean id="helloWorld" class="top.arioso.spring.beans.HelloWorld">
<property name="id">
<value>123456</value>
</property>
<property name="age" value="18"></property>
</bean>
property节点:为Bean属性赋值,name为属性名,value对应属性值,属性值要有setter方法
value属性可用子节点value替代
2. 基于xml 全类名 构造注入
<bean id="person" class="top.arioso.spring.beans.Person">
<constructor-arg type="java.lang.String">
<value>John</value>
</constructor-arg>
<constructor-arg value="America" index="3"><constructor>
<constructor-arg value="123456" type="int"><constructor>
<constructor-arg value="man" typ><constructor>
<property name="id">
<value>123456</value>
</property>
<property name="age" value="18"></property>
</bean>
要求必须有带参构造
根据value值匹配构造器,匹配不到就匹配下一个
可以用index属性指定带参构造器第几个参数
可以用type属性指定构造函数参数类型的匹配
value属性可用子节点value替代
3. 基于xml 通过工厂配置
(1) 静态工厂方法配置
<bean id="car" class="top.arioso.spring.factory.staticCarFactory" factory-method="getCar">
<constructor-arg value="BMW"></constructor-arg>
</bean>
class:工厂方法全类名
factory-method:要调用工厂方法名
若需要传递参数,使用constructor-arg传值
(2)实例工厂方法配置
<bean id="carFactory" class="top.arioso.spring.beans.carFactory"></bean>
<bean id="car" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="BMW"></constructor-arg>
</bean>
carFactory:工厂的实例
factory-bean: 指向工厂的实例
factory-method:要调用工厂方法名
若需要传递参数,使用constructor-arg传值
(3)通过FactoryBean配置
spring框架自身提供的,它需要实现FactoryBean接口,实现代码就必须写在getObject()方法中。
//java代码
public class SpringFactory implements FactoryBean<Calendar>{
public SpringFactory() {
System.out.println("我是一个spring工厂类");
}
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
public Class<?> getObjectType() {
return Calendar.class;
}
public boolean isSingleton() {
return false;
}
//配置文件
<bean id="cal" class="springfactory.SpringFactory"/>
该配置返回的实例是SpringFactory的getObject方法返回的实例。
4. spring配置Bean注入
① 配置字面值
可通过value属性或<value>标签属性注入,
若字面值包含特殊字符,使用<![CDATA[]]>包裹
spring将属性的空参数当空String,下面给email属性设置了空的String值("")
<bean id="nullBean" class="top.arioso.spring.NullBean">
<property name="email" value="" />
</bean>
如果要注入null值,可以使用<null />
<bean id="exampleBean" class="top.arioso.spring.NullBean">
<property name="email">
<null />
</property>
</bean>
② Bean之间的引用
<bean id="dog" class="top.arioso.hello.Dog"/>
<bean id="user" class="top.arioso.hello.User">
<property name="name" value="tony"/>
<property name="dog" ref="dog"></property>
</bean>
③ List属性(set同理)
<bean id="user" class="top.arioso.hello.User">
<property name="list">
<list>
<value>北京</value>
<value>上海</value>
<value>广州</value>
<ref bean="dog"/>
</list>
</property>
</bean>
④ map属性
<bean id="user" class="top.arioso.hello.User">
<property name="map">
<map>
<entry key="bj" value="北京"/>
<entry key="sh" value="上海"/>
</map>
</property>
</bean>
⑤ Properties属性
<bean id="user" class="top.arioso.hello.User">
<property name="dbParams">
<props>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
⑥ 独立的集合Bean
<util:list id="computerList">
<ref bean="computer"/>
<bean class="top.arioso.spring.collection.ComputerList">
<constructor-arg value="hp"></constructor-arg>
<constructor-arg value="dell"></constructor-arg>
<constructor-arg value="acer"></constructor-arg>
</bean>
</util:list>
<bean id="User" class="top.arioso.spring.User">
<property name="id" value="2018310"></property>
<property name="name" value="Tom"></property>
<property name="computers" value="computerList"></property>
</bean>
⑦ 使用p命名空间
<bean id="computer" class="top.arioso.spring.Computer">
<p:brand="dell" p-cpu="i7-7700" p-price="7900">
</bean>
首先需要导入命名空间
5. 基于注解配置Bean
① 注解依赖aop包,要导入aop包。
② Spring 能够从 classpath 下自动扫描(需要配置 context:component-scan )具有特定注解的组件。
特定的组件包括:
@Component:基本注解,标识一个受Spring管理的组件。
@Repository:标识持久层组件。
@Service:标识业务层组件。
@Controller:标识控制层组件。
Spring 对扫描到的组件有默认的命名规则:使用非限定类名,第一个字母小写 (也可以使用value属性指定组件的名称)。
③ 配置<context:component-scan>
<context:component-scan
base-package="top.arioso.spring.annotation"
resource-pattern="service/*.class">
</context:component-scan>
base-package 属性指定Spring扫描的包,Spring将扫描该包及其所有子包,如有多个包,使用逗号隔开。
可以使用 resource-pattern 来过滤特定的类。
<context:exclude-filter>:指定扫描排除哪些类
需设置use-default-filter为false
//只扫描有Service注解的类
<context:component-scan resource-pattern="controller/*.class"
base-package="top.arioso.spring.annotation"
use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service">
</context:component-scan>
//排除扫描实现了UserRepository的类
<context:component-scan resource-pattern="controller/*.class"
base-package="top.arioso.spring.annotation"
use-default-filters="false">
<context:exclude-filter type="assignable" expression="top.arioso.repository.UserRepository">
</context:component-scan>
<context:include-filter>:指定扫描包含哪些类
//排除扫描有Service注解的类
<context:component-scan resource-pattern="controller/*.class"
base-package="top.arioso.spring.annotation"
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service">
</context:component-scan>
//只扫描实现了UserRepository的类
<context:component-scan resource-pattern="controller/*.class"
base-package="top.arioso.spring.annotation"
<context:include-filter type="assignable" expression="top.arioso.repository.UserRepository">
</context:component-scan>
6. 使用注解自动装配Bean
①Autowired自动注入
@Autowired可以在字段、setter方法、构造器上面使用
默认情况下,当使用 @Autowired 注解的属性,如果在IOC容器中找不到匹配的Bean来装配属性时,会抛出异常。可以使用required=false 设置某一属性不被设置(即IOC容器不配置匹配的Bean)。
使用@Autowired注解的属性,当 IOC 容器中存在多个类型匹配的 Bean 时,默认情况下会匹配与属性相同名称的 Bean,若匹配不到则抛出异常。也可以使用 @Qualifier(“Bean名称”) 注解指定注入的Bean。
@Autowired也可以用在数组上,Spring会将所有匹配的Bean自动装配进数组。
@Autowired也可以用在集合上,Spring会判断该集合的类型,然后自动装配所有类型兼容的Bean。
@Autowired也可以用在Map上,若key为String类型,Spring将Bean的名称作为key,Bean本身作为值自动装配所有类型兼容的的Bean。
也可以使用 @Resource 或 @Inject 自动装配Bean,功能与 @Autowired 类似,一般使用@Resource和@Autowired,不推荐@Inject。
②Resource自动装配
@Resource与@Autowired作用类似,只不过@Autowired按ByType自动注入,Resource按ByName自动注入;
@Resource装配顺序
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
③@Qualifier注解
Qualifier译为合格者,一般与Autowired配合使用,当有多个类实现一个借口,spring不知道绑定哪个实现类,可以用Qualifier(Bean名称或注解名) 配合@Autowired使用
④@Autowired与@Resource的区别
@Resource是所属于J2EE的,而@Autowired是所属于Spring的;
@Autowired是默认按类型(ByType)装配的,默认要依赖的对象必须存在,否则抛出异常,如果允许为null值,可以设置@Autowired(required=false),@Resource是默认按名称(ByName)装配的,如果不指定name,则把字段名当做名称寻找,找不到与该名称匹配的Bean时才会按类型寻找。但是一旦指定name,就会按名称寻找,找不到抛出异常。