第一部分Ioc概述

Ioc的全名即为依赖注入也为控制反转,依赖注入的类型分为构造函数注入、属性注入。

构造函数注入

package com.baobaotao.domain;

public class Org {

	private String id;
	private String name;
	public Org(String id,String name) {
		this.id = id;
		this.name = name;
	}
}

属性注入

package com.baobaotao.domain;

public class Org {

	private String id;
	private String name;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

一、资源访问利器

  Resource

  Resource作为Spring的资源加载器,拥有对应不同资源类型的实现类。Spring框架使用Resource加载各种资源,其结构如下:

 

    ByteArrayResource:当需要从字节数组加载内容时,ByteArrayResource 是一个不错的选择。

    ClassPathResource:类路径下的资源,资源以相对于类路径的方式表示,如:new ClassPathResource("com/baobaotao/beanfactory/bean.xml")。

    FileSystemResource:文件系统资源,资源以文件系统路径的方式表示,如:new FileSystemResource("c:\\beans.xml")。

    InputStreamResource:以输入流返回表示的资源。

    ServletContextResource:为访问Web容器上下文中的资源而设计的类,负责从Web应用根目录中加载资源,它支持以流和Url的方式访问,在WAR解包的情况下,也可以通过File的方式访问,该类还可以直接从JAR包中访问资源。如Resource res3 = new ServletContextResource(application, "/WEB-INF/classes/conf/file1.txt"); 

    UrlResource:Url封装了java.net.URL,它使用户能够访问任何可以通过URL表示的资源,如文件系统的资源、HTTP资源、FTP资源等。

  ResourceLoader

    ResourceLoader 接口仅有一个getResource(String  location)的方法,可以根据一个资源地址加载文件资源,不过,资源地址仅支持带资源类型前缀的表达式,不支持Ant风格的资源路径表达式。

    ResourcePatternResolver 扩展了ResourceLoader 接口,定义了一个新的接口方法:getResources(String locationPattern),该方法支持带资源类型前缀及Ant风格的资源路径的表达式。

    PathMatchingResourceResolver 是Spring提供了标准实现类。

  资源加载地址表达式

地址前缀 示例 对应资源类型
classpath: classpath:com/baobaotao/beanfactory/bean.xml 从类路径中加载资源,classpath:和classpath:等价,都是相对与类的跟路径。资源文件可以在标准的文件系统中,也可以在jar或者zip的类包中
file: file:/conf/com/baobaotao/beanfactory/bean.xml 使用URLResource从文件系统目录中装载资源,可采用绝对或相对路径。
http:// http://www.baobaotao/resource/bean.xml 使用UrlResource从web服务器中装载资源
ftp: ftp://www.baobaotao.com/resource/bean.xml 使用UrlResource从FTP服务器中装载资源
没有前缀 com/baobaotao/beanfatory/beans.xml 根据ApplicationContext具体实现类采用对应的类型的Resource

  资源加载器实例

public class Testnew{
	public static void main(String[] args) throws IOException{
			//以系统文件加载实例
			String filepath = "F:/text.txt";
			Resource fileresource=new FileSystemResource(filepath);
			//以class类路径加载实例
			String classpath = "baobaotao-servlet.xml";
			Resource classsource=new ClassPathResource(classpath);
			//PathMatchingResourceResolver类实现加载
			String resolverpath = "classpath:baobaotao-servlet.xml";
			ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			Resource[] resources = resolver.getResources(resolverpath);
			System.out.println(resources.length);
			//DefaultResourceLoader获取文件数据
			ResourceLoader loader = new DefaultResourceLoader();  
			Resource resource = loader.getResource("http://www.google.com.hk");  
			System.out.println(resource instanceof UrlResource); //true  
			//注意这里前缀不能使用“classpath*:”,这样不能真正访问到对应的资源,exists()返回false  
			resource = loader.getResource("classpath:test.txt");  
			System.out.println(resource instanceof ClassPathResource); //true  
			resource = loader.getResource("test.txt");  
			System.out.println(resource instanceof ClassPathResource); //true  
	}
}

  Bean工厂和应用上下文

    BeanFactory

      BeanFactory作为类工厂,用来创建Spring的所有实例。BeanFactory作为最底层的接口,它有多重实现方式。它的主要实现类结构如下:

