Spring的学习(五):Spring容器中的Bean
Spring的学习(五):Spring容器中的Bean
yuhuan121 发表于4个月前
Spring的学习(五):Spring容器中的Bean
  • 发表于 4个月前
  • 阅读 1
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

1、Bean的定义和别名

对于开发者而言,使用Spring主要是做两件事情:开发Bean和配置Bean; <beans../>元素是Spring配置文件的根元素,可以指定以下属性:

  • lazy-init:指定该<beans../>元素下配置的所有Bean默认的_延迟初始化行为_;
  • merge:指定该<beans../>元素下配置的所有Bean默认的_merge行为_;
  • autowire:指定该<beans../>元素下配置的所有Bean默认的_自动装配的行为_;
  • autowire-candidates:指定该<beans../>元素下配置的所有Bean默认_是否作为自动装配的候选Bean_;
  • init-method:指定该<beans../>元素下配置的所有Bean默认的_初始化方法_;
  • destroy-method:指定该<beans../>元素下配置的所有Bean默认的_回收方法_;

<bean../>元素是<beans../>元素的子元素,可以包含很多个子元素。每个子元素定义一个Bean,每个Bean对应Spring容器里的一个java实例。

<beans../>元素可以给<bean../>元素指定属性,当两者属性值不一样时,后者的属性会覆盖前者指定的属性。

<bean../>元素包括两个id、class属性:id属性遵循XML文档的id属性规则,因此有一些特殊要求,比如不能以“/"等特殊符号作为属性值。此时name属性就出场了,可以用来指定Bean的别名,通过访问别名也可以访问Bean实例。

指定别名有两种方式:

  1. 定义<bean../>元素通过name指定别名:同上;但是当Bean实例指定多个别名时,可以用逗号、冒号、空格来分隔多个别名;
  2. 当程序无法在定义Bean时指定所有的别名,需要在其他地方指定时,用<alias../>元素,也包含两个属性:
  • name:指定一个已有的Bean实例的标识名
  • alias:指定一个别名
//为Bean指定了三个别名
<bean id = "person"  class = "..."  name = "#abc,@123,abc*"/>
//下面两行为已有的personBean指定别名
<alias name = "person" alias = "jack"/>

2、Bean的作用域

当通过Spring容器创建Bean实例是我,不仅完成其实例化,还能为其指定特定的作用域。

Spring支持下面5中作用域:

  • singleton:单例模式,在整个SpringIoC容器中,singleton作用域的Bean将只生成一个实例;
  • prototype:原型模式。每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。
  • request:对于每次HTTP请求,使用request定义的Bean都将只产生一个实例,即每次HTTP请求都会产生同一个Bean实例。当然只有在WEB应用中使用Spring时,该作用域才真正有效。
  • session:对于每次HTTP会话,使用session定义的Bean都将只产生一个实例,即每次HTTP Session都将产生同一个的Bean实例。当然只有在WEB应用中使用Spring时,该作用域才真正有效。
  • global session:每个全局的HTTP Session对应一个Bean实例。仅在portlet Context的时候才有效。当然只有在WEB应用中使用Spring时,该作用域才真正有效。

常用的是singleton和prototype作用域。_spring配置文件用scope属性指定Bean的作用域。_如果不指定Bean的作用域,spring默认是singleton作用域。由于singleton作用域的实例一旦被创建成功,就可以重复使用。但是prototype每次都产生一个新的实例。Java在创建Java实例时,会进行内存申请;销毁实例时,会完成垃圾回收,这都会导致系统开销的增加。prototype作用域的Bean创建,销毁的代价比较大,所有应避免将Bean设置prototype作用域。

3、创建Bean的方式

Spring支持使用3种方式:

3.1、调用构造器

  • 不采用构造注入,spring底层会调用Bean类的无参构造器来创建实例;
  • 采用构造注入,spring底层会调用Bean类的带有对应参数的构造器来创建实例;

3.2、 调用静态工厂方法

