spring-beans深入源码之Bean Definitio
Bean定义的时候包含许多可配置的信息,包含构造函数的参数,类属性 还可以包含一些容器相关的信息例如初始化方法,静态工厂方法的名字等等。
定义Bean的时候有个parent参数,可以I指定父bean,这样的话子Bean就可以继承父Bean的配置信息,子Bean可以覆盖父Bean的配置信息,也可以任意增加。Spring的Bean的定义继承性和java的类继承没有关系,但是结果是一样的。
在spring-beans module 的test代码中有关于bean定义继承的测试。找到collectionMerging.xml
collectionMerging.png
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="parentWithList" class="org.springframework.tests.sample.beans.TestBean">
<property name="someList">
<list>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</property>
</bean>
<bean id="childWithList" parent="parentWithList">
<property name="someList">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</property>
</bean>
<bean id="childWithListOfRefs" parent="parentWithList">
<property name="someList">
<list merge="true">
<bean class="org.springframework.tests.sample.beans.TestBean"/>
</list>
</property>
</bean>
<bean id="parentWithSet" class="org.springframework.tests.sample.beans.TestBean">
<property name="someSet">
<set>
<value>Rob Harrop</value>
</set>
</property>
</bean>
<bean id="childWithSet" parent="parentWithSet">
<property name="someSet">
<set merge="true">
<value>Sally Greenwood</value>
</set>
</property>
</bean>
<bean id="childWithSetOfRefs" parent="parentWithSet">
<property name="someSet">
<set merge="true">
<bean class="org.springframework.tests.sample.beans.TestBean">
<property name="name" value="Sally"/>
</bean>
</set>
</property>
</bean>
<bean id="parentWithMap" class="org.springframework.tests.sample.beans.TestBean">
<property name="someMap">
<map>
<entry key="Rob" value="Sall"/>
<entry key="Juergen" value="Eva"/>
</map>
</property>
</bean>
<bean id="childWithMap" parent="parentWithMap">
<property name="someMap">
<map merge="true">
<entry key="Rod" value="Kerry"/>
<entry key="Rob" value="Sally"/>
</map>
</property>
</bean>
<bean id="childWithMapOfRefs" parent="parentWithMap">
<property name="someMap">
<map merge="true">
<entry key="Rob">
<bean class="org.springframework.tests.sample.beans.TestBean">
<property name="name" value="Sally"/>
</bean>
</entry>
</map>
</property>
</bean>
<bean id="parentWithProps" class="org.springframework.tests.sample.beans.TestBean">
<property name="someProperties">
<props>
<prop key="Rob">Sall</prop>
<prop key="Rod">Kerry</prop>
</props>
</property>
</bean>
<bean id="childWithProps" parent="parentWithProps">
<property name="someProperties">
<props merge="true">
<prop key="Juergen">Eva</prop>
<prop key="Rob">Sally</prop>
</props>
</property>
</bean>
<bean id="parentWithListInConstructor" class="org.springframework.tests.sample.beans.TestBean">
<constructor-arg index="0">
<list>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</constructor-arg>
</bean>
<bean id="childWithListInConstructor" parent="parentWithListInConstructor">
<constructor-arg index="0">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</constructor-arg>
</bean>
<bean id="childWithListOfRefsInConstructor" parent="parentWithListInConstructor">
<constructor-arg index="0">
<list merge="true">
<bean class="org.springframework.tests.sample.beans.TestBean"/>
</list>
</constructor-arg>
</bean>
<bean id="parentWithSetInConstructor" class="org.springframework.tests.sample.beans.TestBean">
<constructor-arg index="0">
<set>
<value>Rob Harrop</value>
</set>
</constructor-arg>
</bean>
<bean id="childWithSetInConstructor" parent="parentWithSetInConstructor">
<constructor-arg index="0">
<set merge="true">
<value>Sally Greenwood</value>
</set>
</constructor-arg>
</bean>
<bean id="childWithSetOfRefsInConstructor" parent="parentWithSetInConstructor">
<constructor-arg index="0">
<set merge="true">
<bean class="org.springframework.tests.sample.beans.TestBean">
<property name="name" value="Sally"/>
</bean>
</set>
</constructor-arg>
</bean>
<bean id="parentWithMapInConstructor" class="org.springframework.tests.sample.beans.TestBean">
<constructor-arg index="0">
<map>
<entry key="Rob" value="Sall"/>
<entry key="Juergen" value="Eva"/>
</map>
</constructor-arg>
</bean>
<bean id="childWithMapInConstructor" parent="parentWithMapInConstructor">
<constructor-arg index="0">
<map merge="true">
<entry key="Rod" value="Kerry"/>
<entry key="Rob" value="Sally"/>
</map>
</constructor-arg>
</bean>
<bean id="childWithMapOfRefsInConstructor" parent="parentWithMapInConstructor">
<constructor-arg index="0">
<map merge="true">
<entry key="Rob">
<bean class="org.springframework.tests.sample.beans.TestBean">
<property name="name" value="Sally"/>
</bean>
</entry>
</map>
</constructor-arg>
</bean>
<bean id="parentWithPropsInConstructor" class="org.springframework.tests.sample.beans.TestBean">
<constructor-arg index="0">
<props>
<prop key="Rob">Sall</prop>
<prop key="Rod">Kerry</prop>
</props>
</constructor-arg>
</bean>
<bean id="childWithPropsInConstructor" parent="parentWithPropsInConstructor">
<constructor-arg index="0">
<props merge="true">
<prop key="Juergen">Eva</prop>
<prop key="Rob">Sally</prop>
</props>
</constructor-arg>
</bean>
</beans>
可以看到 所有含有<b>parent</b>定义关键字的bean定义都是没有class指定的,所有可以看到和java的类继承没有关联。
找到测试类<b>CollectionMergingTests</b>
找到第一个测试方法看一下子bean增加属性的测试,增加自己的日志
public class CollectionMergingTests extends TestCase {
private DefaultListableBeanFactory beanFactory;
@Override
protected void setUp() throws Exception {
this.beanFactory = new DefaultListableBeanFactory();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("collectionMerging.xml", getClass()));
}
public void testMergeList() throws Exception {
TestBean bean = (TestBean) this.beanFactory.getBean("childWithList");
List list = bean.getSomeList();
System.out.println("holly.wang test before");
assertEquals("Incorrect size", 3, list.size());
System.out.println("Incorrect size:"+ list.size());
assertEquals(list.get(0), "Rob Harrop");
System.out.println("list.get(0):"+list.get(0));
assertEquals(list.get(1), "Rod Johnson");
System.out.println("list.get(1):"+list.get(1));
assertEquals(list.get(2), "Juergen Hoeller");
System.out.println("list.get(2):"+list.get(2));
System.out.println("holly.wang test after");
}
}
父bean的定义
<bean id="parentWithList" class="org.springframework.tests.sample.beans.TestBean">
<property name="someList">
<list>
<value>Rob Harrop</value>
<value>Rod Johnson</value>
</list>
</property>
</bean>
子Bean的定义
<bean id="childWithList" parent="parentWithList">
<property name="someList">
<list merge="true">
<value>Juergen Hoeller</value>
</list>
</property>
</bean>
跑一下<b>testMergeList</b> 看一下
bean_inherate_test.pngholly.wang test before
Incorrect size:3
list.get(0):Rob Harrop
list.get(1):Rod Johnson
list.get(2):Juergen Hoeller
holly.wang test after
结果正确,这样可以少了很多子类的代码,假如有一个属性非常全的bean,但对外或者某些接口需要屏蔽一些属性即可这样做。还有一种方法可以达到屏蔽的效果,就是Java <b>transient</b>关键字。
一个对象需要在网络传输或者存储之类的话 需要实现Serilizable接口(当然还有google的Protocol Buffers等方式,暂时不说这些)这样的话就可以传输或者存储了,但是有些敏感的信息 密码 银行卡 身份证 等不想序列化或者传输 就可以利用transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
使用方式是定义字段的时候 例如:private transient String passwd;
在spring中没有找到相关的测试。
自己写个:
package org.springframework.tests.beans;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author holly.wang
* @since 4.2
*/
public class TransientPropertiesTest {
public static void main(String[] args) {
TestBean bean = new TestBean();
bean.setPropertie1("p1");
bean.setPropertie2("p2");
System.out.println("holly.wang test beafore Serializable :");
System.out.println("p1: " + bean.getPropertie1());
System.err.println("p2: " + bean.getPropertie2());
try {
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream("D:/bean.txt"));
os.writeObject(bean); // 将User对象写进文件
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(
"D:/bean.txt"));
bean = (TestBean) is.readObject(); // 从流中读取User的数据
is.close();
System.out.println("holly.wang test after Serializable: ");
System.out.println("p1: " + bean.getPropertie1());
System.err.println("p2: " + bean.getPropertie2());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class TestBean implements Serializable {
private String propertie1;
private transient String propertie2;
/**
* @return the propertie1
*/
public String getPropertie1() {
return propertie1;
}
/**
* @param propertie1 the propertie1 to set
*/
public void setPropertie1(String propertie1) {
this.propertie1 = propertie1;
}
/**
* @return the propertie2
*/
public String getPropertie2() {
return propertie2;
}
/**
* @param propertie2 the propertie2 to set
*/
public void setPropertie2(String propertie2) {
this.propertie2 = propertie2;
}
}
测试结果正确:
transient-test.pngtransient小结:
- 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
- transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
- 被static关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
- 第三点可能有些人很迷惑,因为发现在Bean类中的p2字段前加上static关键字后,程序运行结果依然不变,即static类型的p2也读出来为“p2”了,这不与第三点说的矛盾吗?实际上是这样的:第三点确实没错(一个静态变量不管是否被transient修饰,均不能被序列化),反序列化后类中static型变量p2的值为当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的
但是这个关键字不一定好使,还有一个<b>Externalizable</b>接口,我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
看下demo:
package org.springframework.tests.beans;
import java.io.Externalizable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
/**
*
* @author holly.wang
* @since 4.2
*/
public class ExternalizableTest implements Externalizable,Serializable{
private transient String content = "ExternalizableTest.txt测试不管我是否被transient关键字修饰";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(content);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
content = (String) in.readObject();
}
public static void main(String[] args) throws Exception {
ExternalizableTest et = new ExternalizableTest();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
new File("D:/ExternalizableTest.txt")));
out.writeObject(et);
ObjectInput in = new ObjectInputStream(new FileInputStream(new File("D:/ExternalizableTest.txt")));
et = (ExternalizableTest) in.readObject();
System.out.println("holly.wang test"+et.content);
out.close();
in.close();
}
}
测试结果:
Externalizable_test_result.png
测试结果一致。
周末喽,回家喽!