9 继承bean配置
本文上接Spring学习总结一,在平时的开发中会有子类父类,同样在bean的配置中也可以从在父bean和子bean,为了进行继承bean的学习,我们重新建一个包:com.alibaba.beans.relation。这个包里面的类分别如上文中提到过的,代码分别如下:(当然了如果没看上文的也不影响,我们只不过是为了方便,其实在这个包中的类是可以独立为一个小的demo的)。
package com.alibaba.beans.relation;
public class Address
{
private String city;
private String street;
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
public String getStreet()
{
return street;
}
public void setStreet(String street)
{
this.street = street;
}
@Override
public String toString()
{
return "Address [city=" + city + ", street=" + street + "]";
}
}
package com.alibaba.beans.relation;
public class Car
{
private String brand;
private double price;
public String getBrand()
{
return brand;
}
public void setBrand(String brand)
{
this.brand = brand;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
@Override
public String toString()
{
return "Car [brand=" + brand + ", price=" + price + "]";
}
}
package com.alibaba.beans.relation;
public class Person
{
private String name;
private Address address;
private Car car;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Address getAddress()
{
return address;
}
public void setAddress(Address address)
{
this.address = address;
}
public Car getCar()
{
return car;
}
public void setCar(Car car)
{
this.car = car;
}
@Override
public String toString()
{
return "Person [name=" + name + ", address=" + address + ", car=" + car + "]";
}
}
package com.alibaba.beans.relation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-realtion.xml");
Address address = (Address) ctx.getBean("address1");
System.out.println("address" + address);
address = (Address) ctx.getBean("address2");
System.out.println("address2" + address);
}
}
<?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.xsd">
<bean id="address1" class="com.alibaba.beans.relation.Address"
p:city="BeiJing" p:street="yangguang"></bean>
<span style="font-family:SimSun;"><!--这里的address2并没有配置class属性,也没有配置city属性,程序却可以正常运行?这是为什么尼,因为它通过parent
属性继承了address1的值,这里address1是父bean,address2是子bean --></span>
<bean id="address2" parent="address1" p:street="wudaokou"></bean>
</beans>
总结:Spring 允许继承 bean 的配置, 被继承的 bean 称为父 bean. 继承这个父 Bean 的 Bean 称为子 Bean。子 Bean 从父Bean 中继承配置, 包括 Bean 的属性配置;子 Bean 也可以覆盖从父 Bean 继承过来的配置;父 Bean 可以作为配置模板, 也可以作为 Bean 实例. 若只想把父 Bean 作为模板, 可以设置 <bean> 的abstract 属性为 true, 这样 Spring 将不会实例化这个 Bean,并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire, abstract 等.也可以忽略父 Bean 的 class 属性, 让子 Bean 指定自己的类, 而共享相同的属性配置. 但此时 abstract 必须设为 true。
10 依赖Bean配置
如果说对于一个Person类,每一个person对象都要有一辆car,那么此时称Person依赖Car,配置依赖关系可以通过depends-on属性来指定,只需要修改上边的beans-relations.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" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address1" class="com.alibaba.beans.relation.Address"
p:city="BeiJing" p:street="yangguang"></bean>
<bean id="address2" parent="address1" p:street="wudaokou"></bean>
<bean id="car" class="com.alibaba.beans.relation.Car" p:brand="bmw"
p:price="20000000"></bean>
<!-- 假设在初始化每一个person的时候我们要求每人要有一辆车,此时就成为person依赖car -->
<bean id="person" class="com.alibaba.beans.relation.Person"
p:name="MaJie" p:address-ref="address1" depends-on="car"></bean>
</beans>
总结:Spring 允许用户通过 depends-on 属性设定 Bean 前置依赖的Bean,前置依赖的 Bean 会在本 Bean 实例化之前创建好。如果前置依赖于多个 Bean,则可以通过逗号或空格的方式配置 Bean 的名称。
11 Bean的作用域
Bean的作用域在默认情况下为singleton,即我们在配置文件中没有配置scope属性的时候就是默认值,在这里我们利用上面com.alibaba.beans.realtion中的类,然后新建一个beans-scope.xml,新建一个com.alibaba.beans.scope包以便写测试类Main。下面给出beans-scope.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.xsd">
<bean id="car" class="com.alibaba.beans.relation.Car">
<property name="brand" value="audi"></property>
<property name="price" value="20000000"></property>
</bean>
</beans>
package com.alibaba.beans.scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.beans.relation.Car;
public class Main
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");
Car car1 = (Car) ctx.getBean("car");
Car car2 = (Car) ctx.getBean("car");
System.out.println(car1 == car2);
}
}
由于在配置文件中我们并没有申明scope,所以默认为singleton,所以测试类的输出结果为“true”,但是要是改为prototype,则测试结果就为false。下面来总结一下:
总结:在 Spring 中, 可以在 <bean> 元素的 scope 属性里设置 Bean 的作用域. 默认情况下, Spring 只为每个在 IOC 容器里声明的 Bean 创建唯一一个实例, 整个 IOC 容器范围内都能共享该实例:所有后续的 getBean() 调用和 Bean 引用都将返回这个唯一的 Bean 实例.该作用域被称为 singleton, 它是所有 Bean 的默认作用域.对于scope属性的取值总结如下。
12 Spring使用外部属性
有时我们需要在使用Spring时使用外部属性,比如我们在使用数据源时,我们先来做一个不使用外部属性的范例。首先加入C3P0和mysqljdbc的jar包。然后为了便于学习我们在类路劲下新建一个beans-properties.xml的配置文件,最后与上面讲解的一样(为了便于测试,我们新建一个Main类,它在com.alibaba.beans.properties)。下面给出在没使用Spring外部属性时配置数据源的代码。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///test"></property>
</bean>
</beans>
ackage com.alibaba.beans.properties;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main
{
public static void main(String[] args) throws Exception
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-properties.xml");
DataSource ds = (DataSource) ctx.getBean("dataSource");
System.out.println(ds.getConnection());
}
}
在实际开发中,因为对于Spring的配置文件将会很复杂,所以我们为了让开发变得更简洁、方便,我们通常会考虑使用外部属性,此时我们在类路径下新建一个名为db.properties的文件。这里先给出该文件的代码以及使用了外部属性的配置文件,然后我们去做一个小结,读者可以参看小结去理解为什么那样做。
db.properties文件:
user=root
password=123456
driverclass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
使用了外部属性的beans-properties.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" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 在spring2.5之后使用下面这个标签去指定外部属性的位置 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 使用外部属性化文件的属性 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
</beans>
总结:1.在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分离。 2.Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 ${var} 的变量, PropertyPlaceholderConfigurer 从属性文件里加载属性, 并使用这些属性来替换变量。 3.Spring 还允许在属性文件中使用 ${propName},以实现属性之间的相互引用。
13 SpEL
SpEL -- Spring Expression Language. Spring的表达式语言。有关SpEL的总结,我们在代码示例完之后做一个总结。这里同样为了便于学习,新建一个包com.alibaba.beans.spel。然后在这个包下面新建以下给出代码的类和一个xml文件。在xml配置文件中我写了一点注释可以帮助理解。
package com.alibaba.beans.spel;
public class Address
{
private String city;
private String street;
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
public String getStreet()
{
return street;
}
public void setStreet(String street)
{
this.street = street;
}
@Override
public String toString()
{
return "Address [city=" + city + ", street=" + street + "]";
}
}
package com.alibaba.beans.spel;
public class Car
{
private String brand;
private double price;
private double tyrePermeter;
public String getBrand()
{
return brand;
}
public void setBrand(String brand)
{
this.brand = brand;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public double getTyrePermeter()
{
return tyrePermeter;
}
public void setTyrePermeter(double tyrePermeter)
{
this.tyrePermeter = tyrePermeter;
}
@Override
public String toString()
{
return "Car [brand=" + brand + ", price=" + price + ", tyrePermeter=" + tyrePermeter + "]";
}
}
package com.alibaba.beans.spel;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-spel.xml");
Address address = (Address) ctx.getBean("address");
System.out.println(address);
System.out.println("---------------------我是测试分割线------------------");
Car car = (Car) ctx.getBean("car");
System.out.println(car);
System.out.println("------------------------我是分割线-------------------");
Person person = (Person) ctx.getBean("person");
System.out.println(person);
}
}
package com.alibaba.beans.spel;
public class Person
{
private String name;
private Car car;
// city引用adress bean的city属性
private String city;
// 根据car的price确定info:Car的price >= 300000 是金领
// 否则是白领
private String info;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Car getCar()
{
return car;
}
public void setCar(Car car)
{
this.car = car;
}
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
public String getInfo()
{
return info;
}
public void setInfo(String info)
{
this.info = info;
}
@Override
public String toString()
{
return "Person [name=" + name + ", car=" + car + ", city=" + city + ", info=" + info + "]";
}
}
<?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.xsd">
<bean id="address" class="com.alibaba.beans.spel.Address">
<!-- 使用SpEL表达式为属性赋一个字面值 -->
<property name="city" value="#{'beijing'}"></property>
<property name="street" value="WuDaoKou"></property>
</bean>
<bean id="car" class="com.alibaba.beans.spel.Car">
<property name="brand" value="audi"></property>
<property name="price" value="500000"></property>
<!-- 使用SpEL引用类的静态属性 <div>•<span style="color:red;">调用静态方法或静态属性</span><span style="color:black;">:通过</span><span style="color:red;">T() </span><span style="color:black;">调用</span><span style="color:black;">一</span><span style="color:black;">个类的静态方法,它</span><span style="color:black;">将返回一</span><span style="color:black;">个</span><span style="color:black;">ClassObject</span><span style="color:black;">,然后再</span><span style="color:black;">调用相应的</span><span style="color:black;">方法或属性:</span></div>-->
<property name="tyrePermeter" value="#{T(java.lang.Math).PI * 80}"></property>
</bean>
<bean id="person" class="com.alibaba.beans.spel.Person">
<property name="name" value="Tom"></property>
<!-- 使用SpEL来引用其他的bean -->
<property name="car" value="#{car}"></property>
<!-- 使用SpEL来引用其他bean的属性 -->
<property name="city" value="#{address.city}"></property>
<!-- 使用SpEL来引用选择运算符 -->
<property name="info" value="#{car.price > 300000 ? '金领 ' : '白领'}"></property>
</bean>
</beans>
总结:Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言。语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL。SpEL 为 bean 的属性进行动态赋值提供了便利。通过 SpEL 可以实现:
- 通过 bean 的 id 对 bean 进行引用;
- 调用方法以及引用对象中的属性;
- 计算表达式的值;
- 正则表达式的匹配。
14 IOC容器中bean的生命周期方法
Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.Spring IOC 容器对 Bean 的生命周期进行管理的过程:(1).通过构造器或工厂方法创建 Bean 实例;(2).为 Bean 的属性设置值和对其他 Bean 的引用;(3).调用 Bean 的初始化方法;(4).Bean 可以使用了;(5).当容器关闭时, 调用 Bean 的销毁方法。在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.这里为了学习和了解IOC容器张bean的生命周期,我们同样新建一个com.alibaba.beans.cycle的包。这个包里包含Car、Main类:现给出配置文件和类的代码:
package com.alibaba.beans.cycle;
public class Car
{
public Car()
{
System.out.println("Car's Constructor...");
}
private String brand;
public void setBrand(String brand)
{
System.out.println("setBrand...");
this.brand = brand;
}
public void init()
{
System.out.println("init...");
}
public void destroy()
{
System.out.println("destroy...");
}
}
package com.alibaba.beans.cycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main
{
public static void main(String[] args)
{
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
Car car = (Car) ctx.getBean("car");
System.out.println(car);
ctx.close();
}
}
<?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.xsd">
<bean id="car" class="com.alibaba.beans.cycle.Car" init-method="init"
destroy-method="destroy">
<property name="brand" value="audi"></property>
</bean>
</beans>
读者可以通过运行上面代码来了解IOC容器中bean的生命周期。
15 创建Bean后置处理器
如果说我们想更细粒度的去管理bean的生命周期,我们就可以通过创建bean的后置处理器去管理。我这里先给出代码和配置文件,然后再下面做一个总结,读者可以通过代码和总结相结合去学习和理解。PS:变化的只是上面的配置文件和新添加的一个类。
package com.alibaba.beans.cycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MybeanProcessor implements BeanPostProcessor
{
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
System.out.println("postProcessAfterInitialization:" + bean + ", " + beanName);
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
System.out.println("postProcessBeforeInitialization" + bean + ", " + beanName);
return 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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="car" class="com.alibaba.beans.cycle.Car" init-method="init"
destroy-method="destroy">
<property name="brand" value="audi"></property>
</bean>
<bean class="com.alibaba.beans.cycle.MybeanProcessor"></bean>
</beans>
读者可以通过运行结果和下面的总结去理解学习创建bean后置处理器。
总结:(1).Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.(2).Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.(3).对Bean 后置处理器而言, 需要实现BeanPostProcessor(org.springframework.beans.factory.config.BeanPostProcessor)接口. 在初始化方法被调用前后, Spring 将把每个 Bean 实例分别传递给上述接口的以下两个方法:
当添加了bean后置处理器之后,bean的生命周期为:(1).通过构造器或工厂方法创建 Bean 实例;(2).–为 Bean 的属性设置值和对其他Bean的引用;(3).将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法;(4).调用 Bean 的初始化方法;(5).将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization方法;(6).Bean 可以使用了;(7).当容器关闭时, 调用 Bean 的销毁方法。
后续将继续在Spring学习总结一(续2)中进行总结,所有的代码将在总结一完结时给出下载地址。