Spring框架系列 1 -- IOC和DI
一、spring bean生命周期
spring的生命周期为:实例化前奏-->实例化-->实例化后期-->初始化前期-->初始化-->初始化后期-->bean的具体调用-->销毁前-->销毁。
现在假设spring就是个容器,而配置文件中配置的bean属性才是我们真正需要的东西。
创建实例就是说,我把配置文件中的bean信息取出来化作一个真正的bean(实例化)并放到容器中。
InstantiationAwareBeanPostProcessor是实例化前后做的事情。
3-4:注入依赖关系;
第3步是创建实例之后对实例作了一些处理,第4步是把xml中配置的bean属性值赋予给容器中的实例化之后的bean。
5:bean初始化之前的处理;
BeanPostProcessor是初始化前后做的事情。
应用开发者需要把容器中实例化的bean拿出来用,这个拿出来的过程就是初始化(注意实例化与初始化的区别,instantiation (实例化)和initialization(初始化)),第五步就是在初始化之前,对已经实例化的bean再作一定的处理。
6,7:初始化。
如果bean实现了InitializingBean,那么将调用InitializingBean的afterPropertiesSet()方法做一些初始化处理。
如果没有实现InitializingBean,而是在配置文件中定义了init-method属性值,那么系统会找到init-method对应的方法并执行之,程序猿哥哥一般在这个方法里写一些初始化操作;
8:bean初始化之后的处理。
初始化之后在这个方法中再对bean进行修饰装点。
9,10:交给应用开发人员处理;
如果在<bean>中指定Bean的作用范围是scopt="prototype",那么系统将bean返回给调用者,spring就不管了(如果两个实例调用的话,每一次调用都要重新初始化,一个实例的修改不会影响另一个实例的值。如果指定Bean的作用范围是scope="singleton",则把bean放到缓冲池中,并将bean的引用返回给调用者。这个时候,如果两个实例调用的话,因为它们用的是同一个引用,任何一方的修改都会影响到另一方。)
11.bean用完之后;
对于scope="singleton"的bean,使用完之后spring容器会做一些处理,比如编写释放资源、记录日志等操作。
12.销毁;
调用配置文件中的销毁方法销毁实例。
二、Spring基于Schema的Xml配置方案
XML Schema : 用来描述 XML文档的结构,也被简称为XSD(XML Schema Definition),是一些规则的集合。(方式:通过定义schema文件 如 spring-tx-3.0.xsd)
xmlns : 命名空间是W3C推荐标准提供的一种统一命名XML文档中的元素和属性的机制
.xsd文件 : 用XML w3c 标准命名空间中规定的元素和属性编写的以targetNamespace作为{目标命名空间}的XML文件,能够约束引入此{目标命名空间}定义的元素和属性的XML文件。
.targetNamespace : 目标命名空间,它的主要作用是指明Schema定义的元素的命名空间。
<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns 声明xml文件默认的命名空间,表示未使用其他命名空间的所有标签的默认命名空间 -->
<!-- xmlns:*** 声明XML Schema实例,声明后就可以使用schemaLocation属性。 -->
<!-- xsi:schemaLocation 指定Schema的位置这个属性必须结合命名空间使用。这个属性有两个值,第一个值表示需要使用的命名空间。第二个值表示供命名空间使用的XML
schema的位置。上面配置的命名空间指定xsd规范文件,这样你在进行下面具体配置的时候就会根据这些xsd规范文件给出相应的提示,比如说每个标签是怎么写的,都有些什么属性是都可以智能提示的,在启动服务的时候也会根据xsd规范对配置进行校验。 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.2.xsd
">
<!-- 引入数据源properties配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/druid.properties</value>
<!--要是有多个配置文件,只需在这里继续添加即可 -->
</list>
</property>
</bean>
<!-- 默认的注解映射的支持 -->
<context:annotation-config />
<!-- 激活组件扫描功能,扫描aop的相关组件组件 -->
<!-- <context:component-scan base-package="com.lh.datasource" /> -->
<!-- 打开aop注解启用aop -->
<!--启动对@AspectJ注解的支持 , proxy-target-class设置为true,表示通知spring使用cglib而不是jdk的来生成代理方法,
这样AOP可以拦截到Controller -->
<!-- <aop:aspectj-autoproxy proxy-target-class="true"/> -->
<!-- 配置数据源 -->
<!-- druid数据源 -->
<import resource="classpath:config/druid.xml" />
<!-- jdbc数据源 -->
<import resource="classpath:config/jdbc.xml" />
<!-- 多数据源 -->
<bean id="dynamicDataSource" class="com.lh.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 指定lookupKey和与之对应的数据源 -->
<!-- druid数据源1 -->
<entry key="druidDataSource" value-ref="druidDataSource"></entry>
<!-- druid数据源2 -->
<!-- <entry key="druidDataSource2" value-ref="druidDataSource2"></entry> -->
<!-- jdbc数据源1 -->
<entry key="dataSource" value-ref="dataSource"></entry>
</map>
</property>
<!-- 这里可以指定默认的数据源 -->
<property name="defaultTargetDataSource" ref="druidDataSource" />
</bean>
<!-- 配置自定义AOP切面 用来切换数据源 -->
<bean id="dataSourceAspect" class="com.lh.datasource.DataSourceAspect" />
<aop:config>
<aop:aspect ref="dataSourceAspect">
<!-- 拦截所有controller方法 -->
<aop:pointcut id="dataSourcePointcut" expression="execution(* com.lh.controller.*.*(..))" />
<aop:before method="intercept" pointcut-ref="dataSourcePointcut" />
<!-- <aop:after method="doAfter" pointcut-ref="pointUserMgr"/>
<aop:around method="doAround" pointcut-ref="pointUserMgr"/>
<aop:after-returning method="doReturn" pointcut-ref="pointUserMgr"/>
<aop:after-throwing method="doThrowing" throwing="ex" pointcut-ref="pointUserMgr"/> -->
<!-- <aop:pointcut id="daoOne" expression="execution(* com.test.dao.test1.*.*(..))" />
<aop:pointcut id="daoTwo" expression="execution(* com.test.dao.test2.*.*(..))" />
<aop:before pointcut-ref="daoOne" method="setdataSourceOne" />
<aop:before pointcut-ref="daoTwo" method="setdataSourceTwo" /> -->
</aop:aspect>
</aop:config>
<!-- 自动扫描注解的bean -->
<context:component-scan base-package="com.lh.service" />
<!-- <context:component-scan base-package="com.lh.dao" /> -->
<!--扫描注解 @Async -->
<context:component-scan base-package="com.lh.excel" />
<!-- 支持异步方法执行 -->
</beans>
xmlns : 是spring的内置xsd xsi : 是调用xsi的schemaLocation去寻找具体的xsd位置
Spring除了基于XML的配置,还有基于Bean,基于注解的配置。但是基于XML的配置功能最强
三、IoC(控制反转)和DI(依赖注入)
IOC, spring的核心,贯穿Spring始终。直观的来说,就是由spring来负责控制对象的生命周期和对象间的关系,将对象之间的关系抽象出来,通过spring容器控制对象生成时机,减少对象之间的耦合度。
所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们可配置的文件。
SpringIOC 的主要依赖源码是 spring-beans 和 spring-context两个包。
IOC容器指的是实现对依赖对象的创建(无参构造器)、管理(参数注入)、销毁(关闭BeanFactory).
spring的依赖注入有属性注入、构造函数注入、工厂方法注入等多种方式。Spring要把xml配置中bean的属性实例化为具体的bean,"依赖注入"是关卡。所谓的"依赖注入",就是把应用程序对bean的属性依赖都注入到spring容器中。
首先是属性注入:
<bean id="icp" class="com.lh.domain.Icp" lazy-init="default">
<property name="host">
<value>host.com</value>
</property>
</bean>
- 配置文件中的lazy-init="default"或者="false",意思是,该bean在spring容器启动时就实例化。而lazy-init="true"表示该bean在spring容器启动时不会实例化,而在需要这个bean时才实例化。
- name与value分别对应什么,一目了然。
- name书写有一定的规范,比如你写个cARBrand可能会出问题。前两个字母,要么全部大写,要么全部小写。
- value中不能掺杂xml的特殊符号,如:& < > “ ‘ 如果必须要写这些字符,要用<![CDATA[XXX]]>来转义。
- value里面如果要设置null值,不能什么都不写,要变为:<property name="host"><null/></property>
- 假如bean类是Icp,Icp类有一个属性是car,那么你想直接在Icp的配置文件中定义Car的brand属性,就酱紫:<property name="car.brand" value="xxx"/>
- 如果是集合类型,配置里面要这样写:
<property name="host">
<set>
<value>aili.com</value>
<value>apple.com</value>
<value>yun.qq.com</value>
</set>
</property>
<bean name="Car">
<list>
<value>BMW</value>
<value>BYD</value>
</list>
</bean>
7.HashMap
<property name="weekends">
<map>
<entry >
<key>
<value>Saturday</value>
</key>
<value>约妹子</value>
</entry>
<entry>
<key>
<value>Sunday</value>
</key>
<value>发呆</value>
</entry>
</map>
</property>
8.Properties 它是一种特殊的Map,键值都是String类型
<property name="mails">
<props>
<prop key="jobMail">john-office@baobaotao.com</prop>
<prop key="lifeMail">john-life@baobaotao.com</prop>
</props>
</property>
构造函数注入: 构造函数的注入可以让属性在实例化的过程中就有了值(其实就相当于java在bean的构造函数中给属性赋值,只不过现在是在xml配置中实现)
<bean id="icp" class="com.lh.domain.Icp" lazy-init="default">
<constructor-arg type="java.lang.String">
<value>niaofuli.com</value>
</constructor-arg>
<constructor-arg type="int">
<value>20000</value>
</constructor-arg>
</bean>
工厂方法的注入:
工厂类负责创建一个或者多个bean实例,调用工厂方法即可获取该实例。一旦在XML中注册了某工厂,那么调用工厂的过程中已经把该Bean实例化了。
code
<?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-3.0.xsd">
<bean id="car1" factory-bean="carFactory" factory-method="createCar"/>
<bean id="carFactory" class="com.test.CarFactory" />
</beans>
package com.test;
public class CarFactory {
public Car createCar(){
Car car = new Car();
car.setBrand("红旗");
return car;
}
}
code 第6行的factory-bean指向第7行的id,第6行的factory-method就是Factory要调用的方法。第7行是Factory的Bean配置(也就是说,CarFactory这个java写的工厂类本身也是一个bean ).调用createCar就可以获取Car实例。
如果方法是静态的:
<?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-3.0.xsd">
<bean id="car2" class="com.test.CarFactory" factory-method="createCar"></bean>
</beans>
spring使用p命名空间进一步作了简化。
修改前
<bean id="car" class="com.baobaotao.attr.Car">
<property name="brand" value="吉利CT5" />
<property name="maxSpeed" value="100" />
<property name="price" value="1000.00" />
</bean>
p命名
<bean id="car" class="com.baobaotao.ditype.Car"
p:brand="红旗CA72"
p:maxSpeed="200"
p:price="20000.00"/>
基于注解配置
基于XML的bean属性配置:bean的定义信息与bean的实现类是分离的。// 基于注解的配置:bean的定义信息是通过在bean实现类上标注注解实现。
1、Component : spring看到这个属性标志,会自动将FilterDao变成容器管理类,等同于在XML中这样配置:
<bean id="car" class="com.lh.json.FilterDao"></bean>
package com.lh.json;
import org.springframework.stereotype.Component;
/**
* 页面筛选字段内容
* @author JOMNN
*
*/
@Component
public class FilterDao {
private String fvname;
private int fv;
public String getFvname() {
return fvname;
}
public void setFvname(String fvname) {
this.fvname = fvname;
}
public int getFv() {
return fv;
}
public void setFv(int fv) {
this.fv = fv;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return this.fvname+"-"+this.fv;
}
}
自动扫描注解的bean
默认的过滤器会去解析base-package下的含有@Component注解的类作为bean,其中@Repository, @Service, and @Controller都是@Component的子注解,故,默认的过滤器会将它们全部解析成bean
1、@Component 一般的bean类上面配置。
2、@Controller 对应表现层的Bean,也就是action。
3、@ Service 对应业务层bean。
4、@ Repository 对应持久层Bean。
5、@Autowired 成员变量或方法入参处标注,按类型匹配自动注入。
6、@Qualifier 按名称匹配方式注入。
7、@PostConstruct指定初始化方法。(相当于XML配置中的init-method)
8、@PreDestroy指定销毁方法。(相当于XML配置中的destroy-method)
9、@Scope 指定bean是prototype还是singleton。
使用context的规则:component-scan. 扫描这个包中的所有类,并从注解信息中获取bean的基本信息(没加注解的不扫描也不实例化)。
<!-- 自动扫描注解的bean -->
<context:component-scan base-package="com.lh.service" />
context:component-scan标签下面可以再加子标签过滤
<!-- 使用过滤器的顺序是,exclude-filter优于include-filter。 -->
<context:component-scan base-package="com.baobaotao">
<!-- include-filter表示:包含哪些。type是类型,expression是表达式过滤。 -->
<!-- com.baobaotao.anno包下面所有以Plugin结尾的class。 -->
<context:include-filter type="aspectj" expression="com.baobaotao.anno.*Plugin+" />
<!-- exclude-filter表示:除去哪些。type是类型,expression是表达式过滤。 -->
<!-- 除去所有以Dao或者Service结束的类。 -->
<context:exclude-filter type="regex" expression="cn\.outofmemory\.spring\.[^.]+(Dao|Service)" />
</context:component-scan>
附:过滤规则设置
context:component-scan节点允许有两个子节点<context:include-filter>和<context:exclude-filter>。filter标签的type和表达式说明如下:
Filter Type | Examples Expression | Description | include-filter为例 |
annotation | org.example.SomeAnnotation | 符合SomeAnnoation的target class | <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> 表示扫描base-package下的类上加了Aspect注解的类,并注册到spring的bean容器 |
assignable | org.example.SomeClass | 指定class或interface的全名 | <context:include-filter type="assignable" expression="com.test.scan.StuService"/> 指定扫描StuService类作为bean |
aspectj | org.example..*Service+ | AspectJ語法 | |
regex | org\.example\.Default.* | Regelar Expression | |
custom | org.example.MyTypeFilter | Spring3新增自訂Type,實作org.springframework.core.type.TypeFilter |
package com.lh.controller;
import java.io.File;
import java.util.HashMap;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.annotations.Param;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.lh.def.SqlOpDef;
import com.lh.excel.util.ExcelUtil;
import com.lh.service.RequestAsyncProcessService;
/**
* 数据库添加页面
* @author JOMNN
*
*/
@Controller
@RequestMapping(value="inster")
public class InsterController implements ApplicationEventPublisherAware{
Log log=LogFactory.getLog(InsterController.class);
/**
* 事件分发
*/
@Resource
private ApplicationEventPublisher publisher;
/**
* 请求队列
*/
@Resource
private RequestAsyncProcessService requestAsyncProcessService;
/**
* ICP库
* @return
*/
@RequestMapping(value="icp")
public ModelAndView icpInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: Icp备案库数据");
map.put("page", "1");
return new ModelAndView("html/inster",map);
}
/**
* 主域分类库
* @return
*/
@RequestMapping(value="hostType")
public ModelAndView hostTypeInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: 主域分类库数据");
map.put("page", 2);
return new ModelAndView("html/inster",map);
}
/**
* 主域白名单库
* @return
*/
@RequestMapping(value="hostWhite")
public ModelAndView hostWhiteInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: 主域白名单库数据");
map.put("page", 3);
return new ModelAndView("html/inster",map);
}
/**
* 域名状态库
* @return
*/
@RequestMapping(value="domainState")
public ModelAndView domainStateInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: 域名状态库数据");
map.put("page", 4);
return new ModelAndView("html/inster",map);
}
/**
* 域名页面优化库
* @return
*/
@RequestMapping(value="domainOptimize")
public ModelAndView domainOptimizeInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: 域名页面优化库数据");
map.put("page", 5);
return new ModelAndView("html/inster",map);
}
/**
* 主域白名单库
* @return
*/
@RequestMapping(value="domainInfo")
public ModelAndView domainInfoInsterHtml(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("title_name", "添加: 域名信息库数据");
map.put("page", 6);
return new ModelAndView("html/inster",map);
}
@RequestMapping(value="single",method=RequestMethod.POST)
public String singleHtml(@Param("page") int page){
if (page == 1) {
return "html/instericp";
}
else if (page == 2) {
return "html/insterhostType";
}
else if (page == 3) {
return "html/insterhostWhite";
}
else if (page == 4) {
return "html/insterdomainState";
}
else if (page == 5) {
return "html/insterdomainOptimize";
}
else if (page == 6) {
return "html/insterdomainInfo";
}
else {
return "index/login";
}
}
@RequestMapping(value="inster.do")
@ResponseBody
public String insert(HttpServletRequest request,@Param("file") MultipartFile file){
//服务器上使用
String rootPath =request.getServletContext().getRealPath(File.separator+"WEB-INF");//target的目录
//使用sessionid + UUID 生成文件号
String fileId = request.getSession().getId() + UUID.randomUUID().toString().replaceAll("-", "")+SqlOpDef.getInster();
return ExcelUtil.analysisExcel(file, rootPath,publisher,requestAsyncProcessService,fileId,SqlOpDef.getInster());
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@PostConstruct
public void init(){
log.info("本应用程序正式拉开帷幕。。。");
}
@PreDestroy
public void destroy(){
log.info("本应用程序正式落幕,吓吓侬。。。");
}
}
spring看到@Controller会如同看到@Component一样,转换成spring容器管理的bean。
spring看到@Autowired(required=false) / @Resource会自动注入(实例化)下面的requestAsyncProcessService。(直接用就行,就不用你来new一个对象啦)。
spring看到@PostConstruct就会在实例化本Bean后接着执行这个方法;
spring看到@PreDestroy就会在销毁之前执行下面的方法;
spring容器的三座大山:XML配置文件(或者注解配置、bean配置等),spring容器,bean类。不管是基于XML还是基于注解,无非就是为了把bean信息注入到spring容器中。
适用场景
Spring IOC 的两种实现方式(接口)?
完整的bean的作业流程:整体是下面这样的:
简单点:
读取XML,转化并加工成BeanDefinition,实例化BeanDefinition。
具体点:
ResourceLoader加载XML配置信息后,由BeanDefinitionReader读取配置信息文件,把每个<bean>解析成BeanDefinition对象保存在注册表中。容器首先扫描注册表取出工厂后处理器,对注册表中的BeanDefinition进行加工处理。Spring容器接着从注册表中取出加工过的BeanDefinition开始着手bean实例化的事情。实例化时,首先由BeanWapper对bean进行封装,配置属性。最后利用注册表中的Bean后处理器装饰打扮,装配出一个准备就绪的Bean来。(注册表就类似于一个Map<K,V>,把所有的bean,不管是业务bean,还是spring自己的bean,都放到注册表里,用的时候取出来)。
再具体点:
- ResourceLoader从系统中加载XML配置信息,并由Resource来表示。
- BeanDefinitionReader从Resource中读取配置信息,把配置文件中的<bean>解析成一个BeanDefinition对象,然后把BeanDefinition对象放到BeanDefinitionRegistry注册表中。
- 容器从BeanDefinitionRegistry注册表中扫描出Bean工厂后处理器的Bean(该Bean实现了BeanFactoryPostProcessor),用这个工厂后处理器来加工BeanDefinitionRegistry注册表中的所有BeanDefinition对象。具体做了两件事:
- 对使用到<bean>元素的占位符的Bean进行解析,把占位符转换成具体值,从而把半成品的BeanDefinition对象转为成品的对象。
- 扫描BeanDefinitionRegistry注册表中的所有BeanDefinition对象,通过java反射机制找出所有属性编辑器的Bean(实现了PropertyEditor的Bean),然后把它放到属性编辑器注册表中(PropertyEditorRegistry)。
- 容器从BeanDefinitionRegistry中取出加工过的BeanDefinition,并调用InstantiationStrategy着手bean的实例化工作。
- 在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper结合BeanDefinition以及属性编辑器完成Bean属性的设置工作。
- 利用容器中注册的Bean后处理器(该Bean实现了BeanPostProcessor)对第五步生成的Bean进行后续加工。
从实例化的过程中可以看出,BeanDefinition起到中流砥柱的作用。因为BeanDefinition是配置文件<bean>元素标签在容器中的内部表示。比如,<bean>标签在XML中有class,scope,lazy-init等属性,那么在BeanDefinition中则有相应的beanClass,scope,lazyInit属性等。
BeanDefinition接口的继承结构如图:
顶级的BeanDefinition其实是个接口,下面的AbstractBeanDefinition实现了这个接口,而最下面的ChildBeanDefinition和RootBeanDefinition分别继承了AbstractBeanDefinition。
半成品的BeanDefinition对象:<bean id="simpleBean" class="com.spring.ch04.SimplePostProcessor">
<property name="connectionString" value="${simpleBean.connectionString}"/>
<property name="password" value="${simpleBean.password}"/>
<property name="username" value="${simpleBean.username}"/>
</bean>
1. BeanFactory接口
org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能(工厂模式).(如图)
BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。
最终的默认实现类是 DefaultListableBeanFactory : ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为.
BeanFactory实现:
1: DefaultListableBeanFactory(是Spring注册及加载Bean的默认实现)
2: XmlBeanDefinationReader(是XML文件的读取和注册也就是:资源文件的读取、解析和注册)
步骤1、BeanFactory(继承的是DefaultListableBeanFactory),提供基本的IoC容器功能,可以从classpath或文件系统等获取资源;
步骤2、利用ClassPathResource
Resource cr = new ClassPathResource("applicationContext.xml");
Resource resource = new FileSystemResource(“beans.xml”);
BeanFactory beanFactory = new XmlBeanFactory(resource);
2、 ApplicationContext接口(扩展了BeanFactory)
而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。
ApplicationContext实现:
1、 ClassPathXmlApplicationContext(继承了抽象类):,从classpath获取配置文件;
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");
2、FileSystemXmlApplicationContext:从文件系统获取配置文件。
BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");
3.利用XmlWebApplicationContext读取
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
概述:ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。
从ApplicationContext接口的实现,我们看出其特点(比起BeanFactory):
1. 支持信息源,可以实现国际化。(实现MessageSource接口)
2. 访问资源。(实现ResourcePatternResolver接口)
3. 支持应用事件。(实现ApplicationEventPublisher接口)
4. 提供附加服务(更面向框架的使用风格)
1、准备配置文件:在项目中使用Spring(初始化/引入配置文件)
在配置文件中声明Bean定义也就是为Bean配置元数据。
方式1. 直接加载ApplicationContext
ApplicationContext ctx = new ClasspathXmlApplicationContext("applicationContext.xml");
方式2. 使用ContextLoaderListener
从ServletContext取得web.xml中初始化的ApplicationContext
在web.xml中配置listener
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath: ApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
解释:
org.springframework.web.context.ContextLoaderListener
实现了 javax.servlet.ServletContextListener接口。ServletContextListener接口能够监听ServletContext对象的生命周期,因为每个web应用仅有一个ServletContext对象,故实际上该接口监听的是整个web应用。
方式3.使用 AnnotationConfigApplicationContext 注册配置类
1、使用 AnnotationConfigApplicationContext 注册 AppContext 类
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppContext.class);
Course course = ctx.getBean(Course.class);
course.getName();
}
正如以上代码所示,AppContext 配置类的注册方式是将其传递给 AnnotationConfigApplicationContext 构造函数。此外,您还可以使用所述上下文类的 register 方法来注册配置类。以下代码展示了另外一种方法。
2、注册 AppContext 类:另外一种方法
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppContext.class)
}
注册配置类将自动注册 @Bean 注释的方法名称,因而其对应的 bean 就是 Course、Module 和 Assignment。随后您可以使用 getBean 方法来获取相关的 bean,并调用其业务方法。如您所见,编写 Java 的配置类并将其注册到 Spring 上下文非常简单。下一节将讨论如何将基于 Java 的配置与 Web 应用程序配合使用。
资源Resource:
Resource是Sping中用于封装I/O操作的接口。在创建spring容器时,通常要访问XML配置文件(步骤1),除此之外还可以通过访问文件类型、二进制流等方式访问资源,还有当需要网络上的资源时可以通过访问URL,Spring把这些文件统称为Resource,Resource的体系结构如下:
常用的resource资源类型如下:
FileSystemResource:以文件的绝对路径方式进行访问资源,效果类似于Java中的File;
ClassPathResourcee:以类路径的方式访问资源,效果类似于this.getClass().getResource("/").getPath();
ServletContextResource:web应用根目录的方式访问资源,效果类似于request.getServletContext().getRealPath("");
UrlResource:访问网络资源的实现类。例如file: http: ftp:等前缀的资源对象;
ByteArrayResource: 访问字节数组资源的实现类。
资源加载器ResourceLoader:
spring Bean定义解析器:
实体解析器,dtd, schema解析类图:
属性编辑器(太多了只列出了部分),注册器:
环境相关类图: