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 的生命周期

spring 随笔(一) bean & Dependency Injection_第1张图片

  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="[email protected]"/>
</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="[email protected]" />
</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="[email protected]" />
 </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="[email protected]" />
</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....

你可能感兴趣的:(spring,bean,DI,IOC)