`
runfeel
  • 浏览: 902609 次
文章分类
社区版块
存档分类
最新评论

《partner4java 讲述Spring入门》之第一步:Spring概述与Spring IoC

 
阅读更多

(导读:希望在阅读的过程中,伴随着动手 -- 把讲述的每一步Demo都自己书写一遍,若遇到问题一时解决不了,可发邮件给我:partner4java@163.com;demo下载地址--http://download.csdn.net/detail/partner4java/4779657;也可以结合视频:http://pan.baidu.com/share/link?shareid=122408&uk=4096755201 ,视频说实话应该不咋地,是第一次录制,不存在各种讲课技巧)

第一步:扯蛋

•很多人可能接触了Spring MVC、SpringSecurity啥的,感觉Spring好混乱,亲,这都是单独项目(Spring被VMware收购之后很疯狂,开辟了大量项目)
•Spring就是个IoC容器,然后实现了一部分aopalliance、AspectJ的规范(自己也定义一些AOP相关的接口)
•至于牛人说的轻量级,只是Spring不需要借助EE容器,而EJB干的活Spring干起来也费劲

下面紧跟的很长一段,会讲一些“大道理”,就当天书看一眼吧
begin….


Spring的设计目标 :

•在Java EE的应用开发中,支持POJO和使用JavaBean的开发方式,使应用面向接口开发,充分支持OO(面向对象)的设计方法。
•轻量级,无需像EJB依赖J2EE应用服务器。
•把依赖关系的管理从Java对象中解放出来,交给IoC容器通过IoC容器实现的依赖反转。
•一方面,他通过IoC容器来管理POJO对象,以及他们相互之间的耦合关系,使企业的信息资源可以以简单的Java语言来抽象和描述;另一方面,可以通过AOP,以动态和非侵入式的方式来增强服务的功能。
•Spring的设计理念--面向接口开发而不依赖于具体的产品实现。
•实现AOP多种方式,比如他集成了AspectJ框架,同时也有ProxyFactory代理工厂的模式,而在代理工厂的实现中,既有直接使用JVM动态代理Proxy实现,也有使用第三方代理类库的CGLIB的实现。

Spring架构图:

•Spring IoC:包含了最基本的IoC容器BeanFactory的接口和实现,还提供了一些列这个接口的实现。
•Spring AOP:围绕着AOP增强功能,Spring继承了AspectJ作为AOP的一个特定实现,同时还在JVM动态代理/CGLIB的基础上,实现一个AOP框架,作为Spring继承其他模块的工具。在这个模块中,Spring AOP实现了一个完整的建立AOP代理对象,实现AOP拦截,直至实现各种Advice通知的过程。
•SpringMVC:这个模块以DispatcherServlet为核心,实现了MVC模式,包括怎样与Web容器环境的继承,Web请求的拦击、分发、处理和ModelAndView数据的返回,以及如何集成各种UI视图展示和数据表现。
•SpringJDBC/Spring ORM:对JDBC进行封装,提供了RDBMS的操作对象。同时对现有的ORM框架进行template等封装提供。
•Spring的事务处理:Spring事务处理是通过Spring AOP实现自身功能增强的典型模块。在这个模块中,Spring把企业开发中事务处理的主要过程抽象出来,并且简单的通过AOP的切面增强实现了声明式事务的处理。
•Spring应用:严格来说不属于Spring的范围。丰富了整个Spring生态圈,使Spring应用越来越广泛。
•Spring远端调用:spring为应用屏蔽了各种通讯和调用细节的实现。可以使用HTTP调用器(以HTTP协议为基础),可以使用第三方的二进制通讯实现Hessian/Burlap,甚至还封装了传统Java技术中的RMI调用。


end
天书告一段落


现在Spring不都分包了么?
大体看下包的作用

•spring-aop.jar:此JAR文件包含了所有你在应用中使用Spring AOP特性时需要的类。如果应用中使用了其他涉及AOP的Spring功能时,例如声明式事务管理,你也需要将此JAR文件包含进来。
•spring-beans.jar:此文件包含了所有Spring依赖注入需要的代码。包括Bean工厂和相关支持类。
•spring-context.jar: 大部分情况下,你会需要加入spring-context.jar文件,他包含了建立应用环境上下文需要的代码。此JAR包含了建立Spring应用环境上下文所需要的代码,他将主要的ApplicationContext接口和实现、说明、JNDI、调度、主题和验证一起纳入其中。
•spring-context-support.jar:这个包文件包含了Spring的工具代码,其中包括缓存、说明、电子邮件、调度支持一级一个有趣的脚本语言支持。
•spring-core.jar: 此文件包含了Spring框架的核心代码。它用来处理注解、枚举、任务执行、资源加载一级其它一些即便在Spring框架环境外也会有用的工具和异常类。
•spring-jdbc.jar:此文件包含了JDBC支持类的代码,例如JdbcTemplate类和JdbcDaoSupport类。
spring-jms.jar: 此文件包含JMC的代码。

•spring-orm.jar: 此文件包含了对象-关系映射(ORM)工具需要的文件。把这个包加入到classpath上会提供对Hibernate3、iBATIS、JDO、JPA和TopLink的Spring支持
•spring-test.jar: 此文件包含了使用Spring框架编写单元测试和继承测试的支持代码。他支持JUnit3、JUnit4和TestNG测试框架。另外,你也可以使用org.springframe-work.mock包中的类,他代表了JNDI类和Web相关类的模拟实现。
•spring-tx.jar:此文件提供了核心的数据访问异常和事务技术支持。这两个概念彼此关系密切,因为一般情况下事务的同某些数据访问代码一起工作的。
•spring-web.jar:此文件包含了Spring Web应用支持(工具类、绑定器、分段文件解析器)的代码。
•spring-webmvc.jar:此文件 包含了Spring MVC代码。
•spring-webmvc-portlet.jar:此文件包含创建基于porlet(而不是servlet)Web应用所需要的代码。
•spring-webmvc-struts.jar:此文件包含了在Spring中使用Jakarta Struts框架所需的代码。

哥,我走了,别说了,吓到我了,东西太多了
亲,别怕,其实Spring就那十个左右的注解就完事了
牛B的框架不会太难用,牛B的代码不会太难懂。我们要跻身于那批能写牛B的代码人群中。。。


第二步,简易IoC:


控制反转IoC

begin:


控制反转和依赖注入:

•控制反转(IoC)。依赖注入(DI)。
•实际上依赖注入是控制反转的一种特殊形式。
•控制反转和依赖注入的核心目标是提供一个更为简单的机制去规定和组件之间的依赖关系(他通常被称作为对象的协助者),并在他们的生命周期中管理这些依赖。
•一个需要某些特定依赖的组件通常被称为依赖对象或者目标。控制反转提供的服务使一个组件能够在他的生命周期中访问他的依赖和服务,使用这个这种方法与他的依赖进行交互,这是一件非常美妙的事情。
•大体来说,控制反转可以分为两个子类型:依赖注入和依赖查找。这些字类型又能划分成控制反转服务的若干具体实现。以此定义我们可以清楚的知道,当我们谈论依赖注入时我们总是在谈论控制反转,反之则不然。


不依赖Spring,我们先简单的实现下控制反转:

•Helloworld背景:
•我们要实现一个男人和多个女人,男人对女人有管理权限,但是这个男人还想能够随意的更换女人(当然所有男人都想,除非你懂得)。
•问题:
•当这个男人出生的时候(instance)的时候并不知道未来会是什么样的女人可以交予他管理
package com.partner4java.nospring.ioc;

public interface Girl {
	/**
	 * 目前只提供简单服务
	 * 
	 * @return
	 */
	public String kiss();
}


package com.partner4java.nospring.ioc;

public interface Boy {
	/**
	 * 好吧,我只想关灯,但是关灯之前,我需要一个女孩
	 * 
	 */
	public void closeLight();
}



package com.partner4java.nospring.ioc;

/**
 * 
 * simple introduction 知道白富美么?
 * <p>
 * detailed comment
 * 
 * @author 王昌龙 2012-9-3
 * @see
 * @since 1.0
 */
public class Baifumei implements Girl {

	@Override
	public String kiss() {
		return "白富美";
	}

}



package com.partner4java.nospring.ioc;

/**
 * 
 * simple introduction 懂萝莉控么?
 * <p>
 * detailed comment
 * 
 * @author 王昌龙 2012-9-3
 * @see
 * @since 1.0
 */
public class Luoli implements Girl {

	@Override
	public String kiss() {
		return "小萝莉";
	}

}



package com.partner4java.nospring.ioc;

/**
 * 
 * simple introduction 对不起了哥们们,我自己写的代码,我就先自己当一把男一号了
 * <p>
 * detailed comment
 * 
 * @author 王昌龙 2012-9-3
 * @see
 * @since 1.0
 */
public class XiaoLong implements Boy {
	private Girl girl;

	public void setGirl(Girl girl) {
		this.girl = girl;
	}

	@Override
	public void closeLight() {
		System.out.println(this.getClass().getSimpleName() + " closeLight " + girl.getClass().getSimpleName() + " " + girl.kiss());
	}

}

类路径下创建一个properties文件:boy2girl.properties

baifumei=com.partner4java.nospring.ioc.Baifumei
luoli=com.partner4java.nospring.ioc.Luoli
longge=com.partner4java.nospring.ioc.XiaoLong

工厂:

package com.partner4java.nospring.ioc;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {
	private Map<String, String> beanDefinitions;

	public BeanFactory(String beanDefinitionSource) {
		readBeanDefinitions(beanDefinitionSource);
	}

	private void readBeanDefinitions(String beanDefinitionSource) {
		Properties props = new Properties();
		InputStream is = BeanFactory.class.getResourceAsStream(beanDefinitionSource);
		if (is == null) {
			throw new IllegalArgumentException("Could not load properties file " + beanDefinitionSource);
		}
		try {
			props.load(is);
			is.close();

			beanDefinitions = new HashMap<String, String>();

			for (Map.Entry<Object, Object> bean : props.entrySet()) {
				beanDefinitions.put((String) bean.getKey(), (String) bean.getValue());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Object getBean(String name) {
		String className = beanDefinitions.get(name);
		if (className == null) {
			return null;
		}
		try {
			return Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}
开始干大事了:

package com.partner4java.nospring.ioc;

import java.util.concurrent.TimeUnit;

public class BeanFactoryTest {

	public static void main(String[] args) throws InterruptedException {
		BeanFactory beanFactory = new BeanFactory("/boy2girl.properties");
		Girl baifumei = (Girl) beanFactory.getBean("baifumei");
		Girl luoli = (Girl) beanFactory.getBean("luoli");
		XiaoLong longge = (XiaoLong) beanFactory.getBean("longge");
		longge.setGirl(baifumei);
		longge.closeLight();
		System.out.println("休息下");
		TimeUnit.SECONDS.sleep(2);
		longge.setGirl(luoli);
		longge.closeLight();
	}

}

怎么样?我们这工厂。是不是想找什么女人都可以了?

使用Spring简单IoC:

•两个例子:
•1、只是控制反转
•2、使用了依赖注入

这俩简单例子所用就是真实的Spring解析核心类,虽然我们平时不直接接触,但是可以自己私下研究下

(我们还是借助上面已经完成的男孩、女孩类 -- BeanFactory使用Spring提供的)

新建文件boy2girlspring1.properties:

baifumei.(class)=com.partner4java.nospring.ioc.Baifumei
luoli.(class)=com.partner4java.nospring.ioc.Luoli
longge.(class)=com.partner4java.nospring.ioc.XiaoLong

package com.partner4java.spring.ioc;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;

import com.partner4java.nospring.ioc.Girl;
import com.partner4java.nospring.ioc.XiaoLong;

public class HelloWorld1 {

	public static void main(String[] args) throws InterruptedException {
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		BeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(
				beanFactory);
		beanDefinitionReader.loadBeanDefinitions("boy2girlspring1.properties");

		Girl baifumei = (Girl) beanFactory.getBean("baifumei");
		Girl luoli = (Girl) beanFactory.getBean("luoli");
		XiaoLong longge = (XiaoLong) beanFactory.getBean("longge");
		longge.setGirl(baifumei);
		longge.closeLight();
		System.out.println("休息下");
		TimeUnit.SECONDS.sleep(2);
		longge.setGirl(luoli);
		longge.closeLight();
	}

}
新建文件boy2girlspring2.properties:

baifumei.(class)=com.partner4java.nospring.ioc.Baifumei
luoli.(class)=com.partner4java.nospring.ioc.Luoli
longge1.(class)=com.partner4java.nospring.ioc.XiaoLong
longge2.(class)=com.partner4java.nospring.ioc.XiaoLong
longge1.girl(ref)=baifumei
longge2.girl(ref)=luoli

package com.partner4java.spring.ioc;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;

import com.partner4java.nospring.ioc.Boy;

public class HelloWorld2 {

	public static void main(String[] args) throws InterruptedException {
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		BeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);
		beanDefinitionReader.loadBeanDefinitions("boy2girlspring2.properties");
		
		Boy longge1 = (Boy) beanFactory.getBean("longge1");
		longge1.closeLight();
		TimeUnit.SECONDS.sleep(2);
		Boy longge2 = (Boy) beanFactory.getBean("longge2");
		longge2.closeLight();
	}

}

基础类:

•DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
•BeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(
•beanFactory);
•beanDefinitionReader.loadBeanDefinitions("boy2girlspring1.properties");

小结(白话):

•是不是感觉很爽啊?这样,我们就可以简单是使用“策略模式”等
•然后,我们就可以很容易的解耦,可以简易使用设计模式核心思想“少用继承、多用组合”(查看Spring的源码,你也可以看到IoC无处不在)

第三步:再议IoC

再议Spring IoC,前面我们简单的helloworld了下,先有个直观的认识 下面我们继续IoC (通过这几个demo是不是对IoC有些直观的认识了?)


控制反转类型:

•依赖查找有两种类型:依赖拖拽和上下文依赖查找(CDL)。
•依赖注入两种类型:构造方法依赖注入和设置方法依赖注入。

依赖拖拽HelloWorld:

•注入的对象如何与组件发生联系,这个过程就是通过依赖拖拽实现。

上下文依赖查找(CDL):

•在某些方面跟依赖拖拽相似,但是上下文依赖查找中,查找的过程是在容器管理的资源中进行的,而不是从集中注册表中,并且通常是作用在某些设置点上。


依赖注入方式:

•构造方法依赖注入:

在构造方法依赖注入中,组件的依赖从他们的构造方法中提供。组件声明一个或一组构造方法,将他们的依赖作为参数,控制反转容器就会降依赖在组件实例化时传给他。

•设置方法依赖注入:

在设置方法依赖注入中,控制反转容器通过JavaBean风格的设置方法为组件注入依赖。一个组件的设置方法向反转容器公开一组依赖。


XmlBeanFactory:

•XmlBeanFactory源于DefaultListableBeanFactory且简单的扩展了他,利用XmlBeanDefinitionReader进行自动配置
•现在这个类已经@Deprecated了
•代码写法和前面基本一样,只是配置格式有很多需要学习的

我们前面利用Properties形式的配置改成XML格式:

在类路径下创建文件:

/META-INF/spring/helloworld.xml

1、交给spring管理的基本单位<bean>;

2、一般需要一个标识id属性来进行区分;

3、然后class指定<bean>标签要交给Spring管理哪个类;

4、当你要利用依赖注入来实现某属性的赋值,可利用<property>标签;

5、<property>标签的name属性为对应调用的POJO的set方法,ref为一个bean的id(也就是传入的赋值类 -- 此类也是Spring管理的)。

<?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:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<bean id="baifumei" class="com.partner4java.nospring.ioc.Baifumei" />
	<bean id="luoli" class="com.partner4java.nospring.ioc.Luoli" />
	<bean id="xiaoLong" class="com.partner4java.nospring.ioc.XiaoLong">
		<property name="girl" ref="baifumei" />
	</bean>

</beans>         


调用:

package com.partner4java.spring.ioc;


import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;


import com.partner4java.nospring.ioc.Boy;


public class XmlBeanFactoryDemo {


	public static void main(String[] args) {
		XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(
				"/META-INF/spring/helloworld.xml"));
		Boy xiaoLong = beanFactory.getBean("xiaoLong", Boy.class);
		xiaoLong.closeLight();
	}


}


叨叨两句配置文件:

•首先都放到< beans></ beans>里面
•然后声明各种xmlns(XML命名空间)、 schemaLocation
•如果有多个xmlns,后面跟上特殊指定,如果不跟就是末尾不需指定(如:xmlns:tx=“http://www.springframework.org/schema/tx”)我们一般直接给bean了默认如: xmlns=“http://www.springframework.org/schema/beans”, 所以一般使用无需添加前缀


再来俩demo,构造器注入和set注入

public class Person {
	private String personName;
	private String address;

	public Person(String personName, String address) {
		super();
		this.personName = personName;
		this.address = address;
	}

	public void setPersonName(String personName) {
		this.personName = personName;
	}
....
创建文件:/META-INF/spring/constructor.xml

<?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:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<bean id="person" class="com.partner4java.spring.ioc.constructor.Person">
		<constructor-arg name="address" value="高铁1号1号线"/>
		<constructor-arg name="personName" value="高铁1号"/>
	</bean>
</beans>         
调用:
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/constructor.xml");
		System.out.println(applicationContext.getBean("person"));
	}

为Bean配置集合:

•就是包括基本的List、Set、Map等,基本用法没有什么好说的
•但是,当你使用继承的时候,可以使用<list merge="true">来在子类中声明合并父类,但是会放在父类的序列之后

为集合元素指定数据类型 :

•问题:
默认情况下,Spring将集合中所有元素作为字符串对待。
如果你不打算将集合元素作为字符串使用,就必须为他们指定数据类型。
•解决方案:
可以使用<value>的type指定,也可以在集合标记中指定value-type

使用工厂Bean和Utility Schema定义集合 :

•问题:
使用基本集合标记定义集合时,你不能指定集合的实体类,例如LinkedList、TreeSet或TreeMap,而且,你不能通过将集合定义为可供其他Bean引用的单独Bean在不同的Bean中共享集合。
•解决方案:
两种方式
1、使用对应的集合工厂Bean,如ListFactoryBean、SetFactoryBean和MapFactoryBean。
2、引入util schema中使用集合标记,如<util:list>、<util:set>和<util:map>。

bean的实例化模式:

•默认情况下,Spring中所有的bean均为单例。也就是Spring只维护一个单独的bean实例,所有的依赖对象都是用同一个实例,并且所有对BeanFactory.getBean(XXX)方法的调用都返回相同的实例。
•对此测试下:打印了true(要是你的bean里有外部变量,你懂的,就等着掉坑吧)

非单例实例化模式:

•prototype:每次调用getBean()方法都返回一个bean的新实例。
•request:每次在Web应用中调用getBean()方法将为每一个Http请求返回bean的唯一实例。此行为只在WebApplicationContext和他的子接口中实现。
•session:每次调用getBean()方法将为每个HttpSession返回bean的唯一实例。跟request一样,此作用范围只在WebApplicatoinContext和他的子接口中有效。
•globalsession:每次在portlet上下文中调用getBean()方法将为全局HttpSession返回bean的唯一实例。跟request和session一样,此实例化方式只被WebApplicationContext和他的子接口所支持。

选择一种实例化模式:

•单例适用:
•无状态的共享对象:当有一个无状态且关联很多依赖的对象时,使用单例。如果无状态的差别,你就不需要做同步处理,当某个依赖对象在工作中需要使用这个bean时,你就不需要创建新的实例。
•只读状态共享对象:这跟前一点相似,但是有一些只读的状态,比如说一个只读的属性。这种情况下,你仍然不需要做同步处理,因此为每一个请求创建bean的实例只会增加额外的开销而已。
•共享状态的共享对象:如果你有一个必须共享状态的bean,单例便是理想选择。这种情况下,你需要确保状态写的同步尽量原子化。
•具有可读状态的高吞吐量对象:如果某一个bean在你的应用程序中被大量的使用,你可能会发现保持单例并且对bean状态的所有写访问进行同步会比持续的创建上百个bean实例具有更好的性能。使用此方法时,在不牺牲连贯性的前提下尽量保持同步的原子性。当应用程序在一个较大的时间跨度内创建了大量的实例,或者当你的共享对象只有少量可写状态,再或者当创建新实例花销太大时,你会发现这个方法尤其有用。

使用非单例:

•具有可写状态的对象:如果你的bean有大量的可写状态,你会发现用以同步的成本比创建新实例来处理依赖对象的每一个请求的成本要高。
•具有私有状态的对象:有时,依赖对象需要一个包含私有状态的bean以便他们能够同其他依赖此bean的对象区分开来独立运行。在这样的情况下,单例显然是不合适的。

亲,IoC知道是啥了么? 下面俩小时我们来具体说下各种基本使用 begin…


用依赖检查属性:

•问题:
•在大规模的应用中,IoC容器中可能声明了几百个甚至几千上万个Bean,这些Bean之间的依赖往往非常复杂。
•设置方法注入的不足之一是无法确定一个属性将会被注入。
•检查所有必要的属性是否已经设置是非常困难的。

解决方案(依赖检查):

•dependency-check--
•none*不执行任何依赖检查,任何属性都可以保持未设置状态
•simple如果任何简单类型(原始和集合类型)的属性未设置,将抛出UnsatisfiedDependencyException异常
•objects如果任何对象类型属性没有设置,将抛出UnsatisfiedDependencyException异常
•all如果任何类型的属性为设置,将抛出UnsatisfiedDependencyException异常
不过在新的版本中已经废弃 ,以后遇到了面熟就行

用@Required注解检查属性:

•问题:
Spring的依赖检查功能仅能检查某些类型的所有属性。他的灵活性不够,不能仅检查特定的属性。
•解决方案:
RequiredAnnotationBeanPostProcessor是一个Spring bean后处理器,检查带有@Required注解的所有bean属性是否设置。
bean后处理器是一类特殊的Spring bean,能够在每个Bean初始化之前执行附加的工作。
为了启用这个Bean后处理器进行属性检查,必须在Spring IoC容器中注册他。

(后处理器是什么我们后面会讲到)


检查不合格后报错:

Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'username'is required for bean 'user'


用法:
1、xml加上<context:annotation-config />
2、在你要检测的字段上加上注解

package com.partner4java.spring.ioc.required;

import org.springframework.beans.factory.annotation.Required;

public class User {
	private String username;
	private String password;

	public User() {
		super();
	}

	public User(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	@Required
	public void setUsername(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [username=" + username + ", password=" + password + "]";
	}

}

<?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:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<context:annotation-config />

	<bean id="user" class="com.partner4java.spring.ioc.required.User">
	</bean>
</beans>         

•调用:
		DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
		BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		beanDefinitionReader.loadBeanDefinitions("/META-INF/spring/required.xml");
		System.out.println(beanFactory.getBean("user"));
•晕,以前面讲的方式获取Bean,咋操作正常,也没报错啊

(插曲)引入ApplicationContext:

•BeanFactory接口的实现,这个系列仅实现了容器的基本功能
•ApplicationContext应用上下文,他作为容器的高级形态而存在
•具体可参照:

ApplicationContext的实现类:

•FileSystemXmlApplicationContext:可以在文件系统中应用程序有权访问的任何位置加载。
•ClasspathXmlApplicationContext:可以从classpath上任何位置加载配置,这在你需要将配置文件跟一些类文件一起打包在一个JAR文件内时会很有用处。
•XmlWebApplicationContext:是专门在Web应用环境中使用的,通过使用ContextLoaderListener或者ContextLoaderServlet,你可以为你的Web应用自动加载ApplicationContext配置。

那么改一下上面的调用:

package com.partner4java.spring.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RequiredDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/required.xml");
		System.out.println(applicationContext.getBean("user"));
	}

}

这下是不是看到报错了?

(插曲) ApplicationContext不多说(有兴趣看前页地址),记住就行了,他的Bean容器还是借助了我们前面的DefaultListableBeanFactory,然后自己又加了一些高级特性。


使用ApplicationContextAware:

•一个bean可以实现BeanFactoryAware接口的方式获得一个他所在BeanFactory的引用。同样,也可以通过实现ApplicationContextAware接口获得他在ApplicationContext的引用。


替换@Required:

•如果你有特殊语义的注意要替换@Required这个,只需要做两步:
•1、定义一个注解
package com.partner4java.spring.ioc.required;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NotNull {
	
}
修改POJO里的注解:

	// @Required
	@NotNull
	public void setUsername(String username) {
		this.username = username;
	}

•2、注入给RequiredAnnotationBeanPostProcessor交给IoC
	<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
		<property name="requiredAnnotationType" value="com.partner4java.spring.ioc.required.NotNull"/>
	</bean>

这样Spring就会自动扫描这个配置,知道你加了一个自定义注解

解析依赖:

•就是告诉BeanFactory解析某个bean需要依赖于另外一个bean的先解析
package com.partner4java.spring.ioc.dependson;

public class A {
	public A(){
		System.out.println("hello A");
	}
}



package com.partner4java.spring.ioc.dependson;

public class B {
	public B() {
		System.out.println("hello B");
	}
}

<?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:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<bean id="b" class="com.partner4java.spring.ioc.dependson.B" depends-on="a"/>
	<bean id="a" class="com.partner4java.spring.ioc.dependson.A"/>
</beans>         

来,自己写一个Test加载一下,看看他们的打印顺序。


解决构造程序歧义:

•问题:
当你为Bean指定一个或者多个构造程序参数时,Spring将视图在Bean类中查找对应的构造程序,并且传递用于Bean实例化的参数。
•解决方案:
你可以为<constructor-arg>元素指定type和index属性,帮助Spring查找预期的构造程序。

指定Bean引用:

•问题:
组成应用程序的Bean往往需要互相协作完成应用功能。为了Bean之间的互相访问,你必须在Bean配置文件中指定Bean引用。
•解决方案:
在Bean配置文件中,你可以用<ref>元素为Bean属性或者构造程序参数指定Bean引用。
只需要用<value>元素指定一个值。

自动装配:

•有时候我们会感觉,这样设置来设置去的会不会太麻烦啊?
•Spring难道不会智能点自己自动给我们塞个已有的对应值进去么?

用XML配置自动装配Bean:

•问题:
当一个Bean需要访问另一个Bean时,你可以显示指定引用装配他。但是,如果你的容器能够自动装配bean,就可以免去手工手工配置装配的麻烦。
•解决方案:
autowire属性--
no* 不执行自动装配。你必须显示的装配依赖
byName 对于每个Bean属性,装配一个同名的bean
byType 对于每个Bean属性,装配类型与之兼容的Bean。如果超过一个,将抛出UnsatisfiedDependencyException异常。
Constructor 对于每个构造程序参数,首先寻找与参数兼容的Bean。然后,选择具有最多参数的构造程序。对于存在歧义的情况,将抛出UnsatisfiedDependencyException异常。
autoetect 如果找到一个没有参数的默认构造程序,依赖讲按照类型自动装配。否则,将由构造程序自动装配。
•还可以在beans里设置全局类型:default-autowire="byName"

<?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:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<bean id="student" class="com.partner4java.spring.ioc.autowire.Student">
		<constructor-arg name="name" value="小国"/>
		<!-- 此年龄只代表他的智商 -->
		<constructor-arg name="age" value="2"/>
	</bean>
	
	<bean id="teacher" class="com.partner4java.spring.ioc.autowire.Teacher" autowire="byName"/>
</beans>         
在我们的Teacher里:

public class Teacher {
	private Student student;
	
	public void setStudent(Student student) {
		this.student = student;
	}

我们从Spring中获取一下teacher会发现,student已经自动传入。

用@Autowired和@Resource自动装配Bean :

•问题:
在Bean配置文件中设置autowire属性进行的自动装配将装配一个Bean的所有属性。这样的灵活性不足以紧紧装配特定的属性。
而且,你只能通过类型或者名称自动装配Bean。
如果这两种策略都不能满足你的需求,就必须明确的装配Bean。
•解决方案:
你可以通过@Autowired或者@Resource注解一个设置方法、构造程序、字段甚至任意方法自动装配特定的属性。
这意味着你除了设置autowire属性之外,还有一个能够满足需求的选择。
•工作原理:
配置文件加入:
<context:annotation-config />
然后利用如下其中一个注解:
@Autowired、@Qualifier("mainCatalog")、@Resource(name="myMovieFinder")
public class Teacher {
	@Resource(name="student")
	private Student student;
	
	public void setStudent(Student student) {
		this.student = student;
	}

现在试一下,看看效果。

@Autowired

@Autowired 注解可以用于“传统的”setter 方法,如下例:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

这个注解也可以用于以属性为参数/多个参数的方法

public class MovieRecommender {

    private MovieCatalog movieCatalog;
    
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

@Autowired注解甚至可以用于构造器与字段:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;
    
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

也可以一种提供来自ApplicationContext的特殊类型的所有 beans,注解字段或者方法,例如:

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}

这同样适用于集合类型:

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;
    
    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}

甚至是 Maps 也可以这样注解,只要这个 Map 的 key 类型为 String。这个 Map 的 values 应该是已知的类型,并且 keys 应该包含符合 bean 的命名:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;
    
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}

在缺省情况下,当出现0个候选的 beans时自动连接将失败;缺省行为把连接方法,构造器,字段假设为required 的依赖。这样的行为如下所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

注意

虽然当 一个类只有一个连接构造器时它将被标记为 required, 但是还是可以标记多个构造器的。在这种情况下,每一个构造器都有可能被认为是连接构造器, Spring 将会把依赖关系能够满足的构造器认为是greediest 的构造器。

@Autowired也能用于总所周知的“可解决的依赖”:BeanFactory接口,ApplicationContext接口,ResourceLoader接口,ApplicationEventPublisher接口,还有MessageSource接口。这些接口(还有它们的扩展,例如ConfigurableApplicationContext或者ResourcePatternResolver)将可以自动解决依赖,没有任何特殊必须的其它步骤需要。

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

基于注解的自动连接微调

因为通过类型的自动连接可能会有多个候选,因此经常需要在选择过程中加以控制。一种方法去完成这个控制就是使用@Qualifier注解。在最简单的情况下,您能够通过命名方式去实现这个自动连接:

public class MovieRecommender {

    @Autowired
    @Qualifier("mainCatalog")
    private MovieCatalog movieCatalog;

    // ...
}

@Qualifier注解也能够被指定为构造器的参数或者方法的参数:

public class MovieRecommender {

    private MovieCatalog movieCatalog;
    
    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("mainCatalog") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

@Resource

Spring 也提供了使用 JSR-250 bean 属性支持的注射方式。这是一种在 Java EE 5 与 Java 6 中普遍使用的方式(例如,在 JSF 1.2 中映射 beans 或者 JAX-WS 2.0 端点),对于Spring 托管的对象 Spring 可以以这种方式支持映射。

@Resource有一个‘name’属性,缺省时,Spring 将这个值解释为要注射的 bean 的名字。换句话说,如果遵循by-name的语法,如下例:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

如果没有显式地给出名字,缺省的名字将继承于字段名或者 setter 方法名:如果是字段名,它将简化或者等价于字段名;如果是 setter 方法名,它将等价于 bean 属性名。下面这个例子使用名字 "movieFinder" 注射到它的 setter 方法:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

继承Bean配置:

•问题:

在Spring IoC容器中配置Bean时,你可能拥有超过一个共享某些公用配置的Bean,比如属性和<bean>元素中的属性。你常常必须为多个Bean重复这些配置

•解决方案:
只作为模板而不能检索,必须将abstract设置为true,要求spring不实例化这个bean。
并不是所有在父<bean>元素中定义的属性都将被继承,例如,autowire和dependency-check属性不会从父bean中继承

亲,我还是感觉挺麻烦怎么办?还要配置好多XML


从Classpath中扫描组件:

•问题:
为了便于Spring IoC容器对组件的管理,你需要在Bean配置中逐个声明他们。
但是,如果Spring能够自动地检测你的组件而不需要手工配置,将大大节省你的工作量。
•解决方案:
Spring提供了一个强大的功能--组件扫描
•<context:component-scan base-package=
第一步在XML中指定要扫描的文件:
<?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:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<context:component-scan base-package="com.partner4java.spring.ioc.annotation" />
</beans>         
第二步:给自动扫描的文件加上注解
@Component\@Repository\@Service\@Controller:持久层、服务层和表现层。
•还要其他各种:@Scope、@Value
package com.partner4java.spring.ioc.annotation;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Baby {
	@Value("kiss")
	private String name;
	@Value("2")
	private int age;

	public void setName(String name) {
		this.name = name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	@Override
	public String toString() {
		return "Baby [name=" + name + ", age=" + age + "]";
	}

}

package com.partner4java.spring.ioc.annotation;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

@Component
public class Mother {
	@Resource(name = "baby")
	private Baby baby;

	public void setBaby(Baby baby) {
		this.baby = baby;
	}

	@Override
	public String toString() {
		return "Mother [baby=" + baby + "]";
	}

}
就可以用了:

package com.partner4java.spring.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationDemo {

	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/annotation.xml");
		System.out.println(applicationContext.getBean("baby"));
		System.out.println(applicationContext.getBean("mother"));
//		后台打印:
//		Baby [name=kiss, age=2]
//	    Mother [baby=Baby [name=kiss, age=2]]
	}

}

注解详解:

@Component和更多典型化注解

从Spring 2.0开始,引入了@Repository注解, 用它来标记充当储存库(又称 Data Access Object或DAO)角色或典型的类。

Spring 2.5引入了更多典型化注解(stereotype annotations): @Component@Service@Controller@Component是所有受Spring管理组件的通用形式; 而@Repository@Service@Controller则是@Component的细化, 用来表示更具体的用例(例如,分别对应了持久化层、服务层和表现层)。也就是说, 你能用@Component来注解你的组件类, 但如果用@Repository@Service@Controller来注解它们,你的类也许能更好地被工具处理,或与切面进行关联。 例如,这些典型化注解可以成为理想的切入点目标。当然,在Spring Framework以后的版本中,@Repository@Service@Controller也许还能携带更多语义。如此一来,如果你正在考虑服务层中是该用@Component还是@Service, 那@Service显然是更好的选择。同样的,就像前面说的那样,@Repository已经能在持久化层中进行异常转换时被作为标记使用了。

自动检测组件

Spring可以自动检测“被典型化”(stereotyped)的类,在ApplicationContext 中注册相应的BeanDefinition。例如,下面的这两个类就满足这种自动检测的要求:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

要检测这些类并注册相应的bean,需要在XML中包含以下元素,其中'basePackage'是两个类的公共父包 (或者可以用逗号分隔的列表来分别指定包含各个类的包)。

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
               
     <context:component-scan base-package="org.example"/>
     
</beans>

此外,在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,连个组件都会被自动检测织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。

注意

通过加入值为'false'的annotation-config属性可以禁止注册这些后置处理器。

使用过滤器自定义扫描

默认情况下,用@Component @Repository@Service @Controller (或本身使用了@Component注解的自定义注解) 注解的类是唯一会被检测到的候选组件。但是可以很方便地通过自定义过滤器来改变并扩展这一行为。 可以用'component-scan'的include-filterexclude-filter子元素来进行添加。 每个过滤器元素都要求有'type'和'expression'属性。 下面给出了四个已有的可选过滤器。

表3.7.过滤器类型

过滤器类型 表达式范例
annotation

org.example.SomeAnnotation

assignable

org.example.SomeClass

regex

org\.example\.Default.*

aspectj

org.example..*Service+


下面这个XML配置会忽略所有的@Repository注解并用“stub”储存库代替。

<beans ...>

     <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
     </context:component-scan>

</beans>

注意

你也可以用<component-scan/>元素的use-default-filters="false" 属性来禁用默认的过滤器。这会关闭对使用了@Component@Repository@Service@Controller的类的自动检测。


自动检测组件的命名

当一个组件在某个扫描过程中被自动检测到时,会根据那个扫描器的BeanNameGenerator 策略生成它的bean名称。默认情况下,任何包含name值的Spring“典型”注解 (@Component@Repository@Service@Controller) 会把那个名字提供给相关的bean定义。如果这个注解不包含name值或是其他检测到的组件 (比如被自定义过滤器发现的),默认bean名称生成器会返回小写开头的非限定(non-qualified)类名。 例如,如果发现了下面这两个组件,它们的名字会是'myMovieLister'和'movieFinderImpl':

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

注意

如果你不想使用默认bean命名策略,可以提供一个自定义的命名策略。首先实现 BeanNameGenerator 接口,确认包含了一个默认的无参数构造方法。然后在配置扫描器时提供一个全限定(fully-qualified)类名:

<beans ...>
               
     <context:component-scan base-package="org.example"
                             name-generator="org.example.MyNameGenerator" />

</beans>

作为一条常规,当其他组件可能会显式地引用一个组件时可以考虑用注解来指定名称。 另一方面,当容器负责织入时,自动生成的名称就足够了。


为自动检测的组件提供一个作用域

通常受Spring管理的组件,默认或者最常用的作用域是“singleton”。然而,有时也会需要其他的作用域。 因此Spring 2.5还引入了一个新的@Scope注解。只要在注解中提供作用域的名称就行了, 比方说:

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

各种特色方式的Bean创建 (因为有时候,我们的bean并不是 普通的形态):

使用Spring的FactoryBean创建bean:

•问题:
你可能希望用Spring的工厂Bean在Spring IoC容器中创建Bean。
工厂Bean(Factory Bean)是作为创建IoC容器中其他Bean的工厂的一个FactoryBean。概念上,工厂Bean与工厂方法非常类似,但是他是Bean构造期间可以Spring IoC容器识别为Spring专用Bean。
•解决方案:
工厂Bean的要求是实现FactoryBean接口。为了方便,提供了抽象模板类AbstractFactoryBean供你扩展。
工厂Bean主要用于实现框架机制。如:
·在JNDI中查找对象(例如一个数据源)时,你可以使用JndiObjectFactoryBean。
·使用经典Spring AOP为一个Bean创建代理时,可以使用ProxyFactoryBean。
·在IoC容器中创建一个Hibernate会话工厂时,可以使用LocalSessionFactoryBean。
•工作原理:
尽管你很少有必要编写自定义的工厂Bean,但是会发现通过一个实例来理解其内部机制很有帮助。
通过扩展AbstractFactoryBean类,你的工厂bean能够重载createInstance()方法以创建目标Bean实例。
此外,你必须getObjectType()方法中返回目标Bean的类型,是自动装配(Auto-wiring)功能正常工作。
名称之前添加&,可以得到工厂Bean的实例。
package com.partner4java.spring.ioc.factorybean;

import org.springframework.beans.factory.config.AbstractFactoryBean;

import com.partner4java.spring.ioc.required.User;

public class UserFactoryBean extends AbstractFactoryBean<User> {
	private String username;
	private String password;

	public UserFactoryBean(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	@Override
	public Class<?> getObjectType() {
		return User.class.getClass();
	}

	@Override
	protected User createInstance() throws Exception {
		return new User(username, password);
	}

}

	<bean id="userFactoryBean" class="com.partner4java.spring.ioc.factorybean.UserFactoryBean">
		<constructor-arg name="password" value="123" />
		<constructor-arg name="username" value="hello" />
	</bean>

		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/factorybean.xml");
		System.out.println(applicationContext.getBean("userFactoryBean"));
		System.out.println(applicationContext.getBean("&userFactoryBean"));

使用FactoryBean接口:

•当你无法使用new操作符来新建类对象,但又想将他们作为Spring的bean来使用时,FactoryBean是一个完美的解决方案。
•FactoryBean接口声明了3个方法:getObject、getObjectType和isSingleton。
•getObject:方法获取由FactoryBean创建的对象。这是一个真正被传给其他使用FactoryBean作为协作者的bean的对象。
•getObjectType: 告诉Spring你的FactoryBean将返回什么类型的对象。如果事先不知道返回什么类型,将返回null。
•isSingleton:告诉Spring,正在被Spring管理的是否为单例实例。
package com.partner4java.spring.ioc.factorybean;

import java.security.MessageDigest;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

public class MessageDigestFactoryBean implements FactoryBean<MessageDigest>,
		InitializingBean {
	private MessageDigest messageDigest;
	private String algorithm;

	public void setAlgorithm(String algorithm) {
		this.algorithm = algorithm;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.messageDigest = MessageDigest.getInstance(algorithm);
	}

	@Override
	public MessageDigest getObject() throws Exception {
		return (MessageDigest) messageDigest.clone();
	}

	@Override
	public Class<?> getObjectType() {
		return messageDigest.getClass();
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

}

	<bean id="messageDigest" class="com.partner4java.spring.ioc.factorybean.MessageDigestFactoryBean">
		<property name="algorithm" value="MD5"/>
	</bean>

调用静态工厂方法创建Bean:

•问题:你打算调用一个静态工厂方法在Spring IoC容器中创建一个Bean,静态工厂方法的目的是在静态方法中封装对象创建过程。
•解决方案:Spring支持调用一个静态工厂方法创建Bean,这个方法应该在factory-method属性中指定。
•工作原理:factory-method
public class ProductCreator {  
    public static Product createProduct(int productId){  
        if(1 == productId){  
            return new Product("xiaomei", 16);  
        }else if(2 == productId){  
            return new Product("xiaolang", 15);  
        }  
        throw new IllegalArgumentException("Unknown product");  
    }  
}  
  
<bean id="productCreator1" class="com.partner4java.spring.factorymethod.ProductCreator"  
    factory-method="createProduct">  
    <constructor-arg value="1" />  
</bean>  
  
<bean id="productCreator2" class="com.partner4java.spring.factorymethod.ProductCreator"  
    factory-method="createProduct">  
    <constructor-arg value="2" />  
</bean>  
  
@Test  
public void testFactoryMethod(){  
    System.out.println(applicationContext.getBean("productCreator1"));  
    System.out.println(applicationContext.getBean("productCreator2"));  
}  

调用一个实例工厂方法创建Bean:
•问题:你打算调用一个实例工厂方法在Spring IoC容器中创建一个Bean,目的是在另一个对象实例的一个方法中封装对象创建过程。请求对象的客户可以简单地调用这个方法,不需要了解创建的细节。
•解决方案:Spring支持调用实例工厂方法创建Bean。Bean实例在factory-bean属性中指定,而工厂方法应该在factory-method属性中指定。
•工作原理:factory-bean
package com.partner4java.spring.factorybean;

import java.util.Map;

import com.partner4java.spring.factorymethod.Product;

public class ProductCreator {
private Map<String, Product> products;

public void setProducts(Map<String, Product> products) {
this.products = products;
}

public Product createProduct(String productId){
Product product = products.get(productId);
if(product != null){
return product;
}
throw new IllegalArgumentException("Unknown product");
}
}

<bean id="productCreator" class="com.partner4java.spring.factorybean.ProductCreator">
<property name="products">
<map>
<entry key="gaofumei">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg value="gaofumei" name="name"/>
<constructor-arg value="100" name="price"/>
</bean>
</entry>
<entry key="xiaoneinv">
<bean class="com.partner4java.spring.factorymethod.Product">
<constructor-arg value="xiaoneinv" name="name"/>
<constructor-arg value="200" name="price"/>
</bean>
</entry>
</map>
</property>
</bean>

<bean id="gaofumei" factory-bean="productCreator" factory-method="createProduct">
<constructor-arg value="gaofumei"/>
</bean>

<bean id="xiaoneinv" factory-bean="productCreator" factory-method="createProduct">
<constructor-arg value="xiaoneinv"/>
</bean>

@Test
public void testFactoryMethod(){
System.out.println(applicationContext.getBean("gaofumei"));
System.out.println(applicationContext.getBean("xiaoneinv"));

从静态字段中声明bean:
•问题:你打算从一个静态字段中声明Spring IoC容器中的一个Bean。在Java中,常量值往往声明为静态字段。
•解决方案:为了从静态字段中声明Bean,你可以使用内建的工厂Bean FieldRetrievingFactoryBean,或者Spring 2.X中的<util:contant>标记。
工作原理:  
public class ProductConstant {  
    public static Product gaofumei = new Product("gaofumei", 100);  
    public static Product xiaoneinv = new Product("xiaoneinv", 200);  
}  
  
<util:constant id="gaofumei"  
    static-field="com.partner4java.spring.constant.ProductConstant.gaofumei" />  
  
<util:constant id="xiaoneinv"  
    static-field="com.partner4java.spring.constant.ProductConstant.xiaoneinv" />  
  
@Test  
public void testFactoryMethod(){  
    System.out.println(applicationContext.getBean("gaofumei"));  
    System.out.println(applicationContext.getBean("xiaoneinv"));  
}  

从对象属性中声明bean:
•问题:你打算从一个对象属性或者嵌套的属性(也就是属性路径)中声明Spring IoC容器中的一个Bean。
•解决方案:为了从一种对象属性或者属性路径中声明Bean,可以使用内建的工厂Bean PropertyPathFactoryBean或者Spring2.X中的<util:property-path>标记。
工作原理:  
public class ProductProperty {  
    private Product gaofumei;  
    private Product xiaonennv;  
      
    public Product getGaofumei() {  
        return gaofumei;  
    }  
    public void setGaofumei(Product gaofumei) {  
        this.gaofumei = gaofumei;  
    }  
    public Product getXiaonennv() {  
        return xiaonennv;  
    }  
    public void setXiaonennv(Product xiaonennv) {  
        this.xiaonennv = xiaonennv;  
    }  
      
}  
  
<bean id="productProperty" class="com.partner4java.spring.property.ProductProperty">  
    <property name="gaofumei">  
        <bean class="com.partner4java.spring.factorymethod.Product">  
            <constructor-arg name="name" value="gaofumei"/>  
            <constructor-arg name="price" value="100.1"/>  
        </bean>  
    </property>  
    <property name="xiaonennv">  
        <bean class="com.partner4java.spring.factorymethod.Product">  
            <constructor-arg name="name" value="xiaonennv"/>  
            <constructor-arg name="price" value="200.2"/>  
        </bean>  
    </property>  
</bean>  
  
<util:property-path id="gaofumei" path="productProperty.gaofumei"/>  
<util:property-path id="xiaonennv" path="productProperty.xiaonennv"/>  

好吧,亲,感觉如何?好玩么?


下面,我们来看下IoC的高级形态(其实也不是很高级)


Spring对应用程序可移植性的影响:

•不要凭空杜撰对可移植性的需求。在许多情况下,终端用户并不关心你的应用程序是否能在3个不同的控制反转容器上运行,而只要求他能稳定运行。

使Bean感知容器:

•问题:一个精心设计的组件应该没有对容器的直接依赖。但是,有时候Bean有必要了解容器的资源。
•解决方案:Spring将通过一些接口定义的设置方法将对应资源注入到你的Bean中。
•Spring中的常见感知接口
•感知接口目标资源
•BeanNameAwareIoC容器中配置的实例的Bean名称
•BeanFactoryAware当前的Bean工厂,通过它你可以调用容器的服务。
•ApplicationContextAware*当前应用上下文,通过他你可以调用容器的服务。
•MessageSourceAware消息资源,通过他可以解析文本消息。
•ApplicationEventPublisherAware应用事件发布者,通过他你可以发布应用事件。
•ResourceLoaderAware 资源装载器,通过他可以加载外部资源。
工作原理:  
public class HelloBeanNameAware implements BeanNameAware {  
    private String name;  
      
    @Override  
    public void setBeanName(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public String toString() {  
        return "HelloBeanNameAware [name=" + name + "]";  
    }  
}  
  
public class HelloBeanFactoryAware implements BeanFactoryAware {  
    private BeanFactory beanFactory;  
      
    @Override  
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {  
        this.beanFactory = beanFactory;  
    }  
  
    @Override  
    public String toString() {  
        return "HelloBeanFactoryAware [beanFactory=" + beanFactory.getClass().getSimpleName() + "]";  
    }  
  
}  
  
public class HelloApplicationContextAware implements ApplicationContextAware {  
    private ApplicationContext applicationContext;  
      
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext)  
            throws BeansException {  
        this.applicationContext = applicationContext;  
    }  
  
    @Override  
    public String toString() {  
        return "HelloApplicationContextAware [applicationContext="  
                + applicationContext.getClass().getSimpleName() + "]";  
    }  
      
}  
  
  
<bean id="helloBeanNameAware" class="com.partner4java.spring.aware.HelloBeanNameAware" />  
  
<bean id="helloBeanFactoryAware" class="com.partner4java.spring.aware.HelloBeanFactoryAware" />  
  
<bean id="helloApplicationContextAware" class="com.partner4java.spring.aware.HelloApplicationContextAware" />  
  
@Test  
public void testAware() throws InterruptedException{  
    System.out.println(applicationContext.getBean("helloBeanNameAware"));  
    System.out.println(applicationContext.getBean("helloBeanFactoryAware"));  
    System.out.println(applicationContext.getBean("helloApplicationContextAware"));  
    // 后台打印:  
    // HelloBeanNameAware [name=helloBeanNameAware]  
    // HelloBeanFactoryAware [beanFactory=DefaultListableBeanFactory]  
    // HelloApplicationContextAware  
    // [applicationContext=ClassPathXmlApplicationContext]  
}     

管理bean的生命周期:

•两个生命周期时间与bean关系尤为重要:
•postinitialization(初始化后)和predestruction(销毁前)。
•注意:单例和非单例的bean加载方式不同,所以,初始化执行时间也会不同,lazy-init="true"还可以控制单例的加载机制
方式一:XML

•1、自己写个初始化和销毁的方法
package com.partner4java.spring.ioc.life;

public class SimpleBean {
	public void init(){
		System.out.println("SimpleBean init");
	}
	
	public void des(){
		System.out.println("SimpleBean des");
	}
}

•2、告诉XML
	<bean id="simpleBean" class="com.partner4java.spring.ioc.life.SimpleBean"
		init-method="init" destroy-method="des" lazy-init="true"/>

方式二:实现接口

实现InitializingBean接口:

•Spring中InitializingBean接口允许你在bean的代码中这样定义:你希望bean能接收到Spring已经完成对其配置的通知。就像使用初始化方法时一样,你可以借机检查bean的配置以确保其有效,若要必要,还可以给出默认值。

决议顺序:

•Spring首先调用InitializingBean.afterPropertiesSet()方法,然后再调用初始化方法。(建议如果存在顺序,都写入到afterPropertiesSet中调用。)

afterPropertiesSet方法和类的继承:

•例子抽象父类实现了接口,但是把afterPropertiesSet定义成了final,不可被子类覆盖,也就是去实现一些通用的初始化,然后再调用了自己定义的initSimple()初始化方法。

实现DisposableBean接口

•DisposableBean接口提供了destory方法

public class Work implements InitializingBean,DisposableBean {  
  
    @Override  
    public void destroy() throws Exception {  
        System.out.println("离职");  
    }  
      
    public void vork(){  
        System.out.println("working");  
    }  
  
    @Override  
    public void afterPropertiesSet() throws Exception {  
        System.out.println("入职");  
    }  
  
}  
<bean id="work" class="com.partner4java.spring.initdes.Work"  
        scope="prototype" />  
  

创建Bean后处理器:

(具体可参照:http://blog.csdn.net/partner4java/article/details/6973782)

问题:
你希望在Spring IoC容器中注册自己的插件,在构造期间处理Bean实例。

解决方案:
Bean后处理器允许在初始化回调方法前后进行附加的Bean处理。
Bean后处理器的主要特性是逐个处理IoC容器中的所有Bean实例,而不是单个Bean实例。
一般,Bean后处理器用于检查Bean属性有效性,或者根据特殊条件修改Bean属性。

Bean后处理器的基本要求是实现BeanPostProcessor接口。
你可以实现postProcessBeforeInitialization()和postProcessAfterInitialization()方法,在初始化回调方法前后处理所有Bean。
然后,Spring将在调用初始化回调方法前后向这两个方法传递每个Bean实例。
步骤如下:
1、构造程序或者工厂方法创建Bean实例。
2、为Bean属性设置值和Bean引用。
3、调用感知接口中定义的设置方法。
4、将Bean实例传递给每个Bean前置处理器中的postProcessBeforeInitialization方法。
5、调用初始化回调方法。
6、讲Bean实例传递给每个Bean后处理器中的postProcessAfterInitialization方法。
7、Bean准备就绪,可以使用。
8、容器关闭时,调用析构回调方法。
使用Bean工厂为IoC容器时,Bean后处理器只能编程注册,更准确的讲是通过addBeanPostProcessor()方法注册。
但是,如果你使用一个应用上下文,注册将很简单,只要在Bean配置文件中声明处理器实例,他就会自动注册。

工作原理:

public class LogBeanPostProcessor implements BeanPostProcessor {  
  
    @Override  
    public Object postProcessBeforeInitialization(Object bean, String beanName)  
            throws BeansException {  
        System.out.println(beanName + " say hello world!");  
        return bean;  
    }  
  
    @Override  
    public Object postProcessAfterInitialization(Object bean, String beanName)  
            throws BeansException {  
        System.out.println(beanName + " say good buy!");  
        return bean;  
    }  
  
}  
  
<!-- 要在应用上线文中注册一个Bean后处理器,只要在Bean配置文件中声明他的一个实例就可以了。 应用上下文能够自动检测谁实现了BeanPostProcessor接口,并且注册他一处理容器中的所有其他Bean实例 -->  
<bean class="com.partner4java.spring.postprocess.LogBeanPostProcessor" />  
  
<bean id="helloResourceLoader" class="com.partner4java.spring.resource.HelloResourceLoader"  
    init-method="showResource" />  
          
如果以配置文件的格式设置init-method,对BeanPostProcesser的执行没有什么威胁,BeanPostProcesser还是会先执行。  
但是如果,以@PreDestroy和@PostConstruct的形式,BeanPostProcesser讲不能正常工作,因为BeanPostProcesser的默认优先级低于CommonAnnotationBeanPostProcesser。  
不过可以同时实现PriorityOrdered接口来指定执行顺序。        

Spring BeanFactoryPostProcessor类 (“排队”“后”控制修改beanfactory管理的信息--如一些配置信息)

http://blog.csdn.net/partner4java/article/details/6969417

(比较晚了,明早还有事,后面可能整理的有点乱,改天再修改下)


《partner4java 讲述Spring入门》之第一步:Spring概述与Spring IoC
http://blog.csdn.net/partner4java/article/details/8194747

《partner4java 讲述Spring入门》之第二步:Spring AOP
http://blog.csdn.net/partner4java/article/details/8239721


分享到:
评论

相关推荐

    Spring源代码解析(一):IOC容器.doc

    Spring源代码解析(一):IOC容器.doc

    JavaEE spring IoC入门案例

    JavaEE spring IoC入门案例

    Spring概述与IOC.docx

    Spring概述与IOC,对于刚入门的工程师有用

    Spring中IoC优点与缺点解析

    主要为大家详细解析了Spring中IoC优点与缺点,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    springIOC核心组件分析.vsdx

    spring-beans:Bean工厂与装配 spring-context:上下文,即IOC容器 spring-context-support:对IOC的扩展,以及IOC子容器 spring-context-indexer:类管理组件和Classpath扫描 spring-expression:表达式语句 切面编程: ...

    Spring技术内幕:深入解析Spring架构与设计原理

    《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》从源代码的角度对Spring的内核和各个主要功能模块的架构、设计和...第三部分讲述了ACEGI安全框架、DM模块以及Flex模块等基于Spring的典型应用的设计与实现。

    Java-Spring-SpringIoC容器-SpringIoC的学习

    Java-Spring-SpringIoC容器-SpringIoC的学习 SpringIoC容器的学习笔记 主要介绍了IoC容器工作原理以及如何配置IoC容器 通过标签和注解配置容器

    springIOC手写框架分析

    springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC手写框架分析springIOC...

    spring ioc模块手写demo

    spring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demospring ioc模块手写demo...

    Spring的IoC容器(《Spring揭秘》的精选版)

    迷你书是《Spring揭秘》的精选版,节选了原书中介绍Spring IoC容器的6个章节。《Spring揭秘》以幽默生动的语言、辅以有趣的故事和典故,循循善诱地阐述了Spring框架的方方面面。针对Spring框架的主要功能以及开发者...

    springIoc实现原理

    spring ioc指的是控制反转,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由Spring容器统一进行管理,从而实现松耦合

    Spring技术内幕:深入解析 Spring架构与设计原理.pdf

    第三部分讲述了ACEGI安全框架、DM模块以及Flex模块等基于Spring的典型应用的设计与实现。 无论你是Java程序员、Spring开发者,还是平台开发人员、系统架构师,抑或是对开源软件源代码着迷的代码狂人,都能从本书中...

    JAVA spring 系列案例50个和学习资料

    Spring系列第2篇:控制反转(IoC)与依赖注入(DI)。Spring系列第3篇:Spring容器基本使用及原理。Spring系列第4篇:xml中bean定义详解(-)Spring系列第5篇:创建bean实例这些方式你们都知道?Spring系列第6篇:玩转...

    Spring_IoC入门笔记.md

    第一种:使用构造函数提供(创建对象时,必须提供数据,否则无法创建成功) 使用的标签:constructor-arg 标签出现的位置:bean标签的内部 标签中的属性: type:用于指定要注入的数据的数据类型,该数据类型也是...

    Java反射_Spring IOC

    《Java语言的反射机制.pdf》资料书,示例源代码JAVA+Spring,初学java反射者可以看看

    Spring技术内幕:深入解析Spring架构与设计原理(第2部分)

    《Spring技术内幕:深入解析Spring架构与设计原理》是Spring领域的问鼎之作,由业界拥有10余年开发经验的资深Java专家亲自执笔!Java开发者社区和Spring开发者社区一致强烈推荐。国内第一本基于Spring3.0的著作,从...

    Spring源码分析_Spring_IOC

    Spring源码分析_Spring_IOC

    SpringIoc示例代码

    SpringIoc示例代码,SpringIoc示例代码,SpringIoc示例代码,SpringIoc示例代码

    spring ioc和aop原理流程图(详细)

    spring ioc容器初始化流程图 spring ioc容器依赖注入流程图 spring aop实现原理流程图

Global site tag (gtag.js) - Google Analytics