spring 随笔(一) bean & Dependency Injection

1,一切都是Bean

Bean是Spring应用程序中(其实是Spring容器中)的基本元素,在Spring框架上运行的应用程序,就是由一个又一个的Bean组合在一起、像搭积木一样堆出来的。所有的Bean都由Spring的核心容器负责管理、创建、销毁,同时Bean之间的相互依赖也由Spring容器中的依赖注入功能自动管理。
1.1bean的作用域
  • lsingleton:定义bean的范围为每个Spring容器一个实例(默认值)
  • lprototype:定义bean可以被多次实例化(使用一次就创建一次)
  • lrequest:定义bean的范围是HTTP请求,只有再使用有web能力的spring上下文时有效。
  • lsession:定义bean的范围是HTTP会话,只有再使用有web能力的spring上下文时有效。
  • lglobal-session:定义bean的范围是全局HTTP会话,只有再portlet上下文中有效。
1.2 三种实例化bean的方式
  • 使用构造器实例化(默认无参数)

<bean id="person" class="com.xiaohui.domain.Person"/>

  • 使用静态工厂方法实例化(简单工厂模式)

 <bean id="person" class="com.xiaohui.app.create.PersonFactory" factory-method="getPersonInstance"/>

public class PersonFactory {
	public static Person getPersonInstance(){
		return new Person();
	} 
}
  • 使用实例工厂方法实例化(工厂方法模式)

<bean id="personFactory" class="com.xiaohui.app.create.PersonFactory" /> 

<bean id="person" factory-bean="personFactory" factory-method="createtPersonInstance"/>

public class PersonFactory{
	public  Person createtPersonInstance(){
		return new Person();
	}
}
 
1.3 指定Bean的初始化方法和销毁方法

spring初始化bean或销毁bean时有时候需要执行一些处理工作,因此spring可以在创建或者销毁bean时可以调用bean的两个生命周期方法

<bean id="personService" class="com.xiaohui.service.PersonServiceImpl"
   init-method="init"
   destroy-method="distory" scope="singleton" />

(注意的是只有scope=singleton的bean,destroy-method才有效)

1.4 bean 的生命周期

  1. 实例化bean
  2. 设置属性值
  3. 设置bean名称(如果bean实现了BeanNameAware接口)
  4. 设置beanFactory(如果bean实现了BeanFactoryAware接口)
  5. 设置ApplicationContext(如果bean实现了ApplicationContextAware接口)
  6. 前预处理(如果上下文中存在多个BeanPostProcessor,将调用其postProcessBeforeInitialization()方法)
  7. 初始化Bean(如果bean实现了InitializingBean,将调用其afterPropertiesSet方法,如果bean声明了自定义初始化方法,调用)
  8. 后预处理(如果上下文中存在多个BeanPostProcessors,将调用其postProcessAfterInitialization()方法)
  9. Bean创建完成,待用
  10. 销毁Bean(如果bean实现了DisposableBean,调用其destroy()方法)
  11. 销毁Bean(如果bean声明了自定义的销毁方法,调用)
1.5 bean的继承
在spring的继承中不论在java类的实际情况中是否存在真正的extends继承关系,只要其存在引用相同的属性值,在spring的bean管理都可以将其抽象为继承关系。在spring的xml配置中可将引用相同属性的属性property抽出一个父类bean。该bean需要设计其abstract属性为true。其他“继承”该bean的bean需要制定其parent属性值为该抽象父类的id。如下在studentDao和teacherDao中都存在属性sf(SessionFactory),则可以抽出一个父类。
<bean id="sessionFactory" class="com.xiaohui.test.SessionFactory" />
<bean id="baseDao" abstract="true">
	<property name="sf" ref="sessionFactory" />
</bean>

<bean id="studentDao" class="com.xiaohui.service.impl.StudentDaoImpl" parent="baseDao" />
<bean id="teacherDao" class="com.xiaohui.service.impl.TeacherDaoImpl" parent="baseDao" />

2,依赖注入(Dependency Injection)