<bean../>元素包括以下两个属性:

  • class:不再是指定Bean类的实现类,而是静态工厂类名,Spring由此知道哪个工厂类来创建Bean实例;
  • factory-method:指定静态工厂方法来生产Bean实例

当静态工厂方法需要参数时候,用<constructor-arg../>传入

代码实现

3.2.1、定义一个Being接口

静态工厂方法生产的产品都是这个接口的实例

package org.crazyit.app.service;
public interface Being{
	public void testBeing();
}

3.2.2、定义该接口的两个实现类

静态工厂方法将会产生这两个实现类的实例

package org.crazyit.app.service.impl;

import org.crazyit.app.service.Being;

public class Dog implements Being{
	private String msg;
	public void setMsg(String msg){
		this.msg = msg;
	}
	//实现该接口的方法
	public void testBeing() {
		System.out.println(msg + ",狗爱啃骨头!");		
	}
}
package org.crazyit.app.service.impl;

import org.crazyit.app.service.Being;

public class Cat implements Being{
	private String msg;
	public void setMsg(String msg){
		this.msg = msg;
	}
	//实现该接口的方法
	public void testBeing() {
		System.out.println(msg + ",猫爱吃鱼!");		
	}
}

3.2.3、创建静态工厂类

下面的getBeing()方法用于返回一个Being实例

package org.crazyit.app.factory;

import org.crazyit.app.service.Being;
import org.crazyit.app.service.impl.Cat;
import org.crazyit.app.service.impl.Dog;

public class BeingFactory{
	//返回Being实例的静态工厂方法,arg参数决定返回哪个实例
	public static Being getBeing(String arg){
		// 调用此静态方法的参数为dog,则返回Dog实例
		if(arg.equalsIgnoreCase("dog")){
			return new Dog();
		}else{
			return new Cat();
		}
	}
}

3.2.4、配置

下面是按照静态工厂方法的方式来配置Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
	<!-- 下面配置驱动Spring调用BeingFactory的静态getBeing()方法来创建Bean
	该bean元素包含的constructor-arg元素用于为静态工厂方法指定参数,
	因此这段配置会驱动Spring以反射方式来执行如下代码:
	dog = org.crazyit.app.factory.BeingFactory.getBeing("dog"); -->
	<bean id="dog" class="org.crazyit.app.factory.BeingFactory"
		factory-method="getBeing">
		<!-- 配置静态工厂方法的参数 -->
		<constructor-arg value="dog"/>
		<!-- 驱动Spring以"我是狗"为参数来执行dog的setMsg()方法 -->
		<property name="msg" value="我是狗"/>
	</bean>
	<!--  下面配置会驱动Spring以反射方式来执行如下代码:
	dog = org.crazyit.app.factory.BeingFactory.getBeing("cat"); -->
	<bean id="cat" class="org.crazyit.app.factory.BeingFactory"
		factory-method="getBeing">
		<!-- 配置静态工厂方法的参数 -->
		<constructor-arg value="cat"/>
		<!-- 驱动Spring以"我是猫"为参数来执行dog的setMsg()方法 -->
		<property name="msg" value="我是猫"/>
	</bean>
</beans>

一旦为<bean../>元素指定了factory-method属性,spring就不再调用构造器来创建Bean实例,而是调用工厂方法。如果同时指定了class和factory-method属性,那就会调用静态工厂方法来创建。

3.2.5、主程序

package lee;

import org.crazyit.app.service.Being;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

	public static void main(String[] args) {
		//以类加载路径下的配置文件创建ClassPathResource实例
		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		Being b1 = ctx.getBean("dog" , Being.class);
		b1.testBeing();
		Being b2 = ctx.getBean("cat" , Being.class);
		b2.testBeing();
	}
}

输出 输入图片说明

3.2.6、文件目录

输入图片说明

3.3、调用实例工厂方法

与静态工厂方法的区别

  • 调用静态工厂方法只需要工厂类就行,而调用实例工厂方法需要工厂实例;
  • 配置也有一点区别:前者使用class属性代表静态工厂类,后者不再需要class,用factory-bean代表工厂实例;之所以不用class是因为使用实例工厂方法时,spring不再直接实例化该bean,而是调用实例工厂的工厂方法来创建Bean实例。