      BeanFactory实例

public class BeanFactoryTest {
	public static void main(String[] args) throws IOException{
		String classpath = "applicationContext.xml";
		Resource classsource=new ClassPathResource(classpath);
		BeanFactory factory = new XmlBeanFactory(classsource);
		Org org = factory.getBean("org", Org.class);
		System.out.println(org.getName());
	}
}

    BeanFactory中有一个主要的方法getBean(),用来获取Bean实例。IoC容器需要获取配置信息才能转化,在生成IoC容器时,需要Resource获取文件资源来构建BeanFactory。

    ApplicationContext

    ApplicationContext继承了BeanFactory的功能,并扩展了其他功能,如果BeanFactory是Spring的心脏,ApplicationContext就是完整的身躯。Beanfactory以编程的方式实现功能,ApplicationContext以配置的形式实现功能。ApplicationContext的体系结构为:

主要以两种方式实现获取实例:FileSystemApplicationContext和ClassPathXmlApplicationContext。

ClassPathXmlApplicationContext:用来获取类路径下的配置文件,如ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml")。如果有多个配置文件可以使用如ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"com/baobaotao/context/beans1.xml","com/baobaotao/context/beans2.xml"})。

FileSystemXmlApplicationContext:用来获取文件系统路径下的配置文件,如ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml")。如果有多个配置文件可以使用如FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{"com/baobaotao/context/beans1.xml","com/baobaotao/context/beans2.xml"})。

BeanFactory在初始化时并没有实例化Bean,而是在第一次调用时才会初始化。而ApplicationContext在初始化时就会进行实例化所有的Bean。

实例如下:

public class ApplicationContextTest {

	public static void main(String[] args){
		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				new String[]{"beans1.xml","beans2.xml"});
		User user = (User) ctx.getBean("user");
		ApplicationContext ctxn = new FileSystemXmlApplicationContext(
				new String[]{"com/baobaotao/context/beans1.xml",
				"com/baobaotao/context/beans2.xml"});
		Org org = (Org) ctxn.getBean("org");
	}
}

    AnnottationConfigApplicationcontext

在Spring3.0中已经添加类注解的配置方式,将某一个类转换为一个配置文件,其中生成的对象即为Bean实例。类注解的配置方式需要通过@Configuration来标注某一个类为配置信息容器,可以替代配置文件。基于Java的配置类加载Spring的应用上下文需要使用特殊的类加载器AnnottationConfigApplicationcontext。

实例如下:

 1、基于java类的配置信息

package testpackage;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baobaotao.service.AccountService;

@Configuration
public class AppConf {

	@Bean(name="accountService")
	public AccountService accountService(){
		AccountService accountService = new AccountService();
		return accountService;
	}
}

2、配置类加载容器

package testpackage;

import javax.security.auth.login.AppConfigurationEntry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.baobaotao.service.AccountService;

public class ConfigTest {

	public static void main(String[] args){
		//将Configuration配置类注册到容器中
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConf.class);
		AccountService accountService = applicationContext.getBean("accountService",AccountService.class);
		//如果有多个Configuration配置类注册到容器
		AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext();
		applicationContext1.register(AppConf.class);
		applicationContext1.register(DaoConf.class);
		applicationContext1.refresh();
		AccountService accountService1 = applicationContext.getBean("accountService",AccountService.class);
	}
}

    WebApplicationContext

    WebApplicationContext是为Web准备的管理容器,WebApplicationContext允许从web根目录中加载配置文件完成初始化工作。从WebApplication中获取ServletContext引用,整个web应用上下文对象将作为属性放置在Servlet中,以便web应用环境可以访问到Spring上下文。WebApplicationContex的启动需要有ServletContext实例,也就是说需要有Web容器才能启动初始化工作。目前需要配置监听器ServletContextListener来启动WebApplicationContext。配置如下: 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>baobaotao</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 从类路径下面加载Spring配置文件 -->
  <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>

    通常情况下WebApplicationContext的使用需要配置日志功能,而日志配置的加载需要用到Log4jConfigServlet或Log4jConfigListener监听器。配置如下:

 <!-- Log4J信息配置 -->
    <context-param>
    	<param-name>log4jConfigServlet</param-name>
    	<param-value>/WEB-INF/log4j.properties</param-value>
    </context-param>
  	<servlet>
  		<servlet-name>log4jConfigServlet</servlet-name>
  		<servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
  		<load-on-startup>2</load-on-startup>
  </servlet> 