所谓的依赖注入就是在于行期间,由外部容器动态的将依赖对象注入到组件中。通俗的讲也就是不在程序中显示的调用该类的setter()方法去设置该类的属性。而是由spring容器处理经过程序员配置的xml来设置该类的属性值。

所谓的控制反转(Inversion of Control)就是指应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转,目的是为了获得更好的扩展性和良好的可维护性。

通常的依赖注入有手工装配(配置xml)和自动装配(基于注解)的两种方式方式:

2.1通过setter方法注入依赖
  • <bean>元素的<property>子元素指明了使用该bean的setter方法来注入

    1.可以使用property的value属性来进行简单类型的注入

<bean id="person" class="com.xiaohui.domain.Person">
    	<property name="id" value="1"/>
    	<property name="name" value="张三丰"/>
</bean>

2.使用ref引用其他的bean 

<bean id="person" class="com.xiaohui.bean.Person" />
<bean id="personService" class="com.xiaohi.bean.impl.PersonServiceImpl">
	<!-- 引用类型 -->
	<property name="person" ref="person" />
</bean>

3.内部bean

<bean id="personService" class="com.xiaohui.bean.impl.PersonServiceImpl">
	<!-- 内部bean注入 -->
	<property name="person">
		<bean class="com.xiaohui.bean.Person" />
	</propert>
</bean>

使用该方式的缺点就是无法在其他地方使用这个类,该类只能为外部的bean使用。

4.装配集合

	<!-- 装配list -->
	<property name="lists">
		<list>
			<value>list1</value>
			<ref bean="person" />
		</list>
	</property>
	<!-- 装配数组 -->
	<property name="obj">
		<list>
			<value>obj1</value>
			<ref bean="person" />
		</list>
	</property>
	<!-- 装配set 
		set使用方法和list一样,不同的是对象被装配到set中,而list是装到 List或数组中装配
	 -->
	<property name="sets">
		<set>
			<value>set1</value>
			<value>set2</value>
			<ref bean="person" />
		</set>
	</property>
	<!-- 装配map 
		map中的<entry>的数值和<list>以及<set>的一样可以使任何有效的属性元素,需要注意的是key值必须是String的。
	 -->
	<property name="maps">
		<map>
			<entry key="01" value="map01" />
			<entry key="02" value="map02" />
			<entry key-ref="refkeybean" value-ref="refvaluebean">
		</map>
	</property>
	<property name="props">
		<props>
			<prop key="01">prop1</prop>
			<prop key="02">prop2</prop>
		</props>
	</property>
	<!-- 简化写法 -->
	<property name="props">
		<value>
			01=prop1
			02=prop2
	</value>
	</property>

5.设空值null

<!-- 装配null -->
 <property name="listnull">
     <null/>
 </property>


6,自定义String2Object类型转换器来设置属性

在该属性类的同一包下面创建类 名称 PropertyClassNameEditor,并继承PropertyEditorSupport类,复写其setAsText(String text)方法即可。

例如给Family类的Person类属性创建一个String2Person类的类型转换器。

public class PersonEditor extends PropertyEditorSupport {
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		if(text!=null){
			String[] props = text.split("-");
			if(props!=null && props.length == 3){
				Person p = new Person();
				p.setId(Long.parseLong(props[0]));
				p.setName(props[1]);
				p.setMail(props[2]);
				this.setValue(p);
			}
		}
	}
}

Family类:

public class Family {
	private Person master;
	public void setMaster(Person master) {
		this.master = master;
	}
	public void sayMaster(){
		System.out.println(master);
	}
}

Person类:

public class Person {
	private Long id;
	private String name;
	private String mail;
	
	public String getMail() {
		return mail;
	}
	public void setMail(String mail) {
		this.mail = mail;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", mail=" + mail + "]";
	}
}

则可以这样给Family 类注入Person类的master属性值

<bean id="family" class="com.xiaohui.domain.Family">
	<property name="master" value="3-jack-jack@163.com"/>
</bean> 
 
