5 Spring Bean基础
在Spring IoC容器使用类就会被认为是“Bean”,并可在Spring bean的配置文件或者通过注解来声明。
---------- Spring Bean引用例子
在Spring,bean可以“访问”对方通过bean配置文件指定相同或不同的引用
-
1 Bean在不同的XML文件
如果是在不同XML文件中的bean,可以用一个“ref”标签,“bean”属性引用它。
<ref bean="someBean"/>
“CsvOutputGenerator”或“JsonOutputGenerator”通过属性标签使用'ref'属性,在这个例子中,Bean “OutputHelper” 在 'Spring-Common.xml' 声明可以访问其他 Bean在'Spring-Output.xml'。
- File : Spring-Common.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="OutputHelper" class="com.gp6.help.OutputHelper">
<property name="outputGenerator" ref="CsvOutputGenerator" />
</bean>
</beans>
- File : Spring-Output.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="CsvOutputGenerator" class="com.gp6.service.impl.CsvOutputGenerator" />
<bean id="JsonOutputGenerator" class="com.gp6.service.impl.JsonOutputGenerator" />
</beans>
-
2 在同一个XML文件中的Bean
如果引用在同一个XML文件中的bean,你可以用 'ref' 标签,“local”属性引用它。
<ref local="someBean"/>
在这个例子中,Bean “OutputHelper” 在 'Spring-Common.xml' 声明可以相互访问“CsvOutputGenerator”或“JsonOutputGenerator”。
File : Spring-Common.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="OutputHelper" class="com.gp6.help.OutputHelper">
<property name="outputGenerator" >
<ref local="CsvOutputGenerator"/>
</property>
</bean>
<bean id="CsvOutputGenerator" class="com.gp6.service.impl.CsvOutputGenerator" />
<bean id="JsonOutputGenerator" class="com.gp6.service.impl.JsonOutputGenerator" />
</beans>
总结
其实,无论是在相同或不同的XML文件,“ref标签可以访问一个bean,但是,对于该项目的可读性,如果引用了相同的 XML文件中声明 bean,您应该使用“local”属性
---------- 如何注入值到Spring bean属性
在Spring中,有三种方式注入值到 bean 属性。
- 正常的方式
- 快捷方式
- p 模式
看到一个简单的Java类,它包含两个属性 - name 和 type。稍后将使用Spring注入值到这个 bean 属性。
package com.gp6.bean;
public class FileNameGenerator {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
1 正常方式
在一个“value”标签注入值,并附有“property”标签结束。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<bean id="FileNameGenerator" class="com.gp6.bean.FileNameGenerator">
<property name="name">
<value>gp6</value>
</property>
<property name="type">
<value>html</value>
</property>
</bean>
</beans>
2 快捷方式
注入值“value”属性。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<bean id="FileNameGenerator" class="com.gp6.bean.FileNameGenerator">
<property name="name" value="gp6" />
<property name="type" value="html" />
</bean>
</beans>
3 p 模式
通过使用“p”模式作为注入值到一个属性。
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="FileNameGenerator" class="com.gp6.bean.FileNameGenerator"
p:name="gp6" p:type="html" />
</beans>
记住声明 xmlns:p=”http://www.springframework.org/schema/p" 在Spring XML bean配置文件。
---------- Spring bean加载多个配置文件
在一个大的项目结构,Spring bean配置文件位于不同的文件夹以便于维护和模块化。例如,Spring-Common.xml在common 文件夹中,Spring-Connection.xml 在connection文件夹,Spring-ModuleA.xml在ModuleA 文件夹等等。
你可以加载多个Spring bean的配置文件如下代码中:
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring-Common.xml",
"Spring-Connection.xml","Spring-ModuleA.xml"});
把所有的 Spring XML 文件放入在项目类路径中。
project-classpath/Spring-Common.xml
project-classpath/Spring-Connection.xml
project-classpath/Spring-ModuleA.xml
解决方法
以上方法是缺乏组织并且很容易出错,更好的办法应组织所有的Spring bean 配置文件到一个XML文件。例如,创建一个Spring-All-Module.xml文件,并导入整个Spring bean的文件如下:
File : Spring-All-Module.xml
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<import resource="common/Spring-Common.xml"/>
<import resource="connection/Spring-Connection.xml"/>
<import resource="moduleA/Spring-ModuleA.xml"/>
</beans>
现在,可以加载一个这样的 XML 文件:
ApplicationContext context =
new ClassPathXmlApplicationContext(Spring-All-Module.xml);
将这个文件放入项目的类路径。
project-classpath/Spring-All-Module.xml
-
注意
在Spring3,所述替代解决方案是使用 JavaConfig @Import.
---------- Spring内部bean实例
在Spring框架中,一个bean仅用于一个特定的属性,这是提醒其声明为一个内部bean。内部bean支持setter注入“property”和构造器注入"constructor-arg“。
下面来看看一个详细的例子,演示使用 Spring 内部 bean 。
- Customer.java
package com.gp6.bean;
public class Customer {
private Person person;
public Customer(Person person) {
this.person = person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public String toString() {
return "Customer [person=" + person + "]";
}
}
- Person.java
package com.gp6.bean;
public class Person {
private String name;
private String address;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [address=" + address + "age=" + age + ", name=" + name + "]";
}
}
- Spring内部Bean注入实力
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!--以下三种方式,只需要一个即可 -->
<!--1 很多时候,可以使用 'ref' 属性来引用“Person” bean到“Customer” Bean,person的属性如下: -->
<bean id="CustomerBean" class="com.gp6.bean.Customer">
<property name="person" ref="PersonBean" />
</bean>
<bean id="PersonBean" class="com.gp6.bean.Person">
<property name="name" value="gp6" />
<property name="address" value="安徽省" />
<property name="age" value="24" />
</bean>
<!--2 在一般情况下,引用这样也没有问题,但由于“gp6” persion bean 只用于Customer bean,这是更好地声明 “gp6” person 作为一个内部 bean,如下: -->
<bean id="CustomerBean" class="com.gp6.bean.Customer">
<property name="person">
<bean class="com.gp6.bean.Person">
<property name="name" value="gp6" />
<property name="address" value="安徽省" />
<property name="age" value="24" />
</bean>
</property>
</bean>
<!--3 内部 bean 也支持构造器注入如下: -->
<bean id="CustomerBean" class="com.gp6.bean.Customer">
<constructor-arg>
<bean class="com.gp6.bean.Person">
<property name="name" value="gp6" />
<property name="address" value="安徽省" />
<property name="age" value="24" />
</bean>
</constructor-arg>
</bean>
</beans>
- 注意:
id 或 name 值在bean类是没有必要以一个内部 bean 呈现,它会简单地忽略Spring容器。
- 执行结果:
package com.gp6.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gp6.bean.Customer;
import com.gp6.bean.JavaConfigImport01;
import com.gp6.bean.JavaConfigImport02;
import com.gp6.help.JavaConfigImportTestAll;
public class SpringTest07 {
public static void main( String[] args ) {
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"Spring04.xml"});
Customer cust = (Customer)context.getBean("CustomerBean");
System.out.println(cust);
}
}
- 输出结果:
Customer [person=Person [address=安徽省age=24, name=gp6]]
---------- Spring Bean作用域实例
在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者。bean支持的5种范围域:
1 单例 - 每个Spring IoC 容器返回一个bean实例
2 原型- 当每次请求时返回一个新的bean实例
3 请求 - 返回每个HTTP请求的一个Bean实例
4 会话 - 返回每个HTTP会话的一个bean实例
5 全局会话- 返回全局HTTP会话的一个bean实例
在大多数情况下,可能只处理了 Spring 的核心作用域 ----单例和原型,默认作用域是单例。
注:意味着只有在一个基于web的Spring ApplicationContext情形下有效!
单例VS原型
这里有一个例子来说明,bean的作用域单例和原型之间的不同:
package com.gp6.bean;
public class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
1.单例例子
如果 bean 配置文件中没有指定 bean 的范围,默认为单例。
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="message"
class="com.gp6.bean.Message" />
</beans>
- 执行结果:
package com.gp6.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.gp6.bean.Customer;
import com.gp6.bean.JavaConfigImport01;
import com.gp6.bean.JavaConfigImport02;
import com.gp6.bean.Message;
import com.gp6.help.JavaConfigImportTestAll;
public class SpringTest08 {
public static void main( String[] args ) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"Spring05.xml"});
Message custA = (Message)context.getBean("message");
custA.setMessage("Message by custA");
System.out.println("Message : " + custA.getMessage());
//retrieve it again
Message custB = (Message)context.getBean("message");
System.out.println("Message : " + custB.getMessage());
}
}
- 输出结果
Message : Message by custA
Message : Message by custA
由于 bean 的 Message' 是单例作用域,第二个通过提取”custB“将显示消息由 ”custA' 设置,即使它是由一个新的 getBean()方法来提取。在单例中,每个Spring IoC容器只有一个实例,无论多少次调用 getBean()方法获取它,它总是返回同一个实例。
2.原型例子
如果想有一个新的 “Message”bean 实例,每次调用它的时候,需要使用原型(prototype)来代替。
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<bean id="customerService" class="com.yiibai.customer.services.CustomerService"
scope="prototype"/>
</beans>
- 运行-执行
Message : Message by custA
Message : null
在原型作用域,必须为每个 getBean()方法中调用返回一个新的实例。
3. Bean作用域注释
还可以使用注释来定义 bean 的作用域。
package com.gp6.bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
启用自动组件扫描
<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="com.gp6.bean" />
</beans>