<bean../>元素包括以下两个属性:

  • factory--bean:其值为工厂Bean的id;
  • factory-method:指定实例工厂的工厂方法;

代码实现

3.3.1、创建一个接口

该接口为实例工厂方法产生对象实现的接口

package org.crazyit.app.service;
public interface Person{
	//定义一个打招呼的方法
	public String sayHello(String name);
	//定义一个再见的方法
	public String sayGoodBye(String name);	
}

3.3.2、定义两个实现类

package org.crazyit.app.service.impl;

import org.crazyit.app.service.Person;

public class American implements Person{
	public String sayHello(String name) {
		return name + ",Hello";
	}
	public String sayGoodBye(String name) {
		return name + ",GoodBye";
	}
}
package org.crazyit.app.service.impl;

import org.crazyit.app.service.Person;

public class Chinese implements Person{
	public String sayHello(String name) {
		return name + ",你好!";
	}
	public String sayGoodBye(String name) {
		return name + ",再见!";
	}
}

3.3.3、定义一个实例工厂类

PersonFactory是负责产生person对象的实例工厂,该类提供了一个getPerson()的方法,根据传入的参数决定产生哪种person对象

package org.crazyit.app.factory;

import org.crazyit.app.service.Person;
import org.crazyit.app.service.impl.American;
import org.crazyit.app.service.impl.Chinese;

public class PersonFactory{
	//	获得Person实例的实例工厂方法,返回相应的Person实例的实现类;
	public Person getPerson(String ethnic){
		if(ethnic.equalsIgnoreCase("chin")){
			return new Chinese();
		}else{
			return new American();
		}
	}
}

3.3.4、创建配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
	<!-- 配置工厂Bean,该Bean负责产生其他Bean实例 -->
	<bean id="personFactory" class="org.crazyit.app.factory.PersonFactory"/>
	<!-- 下面配置驱动Spring调用personFactory Bean的getPerson()方法来创建Bean
	该bean元素包含的constructor-arg元素用于为工厂方法指定参数,-->
	<bean id="chinese" factory-bean="personFactory" 
		factory-method="getPerson">
		<!-- 配置实例工厂方法的参数 -->
		<constructor-arg value="chin"/>
	</bean>
	<bean id="american" factory-bean="personFactory"
		factory-method="getPerson">
		<constructor-arg value="ame"/>
	</bean>
</beans>

<bean../>的配置会驱动Spring以反射方式来执行如下代码:

// container代表Spring容器
PersonFactory pf = container.get("personFactory"); 
chinese = pf.getPerson("chin"); 

3.3.5、主程序

package lee;

import org.crazyit.app.service.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest
{
	public static void main(String[] args)
	{
		ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		Person p1 = ctx.getBean("chinese" , Person.class);
		System.out.println(p1.sayHello("张三"));
		System.out.println(p1.sayGoodBye("张三"));
		Person p2 = ctx.getBean("american" , Person.class);
		System.out.println(p2.sayHello("Jack"));
		System.out.println(p2.sayGoodBye("Jack"));
	}
}

输出 输入图片说明

3.3.6、文件目录

输入图片说明

3.4、总结

调用静态工厂和实例工厂方法创建Bean的异同之处:

(1)、相同点

  • 都使用factory-method属性来制定生产Bea的实例方法;
  • 若工厂方法需要传入参数,都需要<constructor-arg../>元素指定参数值;
  • 普通的设值注入都需要<property../>元素确定参数值;

(2)、不同点

  • 配置实例工厂创建Bean,必须将实例工厂配置成Bean实例;配置静态工厂创建Bean,无需配置工厂Bean;
  • 配置实例工厂创建Bean,必须用factory-bean属性来确定工厂Bean;配置静态工厂创建Bean,用class来确定静态工厂类;
共有 人打赏支持
粉丝 0
博文 36
码字总数 38704
×
yuhuan121
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: