Spring框架系列 1 -- IOC和DI

Spring框架系列 1 -- IOC和DI


一、spring bean生命周期

spring的生命周期为:实例化前奏-->实例化-->实例化后期-->初始化前期-->初始化-->初始化后期-->bean的具体调用-->销毁前-->销毁。


1-2:创建实例;

   现在假设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>
  1. 配置文件中的lazy-init="default"或者="false",意思是,该bean在spring容器启动时就实例化。而lazy-init="true"表示该bean在spring容器启动时不会实例化,而在需要这个bean时才实例化。
  2. name与value分别对应什么,一目了然。
  3. name书写有一定的规范,比如你写个cARBrand可能会出问题。前两个字母,要么全部大写,要么全部小写。
  4. value中不能掺杂xml的特殊符号,如:& < > “ ‘ 如果必须要写这些字符,要用<![CDATA[XXX]]>来转义。
  5. value里面如果要设置null值,不能什么都不写,要变为:<property name="host"><null/></property>
  6. 假如bean类是Icp,Icp类有一个属性是car,那么你想直接在Icp的配置文件中定义Car的brand属性,就酱紫:<property name="car.brand" value="xxx"/>
  7. 如果是集合类型,配置里面要这样写:
<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 TypeExamples ExpressionDescriptioninclude-filter为例
annotationorg.example.SomeAnnotation符合SomeAnnoation的target class

<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>

表示扫描base-package下的类上加了Aspect注解的类,并注册到spring的bean容器

assignableorg.example.SomeClass指定class或interface的全名

<context:include-filter type="assignable" expression="com.test.scan.StuService"/>

指定扫描StuService类作为bean

aspectjorg.example..*Service+AspectJ語法 
regexorg\.example\.Default.*Regelar Expression 
customorg.example.MyTypeFilterSpring3新增自訂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,都放到注册表里,用的时候取出来)。

 

再具体点:

  1. ResourceLoader从系统中加载XML配置信息,并由Resource来表示。
  2. BeanDefinitionReader从Resource中读取配置信息,把配置文件中的<bean>解析成一个BeanDefinition对象,然后把BeanDefinition对象放到BeanDefinitionRegistry注册表中。
  3. 容器从BeanDefinitionRegistry注册表中扫描出Bean工厂后处理器的Bean(该Bean实现了BeanFactoryPostProcessor),用这个工厂后处理器来加工BeanDefinitionRegistry注册表中的所有BeanDefinition对象。具体做了两件事:
    1.   对使用到<bean>元素的占位符的Bean进行解析,把占位符转换成具体值,从而把半成品的BeanDefinition对象转为成品的对象。
    2.   扫描BeanDefinitionRegistry注册表中的所有BeanDefinition对象,通过java反射机制找出所有属性编辑器的Bean(实现了PropertyEditor的Bean),然后把它放到属性编辑器注册表中(PropertyEditorRegistry)。
  4. 容器从BeanDefinitionRegistry中取出加工过的BeanDefinition,并调用InstantiationStrategy着手bean的实例化工作。
  5. 在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper结合BeanDefinition以及属性编辑器完成Bean属性的设置工作。
  6. 利用容器中注册的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解析类图:


属性编辑器(太多了只列出了部分),注册器:



 环境相关类图:














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值