spring提供了强大的xml文件支持。在此对其标签进行详细的介绍:
1. < beans >
特点:最顶级元素,包含多个。
结构图:
拥有的属性:
default-lazy-init: 值指定为true/false 默认是false,用于设置< bean >延迟初始化
default-autowire: 取值为no/byName/byType/constructor/。默认为no。控制全体bean采用哪种默认绑定方法
default-destroy-method: 指定管理的bean的销毁方法
default-init-method: 指定管理的bean的初始化方法
统一管理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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd" >
2. < description >
一般是省略的。可以为配置文件添加描述性话语
3.< import >
导入其它配置文件:
例子:
<import resource="simple2.xml"/>
4.< alias >
设置别名。
<alias name="ab" alias="x"/>
<bean id="ab" class=""></bean>
给名为ab的bean设置别名为x,此后可以使用x或者ab访问该bean
5.< bean>
例子:
<bean id="a" class="com.example.demo.xmlparse.Aclass"></bean>
属性:
id :区分beans管理下 的bean的唯一标识。这个是一个非必须的属性,特定的情况下可以省略;
name:指定bean的别名(同alias )。与id相比,name的属性更加灵活可以使用特殊的字符如 ‘ / ’ 还可以通过逗号,空格,冒号分割name
class: 大部分情况下是必须的,指定bean的数据类型,在抽象模板中可以不需要指定class
6. < constructor -arg >
配合bean使用,用于使用构造方式处理对象之间依赖关系。当对象中存在多个构造方法的时候,而且请求参数列表数目,类型不同时候,需要使用内部的属性协助。
type:
用于指定构造对象参数的注入类型,由于在xml配置时候,会将普通的入参默认当做字符串处理,因而在这种情况下也可以使用type指定其类型。避免出错。
<bean id="a" class="com.example.demo.xmlparse.Aclass">
<constructor-arg type="int">
<value>1</value>
</constructor-arg>
<constructor-arg>
<value>2</value>
</constructor-arg>
</bean>
如此处理入参,则获取到的bean结果为:
this bean :2 b:1
如果是不使用type的情况下:
<bean id="a" class="com.example.demo.xmlparse.Aclass">
<constructor-arg >
<value>1</value>
</constructor-arg>
<constructor-arg>
<value>2</value>
</constructor-arg>
</bean>
结构为:
this bean :1 b:2
index:
当类型也一致时候可以使用index指定入参的顺序,控制bean的依赖注入关系。此处不给出具体例子了。需要注意的是使用index的时候起始下标是从0开始的
value
可以作为一个属性写入也可以作为子标签在< constructor-arg >中使用:
<constructor-arg >
<value>1</value>
</constructor-arg>
与
<constructor-arg value="1" > </constructor-arg>
效果是一致的
7.、< property >
此标签用于处理setter依赖关系的关系注入。
<bean id="property" class="com.example.demo.xmlparse.Aclass">
<property name="a" value="2"></property>
<property name="b" value="1"></property>
</bean>
属性:
name : 属性名对应
value: 值
ref: 引用哪个bean
8. < property>与< constructor-arg>中的可配置项
< value> : 可以通过value为主体注入简单数据类型。可以指定String与基本类型+基本类型的包装类型。容器在注入的时候会做适当的转换工作,这个是最底层元素,无法再嵌套别的元素了
< ref> :
使用ref引用容器中的其它对象实例。有三种引用范围可以选择:local,parent,bean;
local只能指定与当前配置对象在同一配置文件下的bean对象
parent只能指定当前容器父容器定义的对象引用
bean均可以指定到。同< value>标签一致,它也是一个底层标签不能内嵌标签
9. < idref >
为当前对象注入所依赖对象的名称,而不是引用,最适合的方式是使用idref,这个标签可以在容器解析配置时候,帮助检查beanName是否存在。
使用范例:
<bean id="idreftry" class="com.example.demo.xmlparse.ABclass">
<property name="a">
<idref bean="x"></idref>
</property>
</bean>
注意:是名称不是引用,此方法注入的值一定是String,而且是已经在容器中托管的bean中的一个beanName
10. 内部bean。
定义的bean是私有的时候使用。
使用示例:
<bean id="innerBeanTry" class="com.example.demo.xmlparse.ABclass">
<constructor-arg>
<bean class="com.example.demo.xmlparse.Aclass">
</bean>
</constructor-arg>
</bean>
因为只供内部使用,所以可以不设置id的值。
11. < list>
对应注入对象类型为list类型及其子类或者数组类型的依赖对象。
<bean id="listTry" class="com.example.demo.xmlparse.ABclass">
<property name="b">
<list>
<value>1</value>
<ref bean="property"/>
<bean class="com.example.demo.xmlparse.Aclass">
<property name="a" value="12"></property>
<property name="b" value="11"></property>
</bean>
</list>
</property>
</bean>
12. < set>
注入一组无序的依赖。对set而言,元素顺序无关紧要。对应注入的依赖是set及其子类的依赖对象。
例子:
<bean id="setTry" class="com.example.demo.xmlparse.ABclass">
<property name="c">
<set>
<value>1</value>
<ref bean="property"/>
<bean class="com.example.demo.xmlparse.Aclass">
<property name="a" value="12"></property>
<property name="b" value="11"></property>
</bean>
</set>
</property>
</bean>
13. < map>
注入map及其子类类型依赖对象,使用该标签。该标签下有子标签< entry>.具体使用例子如下:
<bean id="mapTry" class="com.example.demo.xmlparse.ABclass">
<property name="d">
<map>
<entry key="first key">
<value>first value</value>
</entry>
<entry key="second key">
<ref bean="property"/>
</entry>
<entry key-ref="property" value="property bean"></entry>
<entry key-ref="a" value-ref="property"></entry>
</map>
</property>
</bean>
14.< props>
< props>是简化的< map>,但是不同于< map>,只能注入String类型的数据,< prop >中有且仅有key属性,不能内嵌别的标签
例子:
<bean id="propertiesTry" class="com.example.demo.xmlparse.ABclass">
<property name="e">
<props>
<prop key="author1">作者1</prop>
<prop key="author2">作者2</prop>
<prop key="author3">作者3</prop>
</props>
</property>
</bean>
15. < null/>
注入null值时候使用,需要注意的是< value></ value >注入的值是空字符串,不是null值。
16.bean的 depends-on 属性
此标签用于隐式指定bean之间的依赖关系。场景如:在静态代码块中初始化代码,或者数据库驱动注册时候。当存在多个隐式的依赖关系时候可以使用“,”进行分割。例子:
此为Aclass类,存在一个静态方法 config();
public class Aclass {
private static Aclass c;
public static Aclass config() {
return c;
}
public static void setC(Aclass c) {
Aclass.c = c;
}
....省略代码
ABclass类:ABclass的创建需要调用静态方法块内容,静态方法块内容中需要使用Aclass.config().say()方法,而say()需要依赖于Aclass的实体类,因而ABclass与Aclass之间虽然未标明依赖关系但依旧存在一层隐式依赖关系。Aclass需要在ABclass实例化之前实例化。此刻需要使用depends-on,标明隐式依赖关系。
public class ABclass {
static {
Aclass.config().say();
}
....省略代码
配置文件设置:
<bean id="dependsOnTry" class="com.example.demo.xmlparse.ABclass" depends-on="property">< /bean>
<bean id="a" class="com.example.demo.xmlparse.Aclass">
<constructor-arg >
<value>1</value>
</constructor-arg>
<constructor-arg>
<value>2</value>
</constructor-arg>
</bean>
<bean id="property" class="com.example.demo.xmlparse.Aclass">
<property name="a" value="2"></property>
<property name="b" value="1"></property>
<property name="c" ref="a"></property>
</bean>
17.bean的可选属性autowire
除开使用配置明确指出依赖关系,还可以使用autowire自动绑定的功能。(非标签方式也可以使用)。这样可以减少手工输入的工作量。spring提供了4种自动绑定的模式:
- no: 容器默认的绑定方式。不自动绑定对象
- byName: 与xml文件中配置的beanName进行匹配。匹配符合的bean定义会被自动绑定到当前实例变量上。适用于setter注入方式。
- byType: 类似与byName 的模式,只是从beanName的判定到beanType判定自动绑定。适用于setter注入方式。
- constructor: constructor指定的是按beanType的自动绑定过程。使用上跟byType类似,适用于构造器注入方式。
注意点:
自动注入的时候如果找不到对应的bean则不进行注入,当找到对应的参数的时候,则会注入,当找到多个可注入对象的时候,因无法判断该注入哪个值会发生异常,因而还是推荐使用手动指定依赖关系。
可以使用< beans >的属性default-autowire 指定beans管理下所有bean的自动注入方式。(一般不推荐)
简单的例子:
<bean id="autoByName" class="com.example.demo.xmlparse.Bclass" autowire="byName"></bean>
<bean id="autoByType" class="com.example.demo.xmlparse.Bclass" autowire="byType"></bean>
<bean id="autoByConstructor" class="com.example.demo.xmlparse.Bclass" autowire="constructor"></bean>
<alias name="a" alias="x"/>
<bean id="a" class="com.example.demo.xmlparse.Aclass">
<constructor-arg >
<value>1</value>
</constructor-arg>
<constructor-arg>
<value>2</value>
</constructor-arg>
</bean>
package com.example.demo.xmlparse;
public class Bclass {
private Aclass a;
....省略代码
测试代码:
// Bclass b=(Bclass)registry.getBean("autoByName");
// Bclass b=(Bclass)registry.getBean("autoByType");
Bclass b=(Bclass)registry.getBean("autoByConstructor");
b.getA().say();
18. bean的可选属性lazy-init
主要针对于ApplicationContext类型的容器初始化,bean的加载策略,通常对于ApplicationContext容器在容器启动的时候会马上对所有singleton类型的bean进行实例化加载。当想修改这个行为可以使用lazy-init属性加以限制。但是并非指定了lazy-init就一定会延迟加载。未延迟加载的bean对延迟加载的bean存在依赖关系时候,则还是会一开始便初始化设定了延迟加载规则的bean。
可以使用< beans>中的属性 default-lazy-init对beans下的bean进行统一的加载策略设置。
<bean id="autoByName" class="com.example.demo.xmlparse.Bclass" autowire="byName" lazy-init="true"></bean>
测试代码
ApplicationContext registry=new ClassPathXmlApplicationContext("classpath:simple.xml");
Bclass b=(Bclass)registry.getBean("autoByName");
运行结果:
19:15:01.325 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a'
19:15:01.362 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoByType'
19:15:01.403 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoByConstructor'
19:15:01.403 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'autoByConstructor' via constructor to bean named 'a'
19:15:01.429 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoByName'
可以看出执行是在调用时候才进行的实例化。
19.xml中bean的继承关系 :bean属性 parent 与abstract
使用场景:继承关系下,属性继承父类的bean的属性,特定的属性额外配置其值。与java的继承不同的是,此处继承的是属性的值,java继承的属性跟方法。因而这种继承不同于java继承的纵向继承。常规写法需要对子类重复赋值会存在冗余的情况,因而可以考虑使用xml中bean继承。
使用示例:
<bean id="parentBean" class="com.example.demo.xmlparse.Bclass">
<property name="a" ref="a"></property>
<property name="b" value="223344"></property>
</bean>
<bean id="children1" class="com.example.demo.xmlparse.Cclass" parent="parentBean">
<property name="b" value="this is children C"></property>
</bean>
<bean id="children2" class="com.example.demo.xmlparse.Dclass" parent="parentBean">
<property name="b" value="this is children D"></property>
</bean>
测试:
public static void main(String[] args) {
DefaultListableBeanFactory registry=new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(registry);
beanDefinitionReader.loadBeanDefinitions("simple.xml");
Cclass cclass=registry.getBean(Cclass.class);
Dclass dclass=registry.getBean(Dclass.class);
System.out.println(cclass.getA().equals(dclass.getA()));
System.out.println(cclass.getB().equals(dclass.getB()));
}
parent可以把这个属性与abstract一起使用,以达到模板化bean的目的。在这种情况下,定义为抽象的bean无须指定其class(少数可以不使用class的情景)。例子:
<bean id="abstractTry" abstract="true">
<property name="a" ref="a"></property>
<property name="b" value="223344"></property>
</bean>
<bean id="abstractChildren1" class="com.example.demo.xmlparse.Cclass" parent="abstractTry">
<property name="b" value="this is children C"></property>
</bean>
<bean id="abstractChildren2" class="com.example.demo.xmlparse.Dclass" parent="abstractTry">
<property name="b" value="this is children D"></property>
</bean>
测试代码:
public static void main(String[] args) {
DefaultListableBeanFactory registry=new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(registry);
beanDefinitionReader.loadBeanDefinitions("simple.xml");
Cclass cclass=(Cclass)registry.getBean("abstractChildren1");
Dclass dclass=(Dclass)registry.getBean("abstractChildren2");
System.out.println(cclass.getA().equals(dclass.getA()));
System.out.println(cclass.getB().equals(dclass.getB()));
}
虽然abstractTry的bean并未有具体的类,但是其内部的值依旧被Cclass与Dclass的bean继承了。可以看出来在spring容器启动的过程中,标志位abstract的bean定义是不会被创建的。这个适用于全部所有的beanFactory。可以使用该属性,避免bean定义被容器实例化
20.bean的scope属性
该属性也被叫做bean的作用域。主要是负责管理bean的生命周期以及声明对象所处的限定场景。限定好场景后,在进入其对应的scope之前,容器不会生成并装配这些对象。当不再处于其scope限定范围内时候,容器会销毁这个对象。
容器最初提供了两种bean的scope类型:singleton+prototype
在2.0后扩展了3种类型,其后一共是5种类型:
- request
- session
- global session
- singleton
- prototype
往后详细介绍下各个scope的特点:
singleton:
对象实例数:在一个容器中,只存在一个共享实例。所有对该类型的bean依赖全部都引用这个单一实例
存活时间:从容器请求而实例化开始,该类型的bean实例会一直存在,直到容器销毁或者退出
示例图:
prototype
对象实例数:每次接到请求时候,均会重新生成一个新的对象实例。在容器中存在复数个的同类型对象,适用于对象不共享的情况
存活时间:容器生成prototype类型的bean对象时候,会将对象实例返给请求方,容器不再拥有当前返回对象的引用。请求方需要自行管理该bean对象的生命周期
示例图:
request
只适用于web应用程序。通常与XmlWebApplicationContext一起使用。
实例数:XmlWebApplicationContext会给每个Http请求创建一个全新的Request-Processor供其使用。
存活时间:请求结束时候,该实例生命周期结束。可以说其是prototype的特殊情况。场景比prototype更加具体
session
只适用于web应用程序。通常与XmlWebApplicationContext一起使用。
同request类似,只不过范围从request扩大到了session范围。
global session
只适用于基于portlet的web应用程序。映射到portlet范围的session。在基于servlet的情况下使用的时候,会按session的情况进行处理。
spring容器支持自定义SCOPE.需要实现接口org.springframework.beans.factory.config.Scope
package org.springframework.beans.factory.config;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.lang.Nullable;
public interface Scope {
Object get(String name, ObjectFactory<?> objectFactory);
@Nullable
Object remove(String name);
void registerDestructionCallback(String name, Runnable callback);
@Nullable
Object resolveContextualObject(String key);
@Nullable
String getConversationId();}
其中get,remove是必须要实现的方法。
这里给出两种实现方式:
其一:编码方式实现
创建线程生命周期的Scope:ThreadScope
public class ThreadScope implements Scope{
private final ThreadLocal threadScope=new ThreadLocal() {
protected Object initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// TODO Auto-generated method stub
Map scope=(Map) threadScope.get();
Object object=scope.get(name);
if(object==null) {
object=objectFactory.getObject();
scope.put(name, object);
}
return object;
}
@Override
public Object remove(String name) {
// TODO Auto-generated method stub
Map scope=(Map) threadScope.get();
return scope.remove(name);
}
....省略代码
设定beanFactory为其注册名为“thread”的scope
public static BeanFactory getMyBeanFactory() {
ConfigurableBeanFactory beanFactory=new DefaultListableBeanFactory();
Scope myScope=new ThreadScope();
beanFactory.registerScope("thread", myScope);
AbstractBeanDefinition beanB=new RootBeanDefinition(Bclass.class);
AbstractBeanDefinition beanA=new RootBeanDefinition(Aclass.class);
MutablePropertyValues propertyValuesB=new MutablePropertyValues();
propertyValuesB.add("a", beanA);
propertyValuesB.add("b", "BClass类b属性编码方式Setter注入");
beanB.setPropertyValues(propertyValuesB);
MutablePropertyValues propertyValuesA=new MutablePropertyValues();
propertyValuesA.add("c", new Aclass("Aclass类 c属性编码Setter注入值",0));
propertyValuesA.add("b", 2);
propertyValuesA.add("a", "Aclass类 a属性编码Setter注入值");
beanA.setPropertyValues(propertyValuesA);
beanB.setScope("thread");
beanA.setScope("singleton");
((DefaultListableBeanFactory)beanFactory).registerBeanDefinition("threadScope", beanB);
((DefaultListableBeanFactory)beanFactory).registerBeanDefinition("singletonScope", beanA);
return beanFactory;
}
获取注册的Bean
BeanFactory beanFactory=getMyBeanFactory();
Aclass aclass=(Aclass)beanFactory.getBean("singletonScope");
Bclass bclass=(Bclass)beanFactory.getBean("threadScope");
if(bclass!=null) {
System.out.println(bclass.getB());
bclass.getA().say();
}
其二:xml配置文件方式:
首先需要创建一个继承了BeanFactory的类,同时初始化时候需要将“thread”注册到容器中。
public class BeanFactoryScopeTry extends DefaultListableBeanFactory{
public BeanFactoryScopeTry() {
ThreadScope threadScope=new ThreadScope();
this.registerScope("thread", threadScope);
}
}
编写xml文件:
<bean id="scopeDefineTry" class="com.example.demo.xmlparse.Bclass" scope="thread">
<property name="a" ref="a"></property>
<property name="b" value="我的自定义scopeBean"></property>
</bean>
调用定义的bean:
BeanFactory beanFactory=new BeanFactoryScopeTry();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader((BeanDefinitionRegistry)beanFactory);
reader.loadBeanDefinitions("simple.xml");
Bclass b=(Bclass) beanFactory.getBean("scopeDefineTry");
b.getA().say();
21.bean属性 :factory-method 与factory-bean
使用工厂方法,解决接口与实现类的耦合性。提供一个工厂类来实例化具体的接口实现类。主体对象只需要依赖工厂类。具体使用的实现类存在变更的时候。可以通过变更工厂类实现,主体类不需要变动。(正常通过依赖注入的方式也可以解决耦合问题情况,但是在依赖注入的过程中总是会有耦合存在的,使用工厂类可以屏蔽两者之间的直接耦合)
方式一:静态工厂方式
静态工厂方法模式指的是其工厂方法由static修饰的,这类方法不需要依赖一个工厂实例,只需要一个工厂类路径即可,因而不需要使用factory-bean,只需要指定其工厂类class。
例子:
配置文件:
<bean id="main" class="com.example.demo.beanfactory.MainBean">
<property name="bean" ref="instance"></property>
</bean>
<bean id="instance" class="com.example.demo.beanfactory.MyBeanFactory" factory-method="getInstance"></bean>
bean类:
package com.example.demo.beanfactory;
public class BeanFactoryTry {
private String a;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
工厂类:
package com.example.demo.beanfactory;
public class MyBeanFactory {
public static BeanFactoryTry getInstance() {
BeanFactoryTry bean=new BeanFactoryTry();
bean.setA("A Value");
return bean;
}
}
调用:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("simple2.xml");
MainBean bean=(MainBean) beanFactory.getBean("main");
System.out.println(bean.getBean().getA());
此方式定义的bean (“instance”)其类型值与class不一定一致,其bean值的类型主要与其工厂方法返回值有关。
如果工厂方法需要入参。则需要使用标签< constructor -arg>进行值的入参。
如:
<bean id="instance" class="com.example.demo.beanfactory.MyBeanFactory" factory-method="getInstance">
<constructor-arg ref="..."></constructor-arg>
</bean>
方式二:非静态工厂方法模式
不同于静态工厂方法模式,该类型的工厂方法是非静态的,因而需要依赖一个具体的工厂实现类bean,需要使用到属性factory-bean指定工厂方法所在的工厂bean实例
其余方面的使用方式同静态工厂方式。
例子:
<bean id="main" class="com.example.demo.beanfactory.MainBean">
<property name="bean" ref="instance"></property>
</bean>
<bean id="factory" class="com.example.demo.beanfactory.MyBeanFactory"></bean>
<bean id="instance" factory-bean="factory" factory-method="getNonInstance"></bean>
测试:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("simple2.xml");
MainBean bean=(MainBean) beanFactory.getBean("main");
System.out.println(bean.getBean().getA());
工厂方法:
public class MyBeanFactory {
public BeanFactoryTry getNonInstance() {
BeanFactoryTry bean=new BeanFactoryTry();
bean.setA("A Value");
return bean;
}
....省略代码
注意点:
Spring的特殊类型FactoryBean的实现类,当xml文件引用其id对象的时候,返回值是其工厂方法产生的bean。这种方式引用时候,不需要使用到factory-bean及factory-method.当想让引用的对象是FactoryBean本身时候,需要在其id 的name前面加前缀:"&".
例子:
<bean id="main" class="com.example.demo.beanfactory.MainBean">
<!-- <property name="bean" ref="instance"></property> -->
<property name="bean" ref="factoryBean"></property>
</bean>
<bean id="factoryBean" class="com.example.demo.beanfactory.MyFactoryBean"></bean>
测试:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("simple2.xml");
MyFactoryBean factoryBean=(MyFactoryBean) beanFactory.getBean("&factoryBean");
MainBean bean=(MainBean) beanFactory.getBean("main");
System.out.println(bean.getBean().getA());
几种常见的FactoryBean:
- org.springframework.jndi.JndiObjectFactoryBean
- org.springframework.aop.framework.ProxyFactoryBean
- …
22. < bean> 子标签< lookup-method name="…" bean="…"> (方法注入)
spring依赖注入之后,正常来说被注入 的bean会持有这个注入的bean的引用,就算这个bean是prototype亦是如此。如此导致的结果便是,每次调用被注入的bean的get方法时候,获取的都会是同一个Bean对象。当想要让每次get访问的时候获取到的都是不一样的bean之时,处理方式有两种:
其一:使用标签< lookup-method>方法注入方式实现
配置方式:
<bean id="prototype" class="com.example.demo.beanfactory.BeanFactoryTry" scope="prototype"></bean>
<bean id="lookup-method" class="com.example.demo.beanfactory.MainBean">
<lookup-method name="getBean" bean="prototype"/>
</bean>
配置方式中第一个属性:name是com.example.demo.beanfactory.MainBean中的方法。
配置方式中第二个属性:bean是容器中存在的bean的名字。表示,当调用MainBean中的getBean方法时候,把容器中名为prototype的bean值get出来作为返回值,返回。因为此处prototype是原型模式,因而测试案例中返回值两次是不一样的。这种方式是不看MainBean对象中的原先getBean方法的实现是什么 ,哪怕你之前返回值是空,亦不会影响其返回两个不同的bean实例。可以看做是将原先的getBean方法替换了。
测试案例:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("simple2.xml");
MainBean mainBean=(MainBean) beanFactory.getBean("lookup-method");
System.out.println(mainBean.getBean());
System.out.println(mainBean.getBean());
执行结果:
com.example.demo.beanfactory.BeanFactoryTry@2758fe70
com.example.demo.beanfactory.BeanFactoryTry@1f36e637
其二:使用接口的方式实现。
在第一种方式中其实可以看出端倪,主要需要注意的是getBean方法返回的bean值是需要从容器中获取的值。因而可以将MainBean 类与BeanFactory耦合。重写getBean方法,使其return的值是从BeanFactory中获取到的值即可。
Spring提供了接口BeanFactoryAware。实现该接口的类。会自动将容器本身注入到该类当中
配置:
<bean id="prototype" class="com.example.demo.beanfactory.BeanFactoryTry" scope="prototype"></bean>
<bean id="beanFactoryAware" class="com.example.demo.beanfactory.MainBean">
</bean>
对MainBean的改造:
public class MainBean implements BeanFactoryAware{
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
this.beanFactory=beanFactory;
}
private BeanFactoryTry bean;
public BeanFactoryTry getBean() {
return (BeanFactoryTry) beanFactory.getBean("prototype");
}
public void setBean(BeanFactoryTry bean) {
this.bean = bean;
}
}
测试结果殊途同归,在此省略…
对于不喜欢将BeanFactory与具体类耦合的类。Spring提供了ObjectFactoryCreatingFactoryBean.此类返回一个ObjectFactory实例。
package com.example.demo.beanfactory;
import org.springframework.beans.factory.ObjectFactory;
public class MainBeanObjectFactory {
private ObjectFactory<BeanFactoryTry> objectFactory;
public void setObjectFactory(ObjectFactory<BeanFactoryTry> objectFactory) {
this.objectFactory = objectFactory;
}
private BeanFactoryTry bean;
public BeanFactoryTry getBean() {
return objectFactory.getObject();
}
public void setBean(BeanFactoryTry bean) {
this.bean = bean;
}
}
配置文件
<bean id="prototype" class="com.example.demo.beanfactory.BeanFactoryTry" scope="prototype"></bean>
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName">
<idref bean="prototype"/>
</property>
</bean>
<bean id="objectFactoryMainBean" class="com.example.demo.beanfactory.MainBeanObjectFactory">
<property name="objectFactory" ref="objectFactory"></property>
</bean>
此处给objectFactoryMainBean填入的引用虽然是objectFactory,但其实注入的值是prototype的对应ObjectFactory实例。
调用测试方法:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("simple2.xml");
MainBeanObjectFactory mainBean=(MainBeanObjectFactory) beanFactory.getBean("objectFactoryMainBean");
System.out.println(mainBean.getBean());
System.out.println(mainBean.getBean());
结果依旧是不同的两个bean值。
23.方法替换< replaced-method>方法
< replaced-method>标签用于替换掉bean中的方法。其内部属性包括两个部分:
- name 要替换的方法名
- replacer 替换的类的bean名。这个类需要实现接口MethodReplacer.
例子:
替换类,实现MethodReplacer
public class MyMethodReplacer implements MethodReplacer{
private static final Log logger=LogFactory.getLog(MyMethodReplacer.class);
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
logger.info("before excuting method");
System.out.println(method.getName()+"on Object :"+obj.getClass().getName());
logger.info("end of excuting method");
return null;
}
}
配置文件:
<bean id="replacer" class="com.example.demo.beanfactory.MyMethodReplacer"></bean>
<bean id="replaceAMethod" class="com.example.demo.xmlparse.Aclass">
<property name="a" value="name of A"></property>
<property name="b" value="999"></property>
<replaced-method name="say" replacer="replacer"></replaced-method>
</bean>
测试结果:
20:49:53.657 [main] INFO com.example.demo.beanfactory.MyMethodReplacer - before excuting method
sayon Object :com.example.demo.xmlparse.Aclass E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBa85dfd94
20:49:53.657 [main] INFO com.example.demo.beanfactory.MyMethodReplacer - end of excuting method
可以发现原有的say()方法被替换。
部分代码不清楚的地方,这里放上github示例代码信息。