二、Bean生命周期

    BeanFactory中的Bean生命周期

      整个Bean的生命周期如下图所示:

 

具体过程如下:

(1)当调用者调用getBean(beanName)向容器请求某一个Bean时,如果容器注册了InstantiationAwareBeanPostProcessor接口,则在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法。

(2)根据配置情况调用Bean的构造函数或工厂方法实例化Bean。

(3)如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessBeforeInstantiation()方法,对实例化的对象进行一些初始化设置。

(4)如果Bean 配置了属性信息,那么容器在这一步将配置的属性设置到Bean对应的属性中,在设置属性之前将先调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法。

(5)调用Bean中的setXxx()方法设置属性。

(6)如果Bean实现了BeanNameAware接口,则会调用该接口的setBeanName()方法,将配置文件中该Bean对应的名称设置到Bean中。

(7)如果Bean实现了BeanFactoryAware接口,则会调用该接口的setBeanFactory()方法,将BeanFactory容器实例设置到Bean中。

(8)如果BeanFactory装配了BeanPostProcessor后处理器,则将调用BeanPostProcessor接口的Object postProcessBeforeInitialization方法对Bean进行加工操作。其中,传入的bean是当前正在处理的bean,而beanName是当前Bean在配置文件中的配置名,返回加工处理后的bean。用户可以使用该方法对某些Bean进行特殊的处理。Spring容器所提供的AOP、动态代理等功能都通过BeanPostProcessor实现。

(9)如果Bean实现了InitializingBean接口,则将调用该接口的afterPropertiesSet()方法。

(10)如果在配置文件的bean标签中通过init-method属性指定了初始化方法,则会执行这个方法。

(11)如果BeanFactory装配了BeanPostProcessor后处理器,在这里则会执行Object postProcessAfterInitialization()方法对Bean进行加工处理。

BeanPostProcessor后处理器定义了两个方法,分别是postProcessBeforeInitialization()和postProcessAfterInitialization(),分别在第8步和在此处调用。

(12)如果在配置文件中指定了bean的scope=”prototype”,意味着配置的这个bean是多例的,每次获取该bean都会返回一个新实例化的bean,所以在这一步之后Spring容器不再管理多例的Bean,直接将当前生成的实例返回给用户。

对于scope=”singleton”的Bean(默认情况),意味着这个Bean是单例的,就需要把这个Bean缓存到在Spring IOC容器中,用户每次获取时都从这个容器中获取,并且Spring会对这些Bean进行后续的生命周期管理。

(13)对于单例的Bean,容器关闭时,会触发Spring对Bean的后续生命周期的管理工作。如果Bean实现了DisposableBean接口,则会调用该接口的destroy()方法,在这里可以进行释放资源、记录日志等操作。

(14)对于单例的Bean,如果在配置文件中指定了destroy-method属性,Spring则会执行这个属性配置的方法,完成Bean资源释放等操作。

  由于以上步骤比较复杂,以下是简短的处理过程介绍:

  

  总结以上过程主要分为以下几步:

  Bean自身的方法:构造方法、设置属性的set方法、init-method属性和destroy-method属性。

  Bean级的生命周期方法:BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean直接实现。

  容器级生命周期接口方法:包括InstantiationAwareBeanPostProcessor和BeanPostProcessor两个接口。

  工厂后处理器接口方法:包括AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等类。

实例如下:

第二部分  Ioc容器装配Bean

  一个java实例就是一个<bean>,bean的配置为<bean id="org" name="org" class="com.baobaotao.domain.Org">。id为bean的唯一标识,不能在容器中重复,name为bean的名称可以重复,class为实例创建所使用的java类。

一、依赖注入

  1、属性注入

  属性值为对象类型的配置文件要使用ref引入属性值。

package com.baobaotao.service;

import com.baobaotao.dao.OrgDao;
import com.baobaotao.domain.Org;

public class OrgService {

	private OrgDao orgDao;
	public OrgDao getOrgDao() {
		return orgDao;
	}
	public void setOrgDao(OrgDao orgDao) {
		this.orgDao = orgDao;
	}
	public Org findAllOrg(String eid){
		Org result = orgDao.findAllOrg(eid);
		return result;
	}
}
<bean id="orgService" class="com.baobaotao.service.OrgService">
<property name="orgDao" ref="orgDao"></property>
</bean>

  属性值为普通数据类型要使用value引入属性值