7,通过加载properties文件为bean属性设值
需要引入context命名空间:xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	 	xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
    <context:property-placeholder location="classpath:jdbc.properties"/>
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
jdbc.properties内容如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring2
jdbc.username=root
jdbc.password=root

2.2.  通过构造函数注入依赖

Person类:

package com.xiaohui.domain;

public class Person {
	private Long id;
	private String name;
	private String mail;
	
	public Person() {}
	public Person(Long id, String name, String mail) {
		this.id = id;
		this.name = name;
		this.mail = mail;
	}
	public String getMail() {
		return mail;
	}
	public void setMail(String mail) {
		this.mail = mail;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", mail=" + mail + "]";
	}
}

 

1.可以按照参数的类型注入,如:

<bean id="person" class="com.xiaohui.domain.Person">
    	<constructor-arg type="Long" value="2" />
    	<constructor-arg type="String" value="herry" />
    	<constructor-arg type="String" value="herry@qq.com" />
</bean>

当一种类型有多个的种情况下其按照构造函数的参数顺序注入。如上,其第二个constructor-arg标签注入的为Person的name属性。

2.可以按照参数的name进行注入,如:

 <bean id="person" class="com.xiaohui.domain.Person">
    	<constructor-arg name="id" value="2" />
    	<constructor-arg name="name" value="herry" />
    	<constructor-arg name="mail" value="herry@qq.com" />
 </bean>

其中name的值为该类的构造方法的参数名。正常情况下,通过软件自动生成的构造方法其参数名和对应的属性名是一样的。

3.可以按照索引(构造函数的参数顺序)进行注入

<bean id="person" class="com.xiaohui.domain.Person">
    	<constructor-arg index="0" value="2" />
    	<constructor-arg index="1" value="herry" />
    	<constructor-arg index="2" value="herry@qq.com" />
</bean> 

2.3 通过自动装配的方式进行注入

通过设置bean标签中的autowire属性。

autowire的取值为:byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。

                               byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。

                              constructor:与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。

byType

autowire-candidate:可选值为 true|false  当autowire匹配时有多个类型的时候,使用该属性可以表明该bean是否为优先的候选bean 通常设置哪些bean不为优先候选的

<bean id="util" class="com.xiaohui.myinject.DbUtil" />
<bean id="util2" class="com.xiaohui.myinject.DbUtil" autowire-candidate="false" />
<bean id="dao" class="com.xiaohui.myinject.PersonDaoImpl" autowire="byType"/>
<bean id="personService" class="com.xiaohui.myinject.PersonServiceImpl" autowire="byType"/>

 byName bean的id要和依赖该bean的类中的属性名一样.

<bean id="util" class="com.xiaohui.myinject.DbUtil" />
<bean id="dao" class="com.xiaohui.myinject.PersonDaoImpl" autowire="byName"/>
<bean id="personService" class="com.xiaohui.myinject.PersonServiceImpl" autowire="byName"/>

2.4 通过注解方式进行装配

在使用注解方式进行注入之前呢,必须现在spring配置文件applicationContext.xml中引入context命名空间

再在配置文件中加入<context:annotation-config/>标签

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      	 http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	
	<context:annotation-config/>
	<bean id="util" class="com.xiaohui.myinject2.DbUtil" />
	<bean id="personDao" class="com.xiaohui.myinject2.PersonDaoImpl" />
	<bean id="personService" class="com.xiaohui.myinject2.PersonServiceImpl" />
</beans>

常用的注解说明:

@Autowire   &  @Resource  & @Qualifier

@Autowire和@Resource的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
相同的是:都可以直接使用在Field上或者setter方法之上。
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。
@Resource可以通过设置name属性指定注入bean的名称,但一旦指定了name属性,就只能按名称装配了
如果我们想使用按名称装配,可以让@Autowired结合@Qualifier注解一起使用。
 
