(导读:希望在阅读的过程中,伴随着动手 -- 把讲述的每一步Demo都自己书写一遍,若遇到问题一时解决不了,可发邮件给我:partner4java@163.com;demo下载地址--http://download.csdn.net/detail/partner4java/4779657;也可以结合视频:http://pan.baidu.com/share/link?shareid=122408&uk=4096755201 ,视频说实话应该不咋地,是第一次录制,不存在各种讲课技巧)
第一步:扯蛋
下面紧跟的很长一段,会讲一些“大道理”,就当天书看一眼吧
begin….
Spring的设计目标 :
Spring架构图:
天书告一段落
现在Spring不都分包了么?
大体看下包的作用
哥,我走了,别说了,吓到我了,东西太多了
亲,别怕,其实Spring就那十个左右的注解就完事了
牛B的框架不会太难用,牛B的代码不会太难懂。我们要跻身于那批能写牛B的代码人群中。。。
第二步,简易IoC:
控制反转IoC
begin:
控制反转和依赖注入:
不依赖Spring,我们先简单的实现下控制反转:
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:
这俩简单例子所用就是真实的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();
}
}
基础类:
小结(白话):
再议Spring IoC,前面我们简单的helloworld了下,先有个直观的认识 下面我们继续IoC (通过这几个demo是不是对IoC有些直观的认识了?)
控制反转类型:
依赖拖拽HelloWorld:
上下文依赖查找(CDL):
依赖注入方式:
在构造方法依赖注入中,组件的依赖从他们的构造方法中提供。组件声明一个或一组构造方法,将他们的依赖作为参数,控制反转容器就会降依赖在组件实例化时传给他。
在设置方法依赖注入中,控制反转容器通过JavaBean风格的设置方法为组件注入依赖。一个组件的设置方法向反转容器公开一组依赖。
XmlBeanFactory:
我们前面利用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();
}
}
叨叨两句配置文件:
再来俩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配置集合:
为集合元素指定数据类型 :
默认情况下,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的实例化模式:
非单例实例化模式:
选择一种实例化模式:
使用非单例:
亲,IoC知道是啥了么? 下面俩小时我们来具体说下各种基本使用 begin…
用依赖检查属性:
解决方案(依赖检查):
用@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"));
(插曲)引入ApplicationContext:
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:
替换@Required:
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;
}
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor">
<property name="requiredAnnotationType" value="com.partner4java.spring.ioc.required.NotNull"/>
</bean>
这样Spring就会自动扫描这个配置,知道你加了一个自定义注解
解析依赖:
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>元素指定一个值。
自动装配:
用XML配置自动装配Bean:
当一个Bean需要访问另一个Bean时,你可以显示指定引用装配他。但是,如果你的容器能够自动装配bean,就可以免去手工手工配置装配的麻烦。
autowire属性--
no* 不执行自动装配。你必须显示的装配依赖
byName 对于每个Bean属性,装配一个同名的bean
byType 对于每个Bean属性,装配类型与之兼容的Bean。如果超过一个,将抛出UnsatisfiedDependencyException异常。
Constructor 对于每个构造程序参数,首先寻找与参数兼容的Bean。然后,选择具有最多参数的构造程序。对于存在歧义的情况,将抛出UnsatisfiedDependencyException异常。
autoetect 如果找到一个没有参数的默认构造程序,依赖讲按照类型自动装配。否则,将由构造程序自动装配。
<?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属性之外,还有一个能够满足需求的选择。
@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提供了一个强大的功能--组件扫描
<?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>
第二步:给自动扫描的文件加上注解
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>
此外,在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
会隐式地被包括进来。 也就是说,连个组件都会被自动检测并织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。
注意
通过加入值为'false'的annotation-config属性可以禁止注册这些后置处理器。
使用过滤器自定义扫描
默认情况下,用@Component
、 @Repository
、@Service
或 @Controller
(或本身使用了@Component
注解的自定义注解) 注解的类是唯一会被检测到的候选组件。但是可以很方便地通过自定义过滤器来改变并扩展这一行为。 可以用'component-scan
'的include-filter或exclude-filter子元素来进行添加。 每个过滤器元素都要求有'type
'和'expression
'属性。 下面给出了四个已有的可选过滤器。
表3.7.过滤器类型
过滤器类型 | 表达式范例 |
---|---|
annotation |
|
assignable |
|
regex | org\.example\.Default.* |
aspectj |
|
下面这个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接口:
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:
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:
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:
工作原理:
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:
工作原理:
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对应用程序可移植性的影响:
使Bean感知容器:
工作原理:
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的生命周期:
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");
}
}
<bean id="simpleBean" class="com.partner4java.spring.ioc.life.SimpleBean"
init-method="init" destroy-method="des" lazy-init="true"/>
方式二:实现接口
实现InitializingBean接口:
决议顺序:
afterPropertiesSet方法和类的继承:
实现DisposableBean接口
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