package com.baobaotao.domain;

public class Org {

	private String id;
	private String name;
	public Org(String id,String name) {
		this.id = id;
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
<bean name="org" class="com.baobaotao.domain.Org">
<property name="id" value="12345"/>
<property name="name" value="青岛大学"/>
</bean>

  2、构造方法注入

  分为按类型匹配、按索引匹配和按名称匹配。在构造属性中如果引入的是普通数据类型使用value引入值,如果是对象类型使用ref引入。

  按索引匹配

package com.baobaotao.dao;

import com.baobaotao.domain.Org;

public class OrgDao {

	private Org org;
	public  OrgDao(Org org) {
		this.org = org;
	}
	public Org findAllOrg(String eid){
		Org org_new = org;
		return org_new;
	}
}
<bean name="orgDao" class="com.baobaotao.dao.OrgDao">
<constructor-arg index="0" ref="org"></constructor-arg>
</bean>

  按名称匹配

package com.baobaotao.domain;

public class Org {

	private String id;
	private String name;
	public Org(String id,String name) {
		this.id = id;
		this.name = name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
<bean name="org" class="com.baobaotao.domain.Org">
<constructor-arg name="id" value="12345"></constructor-arg>
<constructor-arg name="name" value="青岛大学"></constructor-arg>
</bean>

  3、工厂方法注入

import java.util.HashMap;
import java.util.Map;

import com.baobaotao.domain.User;

public class FactoryTest {
	private static Map<Integer, User> map = new HashMap<Integer,User>();
	static{
		map.put(1, new User("Honda","11111"));
		map.put(2, new User("Audi","22222")); 
		map.put(3, new User("BMW","33333"));
		}
	public static User getUser(int id){
		return map.get(id); 
		}

}
<bean id="user" class="testpackage.FactoryTest" factory-method="getUser"> 
<constructor-arg value="3"></constructor-arg>
</bean>

   利用静态工厂方法定义的bean item种, class属性不在是bean的全类名, 而是静态工厂的全类名, 而且还需要指定工厂里的 getBean 静态方法名字和参数。

二、注入参数详解

  为实例注入值时分为两类,一类是注入普通数据类型如String、int等,一类是注入其他Bean实例。

  1、注入普通数据类型

  第一种

<bean name="org" class="com.baobaotao.domain.Org">
<property name="id" value="12345"></property>
<property name="name" value="青岛大学"></property>
</bean>

  第二种

<bean name="org" class="com.baobaotao.domain.Org">
<property name="id"> <value>"12345"</value></property>
<property name="name"> <value>"青岛大学"</value></property>
</bean>

  2、引入其他Bean

  在引入其他bean时,主要分为引入同一容器或父容器的bean,引入本地容器的bean,引入父容器的bean。<ref>标签可以通过三个属性进行区分

  bean:引用同一容器或父容器bean。

  parent:引入父容器bean。

  local:引入同一容器bean。

<!--bean1.xml-->
<bean name="orgDao" class="com.baobaotao.dao.OrgDao">
</bean>
<!--bean2.xml,bean2.xml是bean1的子容器-->
<bean name="orgDao" class="com.baobaotao.dao.OrgDao">
</bean>
<bean name="orgService" class="com.baobaotao.service.OrgService">
<property name="orgDao"><ref parent="orgDao"/></property>
</bean>
<bean name="orgService1" class="com.baobaotao.service.OrgService">
<property name="orgDao"><ref bean="orgDao"/></property>
</bean>
<bean name="orgController" class="com.baobaotao.controller.OrgController">
<property name="orgService"> <ref local="orgService"/></property>
</bean>

  3、内部Bean

  如果该实例自在一个bean中引用,在其他任何地方没有被引用,可以使用内部bean,实例如下:

<bean name="orgDao" class="com.baobaotao.dao.OrgDao">
<property name="org">
<bean class="com.baobaotao.domain.Org">
<property name="id" value="123"/>
<property name="name" value="青岛大学"/>
</bean>
</property>
</bean>

  4、集合类型属性

  如果集合中的元素之为普通数据类型就使用<value>标签引用,如果元素为其他的bean就用<ref>标签引用

  list类型

<bean name="org" class="com.baobaotao.domain.Org">
    <property name="list">
        <list>
            <value>12345</value>
            <value>67890</value>        
        </list>
    </property>
</bean>

  set类型

<bean name="org" class="com.baobaotao.domain.Org">
    <property name="set">
        <set>
            <value>12345</value>
            <value>67890</value>        
        </set>
    </property>
</bean>

  map类型

<bean name="org" class="com.baobaotao.domain.Org">
    <property name="map">
        <map>
            <entry>
                <key><value>12345</value></key>
                <value>67890</value>  
               </key>     
        </map>
    </property>
</bean>    

  5、简化配置方式

  在对属性值注入时,可以对标签进行简化操作。如

  <property name=""><value></value></property>改为<property name=""  value=""></property>

  <property name=""><ref bean=""></ref></property>改为<property name=""  ref=""></property>

  <ref local=""></ref>和<ref parent=""></ref>没有对应的简化

  6、使用P命名空间简化配置

<bean name="org" class="com.baobaotao.domain.Org" 
   p:id="12345"
   p:name="青岛大学"
/>
<bean name="orgDao" class="com.baobaotao.dao.OrgDao"
   p:org-ref="org"
/>

  7、继承

  如果多个实例拥有多个相同的属性,可以为它们设置一个父实例。例如

<bean name="org" class="com.baobaotao.domain.Org"  abstract="true">
<property name="id" value="12345"></property>
<property name="name" value="青岛大学"></property>
</bean>
<bean name="org1" parent="org">
<property name="id" value="12345"></property>
</bean>
<bean name="org2" parent="org">
<property name="name" value="青岛大学"></property>
</bean>

  在父实例的<bean>标签中设置abstract属性,在子实例的<bean>标签中设置parent属性。

  8、整合多个配置文件

  在一个大型的应用系统中可能存在多个xml配置文件,要想合并多个配置文件,主要有两种方法。

  方法一:在web.xml配置信息中以逗号隔开,进行加载

<context-param>
  <param-name>contextConfigLocation</param-name>
     <param-value>
        classpath:applicationContext.xml,
        classpath:applicationContext.xml1,
        classpath:applicationContext.xml2
     </param-value>
</context-param>
<listener>
  <listener-class>
     org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

  方法二:在其中一个xml配置文件中进行<import>引入

<import resource="classpath:applicationContext.xml1"/>
<import resource="classpath:applicationContext.xml2"/>
<import resource="classpath:applicationContext.xml3"/>

三、Bean作用域

  每一个Bean都会有一个作用范围,如果超出这个范围,就会生成新的Bean。现在Spring分为5种作用域。

  singleton:只会加载容器时创建一次实例。如果不指定Bean的作用域,Spring默认使用singleton作用域。在容器启动时会将所有的Bean进行实例化,这样虽然需要耗费一定时间,但是可以带来两个好处:1、提前了解某些实例的配置问题。2、将加载好的实例放到内存可以提高调用的效率。

<bean name="orgController" class="com.baobaotao.controller.OrgController"
   scope="singleton">
<property name="orgService" value="orgService"></property>
</bean>

  prototype:每次通过容器获取Bean实例时都会重新创建一个新的实例。prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效。在Spring的配置中可以通过增加RequestContextListener监听器来负责request作用域生效。但是在Srping2.0之前是通过RequestContextFilter来监听。

<filter>
   <filter-name>requestContextFilter</filter-name>
   <filter-class>
      org.springframework.web.filter.RequestContextListener
   </filter-class>
</filter>
<filter-mapping>
  <filter-name>requestContextFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<bean id="loginAction" class="com.abc.LoginAction" scope="request" />

  session:对于每次HTTP Session,也就是横跨整个Session中的所有HTTP请求。使用session定义的Bean都产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。

globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效。

  作用域依赖问题

  如果一个singleton作用域的Bean引用其他作用域的实例,可以通过动态代理获取到响应的实例。如boss实例引用request作用域的car实例。

<bean id="car" class="com.abc.Car" scope="request" >
<aop:scoped-proxy/>
<bean id="boss" class="com.abc.Boss" scope="singleton" />

  boss实例会根据所在的HTTP请求查找响应HTTP中的实例car。

三、基于注解的配置

  1、使用注解定义Bean

  Spring的Xml配置是将class类与Bean信息进行分离,采用注解的方式是在class类上进行标注。

  spring没有采用约定优于配置的策略,spring要求显示指定搜索哪些路径下的Java文件。spring容器在在启动时会扫描指定包中的java类,将会把标注注解的的java类全部注册成spring Bean。

  @Component:标准一个普通的spring Bean类。 
  @Controller:标注一个控制器组件类。 
  @Service:标注一个业务逻辑组件类。 
  @Repository:标注一个DAO组件类

package com.baobaotao.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.baobaotao.domain.User;
import com.baobaotao.service.UserService;

@Controller
public class UserController{
	@Autowired
	private UserService userService;
	@RequestMapping(value="login")
	public String login(HttpServletRequest request,User user){
		System.out.println("=========aaaaaaaa====");
		String result = userService.login(user.getUserName(), user.getPassword());
		return "success";
	}
}

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Service
public class UserService{
	@Autowired
	private UserDao userDao;
	public String login(String name,String password) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}

package com.baobaotao.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao{
	public String findUserByName(String userName) {
		return "abc";
	}
}

package com.baobaotao.dao;

import com.baobaotao.domain.Org;
@Component("orgDao")
public class OrgDao {

	private Org org;
	public void OrgDao(Org org) {
		this.org = org;
	}
	public Org findAllOrg(String eid){
		Org org_new = org;
		return org_new;
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.baobaotao.dao"></context:component-scan>
<context:component-scan base-package="com.baobaotao.service"></context:component-scan>
<context:component-scan base-package="com.baobaotao.controller"></context:component-scan>
</beans>

  通过标签<context:component-scan>来扫描指定包下面的注解java类,同时可以添加过滤条件查找基包下的子包中的java类进行实例化。如

<context:component-scan base-package="com.baobaotao.controller" 
resource-pattern="ann/*.class">
</context:component-scan>

  该扫描标签主要是扫描基包com.baobaotao.controller下的ann子包中的java类进行实例化。

  2、@Autowired自动装配Bean

  @Autowired自动注入:

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Service(request=true)
public class UserService{
	@Autowired
	private UserDao userDao;
	public String login(String name,String password) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}

  @Autowired在注入实例时会查找复合条件的Bean进行注入,默认会按照类型匹配的方式进行匹配。如果找不到匹配的Bean,request=true时会抛出异常,如果request=false,则不会抛出异常。但是在按照类型匹配时如果有两个Bean都为同一类型时,只能显示的标注注入名称。如现在UserDao类有两个名为userDao和otherUserDao,此时需要使用@Qualifilter进行显示说明。

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Service(request=true)
public class UserService{
	@Autowired
        @Qualifier("userDao")
	private UserDao userDao;
	public String login(String name,String password) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}    

  @Autowired对方法类自动注入:

  可以使用@Autowired对方法中的参数进行注入,如果参数中的类只有一个实例可以在方法上方直接使用@Autowired,如果只有一个参数,但参数的类型有多个名称,可以使用@Qualifier进行显示标注。如

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Service(request=true)
public class UserService{
	private UserDao userDao;
        private OrgDao   orgDao
       	@Autowired
        @Qualifier("userDao")
	public String login(UserDao userDao) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
       	@Autowired
	public String check(@Qualifier("userDao") UserDao userDao,@Qualifier("oserDao")OrgDao orgDao) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}   

  3、@Resource和@Inject

  @Resource是通过名称进行注入,需要在注入时显示的表明名称,如果没有名称属性,就会选择变量名称作为注入名称,但是没有request参数判断注入正确性。但是@Inject的使用方法与@Autowired相同。如

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Service(request=true)
public class UserService{
        @Resource("userDao")
	private UserDao userDao;
	public String login(String name,String password) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}    

  @Scope作用范围

  在每个类的上方通过标注scope可以指定该类实例的作用范围。

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baobaotao.dao.UserDao;
@Scope("singleton")
@Service(request=true)
public class UserService{
        @Resource("userDao")
	private UserDao userDao;
	public String login(String name,String password) {
		String pass = userDao.findUserByName(name);
		return "success";
	}
}    

四、基于Java类的配置

  使用java类提供Bean信息:

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baobaotao.dao.UserDao;
@Configuration
public class Appl{
        @Bean("userDao")
	public UserDao  userDao() {
		UserDao userDao = new UserDao();
		return userDao;
	}
}  

  上述实例中@Configuration会将Appl类注解为一个Spring容器,容器中的@Bean会将每个方法返回的实例标注为一个Bean,@Bean如果没有属性值,默认Bean的名称为类名。上述的实例相当于如下xml配置。

<bean id="userDao" class="com.baobaotao.dao.UserDao"/>

  如果多个Bean在不同的Configuration配置类中,要想引用不同类之间的实例,可以通过@Autowired进行自动装配,因为@Configuration在注解时已经注入@Component注解,可以作为一个普通的实例进行调用。如

package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baobaotao.dao.UserDao;
@Configuration
public class DaoConfig{
        @Bean("userDao")
     @Scope("singleton") public UserDao userDao() { UserDao userDao = new UserDao(); return userDao; } }
package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baobaotao.dao.UserDao;
@Configuration
public class ServiceConfig{
        @Autowired
        DaoConfig  daoConfig;
        @Bean("userService")
	public UserService  userService() {
		UserService userService = new UserService();
                userService.setUserDao(daoConfig.userDao);
		return userService;
	}
}  

  基于java类的配置信息启动Spring容器

  1、启动Spring容器可以使用代码启动

package testpackage;
import javax.security.auth.login.AppConfigurationEntry;
import org.apache.catalina.core.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.baobaotao.service.AccountService;

public class JavaConfigTest {

	public static void main(String[] args){
		//将Configuration配置类注册到容器中
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConf.class);
		AccountService accountService = applicationContext.getBean(AccountService.class);
		//如果有多个Configuration配置类注册到容器
		AnnotationConfigApplicationContext applicationContext1 = new AnnotationConfigApplicationContext();
		applicationContext1.register(AppConf.class);
		applicationContext1.register(DaoConf.class);
		applicationContext1.refresh();
		AccountService accountService1 = applicationContext.getBean(AccountService.class);
	}
}

  如果有多个Configuration类需要注册到容器,可以将多个Configuration类注册注册到容器中。也可以在一个Configuration类中引入其他的多个Configuration类,如下实例

package testpackage;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.baobaotao.dao.UserDao;

@Configuration
@Import(AppConf.class)
public class DaoConf {

	@Bean
	public UserDao userDao(){
		UserDao userDao = new UserDao();
		return userDao;
	}
}

  2、通过XML配置加载Configuration中的Bean

  如果在Configuration类中注册了多个Bean,可以将Configuration类放到配置文件中,在加载配置文件时将Bean添加到容器中。实例如下

package testpackage;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.baobaotao.dao.UserDao;

@Configuration
@Import(AppConf.class)
public class DaoConf {

	@Bean
	public UserDao userDao(){
		UserDao userDao = new UserDao();
		return userDao;
	}
}
xmlns:c="http://www.springframework.org/schema/c"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="testpackage" resource-pattern="DaoConf.class"></context:component-scan>
</beans>

  3、通过Configuration加载XML中的Bean

  除了java类加载Configuration类将Bean添加到容器,将Configuration类放到xml配置文件中,在加载配置文件时将Bean添加到容器外,可以将xml配置文件中的bean加载到Configuration类,在java类启动容器时,加载Configuration类中的所有容器,实例如下

xmlns:c="http://www.springframework.org/schema/c"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.baobaotao.dao"></context:component-scan>
<context:component-scan base-package="com.baobaotao.service"></context:component-scan>
<context:component-scan base-package="com.baobaotao.controller"></context:component-scan>
<bean name="org" class="com.baobaotao.domain.Org">
<constructor-arg name="id" value="12345"></constructor-arg>
<constructor-arg name="name" value="青岛大学"></constructor-arg>
</bean>
<bean name="orgDao" class="com.baobaotao.dao.OrgDao">
<constructor-arg index="0" ref="org"></constructor-arg>
</bean>
<bean name="orgService" class="com.baobaotao.service.OrgService">
<property name="orgDao" value="orgDao"></property>
</bean>
<bean name="orgController" class="com.baobaotao.controller.OrgController">
<property name="orgService" value="orgService"></property>
</bean>
</beans>

  

package testpackage;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

import com.baobaotao.dao.UserDao;

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class DaoConf {

	@Bean
	public UserDao userDao(){
		UserDao userDao = new UserDao();
		return userDao;
	}
}

  @Configuration:将java类标注为Spring容器

  @Import:引入带有@Configuration的java类。

  @ImportResource:引入spring配置文件.xml

第三部分  容器的进阶

一、引用外部属性文件

  为了能够将外部的文件信息引入到容器中,Spring提供了多种方式将外部信息加载到容器中。属性文件中的每个属性的属性名和属性值一一对应。属性文件如下:

dataSource=com.mchange.v2.c3p0.ComboPooledDataSource  
driverClass=com.mysql.jdbc.Driver  
jdbcUrl=jdbc\:mysql\://localhost\:3306/shop  
user=root  
password=root 

  1、PropertyPlaceholderConfigurer引用外部属性文件:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="locations"> <!-- PropertyPlaceholderConfigurer类中有个locations属性,接收的是一个数组,即我们可以在下面配好多个properties文件 -->  
        <array>  
            <value>classpath:conn.properties</value>  
        </array>  
    </property>  
</bean>
<bean id="dataSource" class="${dataSource}"<!-- 这些配置Spring在启动时会去conn.properties中找 --> 
<property name="driverClass" value="${driverClass}" />  
  <property name="jdbcUrl" value="${jdbcUrl}" />
<property name="user" value="${user}" />
<property name="password" value="${password}" />
</bean>  

  2、使用context:property-placeholde引用属性文件:

<context:property-placeholder location="classpath:conn.properties"/><!-- 加载配置文件 -->  
  
<!-- com.mchange.v2.c3p0.ComboPooledDataSource类在c3p0-0.9.5.1.jar包的com.mchange.v2.c3p0包中 -->  
 <bean id="dataSource" class="${dataSource}"> <!-- 这些配置Spring在启动时会去conn.properties中找 -->  
    <property name="driverClass" value="${driverClass}" />  
    <property name="jdbcUrl" value="${jdbcUrl}" />  
    <property name="user" value="${user}" />  
    <property name="password" value="${password}" />  
 </bean>  

  3、通过注解引入属性文件

package testpackage;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ImportResource("classpath:common.properties")
public class MyDataResource {

	@Value("${userName}")
	private String userName;
	
	public String getUserName(){
		return this.userName;
	}
}

二、容器事件

  容器事件中主要包括三个实体:

  1、事件源:事件的产生者。

  2、事件监听器注册表:所有的监听器都要保存在注册表中。

  3、事件广播器:事件与事件注册表之间的桥梁。

  事件类

  ApplicationEvent继承了了普通事件接口EventObject,ApplicationEvent拥有两个子类:ApplicationContextEvent和RequestHandleEvent。

  事件监听器接口

  ApplicationListener接口继承了EventListener,并定义了一个方法onApplicationEvent(E event),该方法接收事件进行处理。在Spring3.0之后新增SmartApplicationListener作为ApplicationListener的子类。

  事件广播器

  事件广播器SimpleApplicationEventMulicaster类继承AbstractApplicationEventMulticaster接口,AbstractApplicationEventMulticaster接口继承ApplicationEvent接口。

  实例

  定义事件类

public class SmsSendEvent extends ApplicationContextEvent {
    private String target;
    public SmsSendEvent(ApplicationContext source, String target) {
        super(source);
        this.target = target;
    }

    public String getTarget() {
        return target;
    }
}

  定义监听器

public class SmsSendListener implements ApplicationListener<SmsSendEvent> {
    public void onApplicationEvent(SmsSendEvent event) {
        System.out.println("向 "+event.getTarget()+" 发送短信");
    }
}

  实现ApplicationContextAware 接口类,并具有发布事件的能力

public class SmsSender implements ApplicationContextAware {

    private ApplicationContext context;

    /**
     * 容器启动时,注入容器实例
     *
     * @param applicationContext
     * @throws BeansException
     */
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    /**
     * 发送短信
     * @param target
     */
    public void send(String target) {
        System.out.println("准备发送短信");
        SmsSendEvent event = new SmsSendEvent(context, target);
        context.publishEvent(event);//向容器中所有注册了该事件的监听器发送该事件
    }
}

  bean 配置

<?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-4.0.xsd">

    <!-- 事件监听器-->
    <bean class="net.deniro.spring4.event.SmsSendListener"/>

    <bean id="SmsSender" class="net.deniro.spring4.event.SmsSender"/>

</beans>

  测试监听器

public static void main(String[] args){
	SmsSender sender= (SmsSender) context.getBean("SmsSender");
	sender.send("1892929xxxx");
}