对应Spring配置文件中的init-method和destory-method,在bean中,可以直接使用@PostConstruct和@PreDestory标签来标记生命周期方法。
public class CachingMovieLister {
  @PostConstruct
  public void populateMovieCache() {
  }
  @PreDestroy
  public void clearMovieCache() {
  }
}
相当于:
<bean id="cachingMovieLister" class="CachingMovieLister" init-method="populateMovieCache" destory-method="clearMovieCache" />
 
更为简洁的bean管理方式:classpath自动扫描
在上面的配置中我们还需要在spring的xml文件中配置bean信息。spring为我们提供了另一种更为简洁的bean管理和依赖注入的的方式,成为classpath自动扫描。
使用@Component,@Controller,@Service,@Repository注释我们需要被spring管理的组件类
@Controller用于标注控制层组件(如struts中的action)。
@Service用于标注业务层组件。
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
这时我们在xml文件中只需要加入<context:component-scan base-package="com.xiaohui.package"/>,这时我们就不用在xml中配置 base-package包下面的所有bean了。
 
xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      	 http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<context:annotation-config/>
	<context:component-scan base-package="com.xiaohui.myinject2"/>       
</beans>
java类如下:
Dbutil类:
package com.xiaohui.myinject2;
import org.springframework.stereotype.Component;
@Component
public class DbUtil {
	public void save(){
		System.out.println("save object in dbutil....");
	}
}
 PersonDaoImpl类:
package com.xiaohui.myinject2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class PersonDaoImpl {
	private DbUtil util;
	@Autowired
	public void setUtil(DbUtil util) {
		this.util = util;
	}
	public void save(){
		System.out.println("save in PersonDaoImpl....");
		util.save();
	}
}
 PersonServiceImpl类:
package com.xiaohui.myinject2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceImpl {
	private PersonDaoImpl dao;
	@Autowired
	public void setDao(PersonDaoImpl dao) {
		this.dao = dao;
	}
	public void save(){
		System.out.println("PersonServiceImpl save user.....");
		dao.save();
	}
}
PersonActionPersonActionPersonAction类:
package com.xiaohui.myinject2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class PersonAction {
	private PersonServiceImpl service;
	@Autowired
	public void setService(PersonServiceImpl service) {
		System.out.println("save in action.....");
		this.service = service;
	}
	public void execute(){
		service.save();
	}
}
测试类:
package com.xiaohui.app;

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

import com.xiaohui.myinject2.PersonAction;

public class AppTest {
	@Test
	public void testSpring() throws Exception {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
		PersonAction service = ctx.getBean(PersonAction.class);
		service.execute();
	}
}

打印结果:
save in action.....
PersonServiceImpl save user.....
save in PersonDaoImpl....
save object in dbutil....
Editorial Reviews Product Description Dependency Injection is an in-depth guide to the current best practices for using the Dependency Injection pattern-the key concept in Spring and the rapidly-growing Google Guice. It explores Dependency Injection, sometimes called Inversion of Control, in fine detail with numerous practical examples. Developers will learn to apply important techniques, focusing on their strengths and limitations, with a particular emphasis on pitfalls, corner-cases, and best practices. This book is written for developers and architects who want to understand Dependency Injection and successfully leverage popular DI technologies such as Spring, Google Guice, PicoContainer, and many others. The book explores many small examples of anchor concepts and unfolds a larger example to show the big picture. Written primarily from a Java point-of-view, this book is appropriate for any developer with a working knowledge of object-oriented programming in Java, Ruby, or C#. About the Author Dhanji R. Prasanna is an Enterprise Java consultant for technologies such as EJB3, JBI, JSF, Guice, Spring, HiveMind, and PicoContainer. He is a co-author of the Bean Validation (JSR-303), JAX-RS (JSR-311), Servlet 3.0 (JSR-315), and JavaServerFaces 2.0 (JSR-314) specifications. He is also co-author of the Java EE 6.0 (JSR-316) platform specification, which is the next edition of J2EE. Product Details * Paperback: 352 pages * Publisher: Manning Publications; 1 edition (August 28, 2009) * Language: English * ISBN-10: 193398855X * ISBN-13: 978-1933988559 * Product Dimensions: 9.1 x 7.4 x 0.8 inches
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值