mapengpeng1999@163.com 引入外部属性文件,SpringEL表达式,IOC容器中Bean的生命周期,

本文详细阐述了如何在Spring配置文件中加载外部属性文件,重点关注数据源配置,如C3P0连接池的配置,以及如何使用外部db.properties文件管理数据库连接信息。此外,介绍了SPEL表达式在配置中的应用和bean的生命周期管理,包括后置处理器的使用。
摘要由CSDN通过智能技术生成

spring配置文件中加载和读取外部属性文件

如何在spring配置文件中加载并读取外部的属性文件

使用外部属性文件,配置最多的就是数据源信息

<context:property-placeholder location="classpath:out.properties"/> 
<bean id="person" class="com.wanbang.outconf.Person" p:age="${age}" p:name="${name}">
</bean>

在conf类路径目录下new个普通File文件重命名为out.properties
out.properties文件中的中文会自动变为其它格式但不影响正常使用
age=20
name=\u8001\u59DC
我们通过数据库连接池的概念来讲解外部属性文件,
连接池的概念:在一个容器中预先创建好指定个数的数据库连接对象,在进行增删改查操作的时候,
从这个容器中拿出一个数据库连接对象来使用,完成操作之后,将数据库连接对象再还回到容器中,
这个容器就叫做数据库连接池。
目前来说,最常用的数据库连接池有两个:
- C3P0【比阿里的Druid要更早出现,现在使用比较少】
- 阿里的Druid【全世界都在用,而且现在是最流行的】
今天使用C3P0,后面我们在讲解SpringBoot 和 Cloud的时候使用阿里的Druid。

步骤一:导入jar文件

c3p0-0.9.1.2.jar

mysql-connector-java-5.1.37-bin.jar

步骤二:在SpringIOC容器xml配置文件中,配置连接池的bean

	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<!-- 属性注入驱动程序类名 -->
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<!-- 属性注入 数据库连接地址 -->
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/fornum"></property>
		<!-- 属性注入 数据库用户名-->
		<property name="user" value="root"></property>
		<!-- 属性注入 数据库密码 -->
		<property name="password" value="3306"></property>
		<!-- 属性注入 连接池中最大的连接数量 -->
		<property name="maxPoolSize" value="10"></property>
		<!-- 属性注入 连接池初始化的连接数量 -->
		<property name="initialPoolSize" value="5"></property>
	</bean>

步骤三:从连接池中获得数据库连接

public static void main(String[] args) throws SQLException {
	ApplicationContext act = new ClassPathXmlApplicationContext("db.xml");
	//DataSource 是 ComboPooledDataSource 的实现的子接口,记住是sql包下的DataSource
	DataSource dataSource = (DataSource) act.getBean("dataSource");
	Connection conn = dataSource.getConnection();//获取数据库连接的另外一种形式
	System.out.println(conn);
}
现在的程序已经可以获取到数据库连接,但是数据库的连接信息我们是直接配置在xml中,这种是不提倡的,
一般的开发都会将数据库的连接信息放置在外部的配置文件中。
使用外部属性文件步骤:

- 创建外部属性文件db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/forum
user=root
password=3306
maxSize=10
initSize=5

- 在Spring配置文件中引入外部属性文件

<context:property-placeholder location=“classpath:db.properties”/>

<!-- 引入外部属性文件,有两种方式:
		- 新的写法:在beans标签中使用使用context命名空间 ,context:property-placeholder标签
		- 老的写法:配置一个bean,定义外部文件的bean
	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
			<property name="location" value="classpath:db.properties"></property>
	</bean>
-->
	<context:property-placeholder location="classpath:db.properties"/>

- 在bean中使用外部属性文件的配置

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<!-- 属性注入驱动程序类名 -->
		<property name="driverClass" value="${driver}"></property>
		<!-- 属性注入 数据库连接地址 -->
		<property name="jdbcUrl" value="${url}"></property>
		<!-- 属性注入 数据库用户名-->
		<property name="user" value="${user}"></property>
		<!-- 属性注入 数据库密码 -->
		<property name="password" value="${password}"></property>
		<!-- 属性注入 连接池中最大的连接数量 -->
		<property name="maxPoolSize" value="${maxSize}"></property>
		<!-- 属性注入 连接池初始化的连接数量 -->
		<property name="initialPoolSize" value="${initSize}"></property>
</bean>
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${driver}"></property>
		<property name="jdbcUrl" value="${url}"></property>
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
		<property name="maxPoolSize" value="${maxSize}"></property>
		<property name="initialPoolSize" value="${initSize}"></property>
</bean>  
<context:property-placeholder location="classpath:db.properties"/> 
以后的开发中,我们获取数据库连接的方式都是使用此种形式,肯定是不会使用原生的JDBC完成的,
不管以后我们开发使用何种数据库操作的框架[Hibernate,SpringData+JPA,MyBatis],
都是使用连接池技术完成数据库连接的。

总结

属性文件的读取必须先导入属性文件
读取属性文件内容是以el表达式的方式
我们要了解连接池的配置,能够完成数据库连接操作

SpringEL表达式

SpringEL表达式的简介

Spring的表达式语言,简称SpEL,是一个支持运行时检查和操作对象的强大的表达式语言,和我们之前学习过的JSP中的EL表达式类似,SPEL使用的是#{}作为定界符,所有在大括号中的字符都被认为是SPEL,
SPEL为bean的属性进行动态赋值提供了非常大的便利,通过SPEL可以实现:
- 通过bean的ID对bean进行引用,类似于 ref标签,比ref更强大
- 可以调用方法以及引用对象的属性
- 可以进行计算(数学运算、比较运算、逻辑运算、三目运算等)
- 支持正则表达式
体验SPEL
	<bean id="car" class="com.wanbangee.spel.Car">
		<property name="brand" value="Audi"></property>
		<property name="color" value="red"></property>
		<property name="maxSpeed" value="200"></property>
		<property name="price" value="300000"></property>
		<!-- 通过SPEL表达式可以获得类中的常量 -->
		<property name="perimeter" value="#{T(java.lang.Math).PI*60}"></property>
	</bean>
	<bean id="person" class="com.wanbangee.spel.Person">
		<property name="car" ref="car2"></property>
		<property name="name" value="zwj"></property>
		<!-- 使用了三目运算符和 关系运算 -->
		<property name="level" value="#{car2.price >= 300000?'金领':'白领'}"></property>
	</bean>
//外面用了双引号里面就用单引号,反之亦反。

SpEL的使用

- SPEL表达式可以使用字面量(意义不大),字面量仅限于字符串、基本数据类型

<!-- SPEL使用字面量 外面用了双引号里面就用单引号,反之亦反。-->
	<bean id="car3" class="com.wanbangee.spel.Car">
		<property name="brand" value="#{'JETU'}"></property>
		<property name="color" value='#{"red"}'></property>
		<property name="maxSpeed" value="#{200}"></property>
		<property name="price" value="#{100000.0}"></property>
		<!-- 通过SPEL表达式可以获得类中的常量 -->
		<property name="perimeter" value="#{T(java.lang.Math).PI*60}"></property>
		<property name="car" ref="car3"></property>
	</bean>

- 引用bean、属性、方法

<bean id="person" class="com.wanbangee.spel.Person">
		<!-- SpringEL 表达式引用bean 相当于ref属性-->
		<property name="car" value="#{car3}"></property>
		<!-- SpringEL 表达式引用bean的属性 -->
		<!-- <property name="name" value="#{car3.brand}"></property> -->
		<!-- SpringEL 表达式引用bean的方法(非私有),还可以链式操作 -->
		<property name="name" value="#{car3.getBrand().toLowerCase()}"></property>
		<!-- 使用了三目运算符和 关系运算 -->
		<property name="level" value="#{car2.price >= 300000?'金领':'白领'}"></property>
</bean>
- SPEL支持数学运算
加减乘除取模
- SPEL支持关系运算
< 、>、 <=、 >=、 ==、 !=   所有的关系运算结果 是boolean类型
- SPEL支持逻辑运算:xml中支持使用&& 表示并且,||表示或者,!表示非,但是不推荐使用,xml中推荐使用效果相同的and,or,not
and(&&) 表示并且,连接多个boolean类型的运算符
or(||) 表示或者,连接多个boolean类型的运算符
not(!) 表示非,后面跟上一个boolean类型的运算符
- SPEL支持三目运算符
- SPLE支持正则表达式
<!-- 使用正则判断 -->
<property name="email" value="#{car3.getBrand() matches '\w{3,5}@\w{3,8}.(com|cn)'}">	</property>

- SPEL支持调用静态属性(上面用到的PI)和静态方法(四舍五入方round(100000.534))
value="#{T(java.lang.Math).PI*60
<property name="price" value="#{T(java.lang.Math).round(100000.53452345234)}"></property>

SpringEL表达式操作类似于jsp中的EL表达式,使用#{}作为定界符,可以进行一些常用的操作。

IOC容器中Bean的生命周期

我们之前讲解过Servlet生命周期,生命周期就是从初始化到销毁的过程,那么我们SpringIOC容器是用来管理bean的,
所以又叫做Bean容器或者IOC容器,既然bean是在容器中管理的,那么肯定在容器中存在生命周期的过程。
刚刚我们看到了bean的实例化和bean的设置属性到bean的使用,但是这些过程不能够完全的表示bean的生命周期,
在Spring中,可以配置bean的初始化和销毁操作。
package com.wanbangee.life;
public class Person {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		System.out.println("set属性注入");
	}
	public Person() {
		System.out.println("调用无参构造器");
	}
	@Override
	public String toString() {
		return "Person [name=" + name + "]";
	}
	public void init() {
		System.out.println("bean初始化");
	}
	public void destroy() {
		System.out.println("bean销毁");
	}
}

	<!-- 
		init-method : 配置bean的初始化方法
		destroy-method :配置bean的销毁方法
	 -->
	<bean id="person" class="com.wanbangee.life.Person" init-method="init" 
	destroy-method="destroy">
		<property name="name" value="牛魔王"></property>
	</bean>
	
	
	//1 创建IOC容器并获得IOC容器对象
	ConfigurableApplicationContext act = new ClassPathXmlApplicationContext("life.xml");
	//2 获得IOC容器中管理的bean
	Person person = (Person)act.getBean("person");
	//3 使用bean
	System.out.println(person);
	//4 关闭容器,此接口ConfigurableApplicationContext有关闭容器的close方法
	//接口ConfigurableApplicationContext是接口ApplicationContext的子接口
	act.close();
我们现在可以完整的通过程序运行观察bean的生命周期了:
- IOC容器初始化时,bean被实例化创建 ,调用无参构造器
- 调用bean的setter方法进行属性设置
- bean初始化
- bean可以正常使用了(打印bean对象,Person [name=牛魔王])
- bean销毁(IOC容器关闭的时候bean进行销毁)
为了更加细致的观察bean的生命周期,我们可以使用bean的后置处理器。

bean的后置处理器

BeanPostProcessor这个接口表示的是bean的后置处理器,里面有两个方法:
Ctrl+Shift+t查看BeanPostProcessor这个接口
Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException 
表示在bean初始化方法之前执行
Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException
表示在bean初始化方法之后执行
object : 是IOC容器中管理的bean的对象
beanName : 是IOC容器中管理的bean的名称
实现bean的后置处理器:
- 定义实现类,实现BeanPostProcessor接口,并且复写两个方法
package com.wanbangee.life;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
//postProcessBeforeInitialization : 在bean初始化方法之前执行
@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("执行bean后置处理器的前置方法.....");
		return bean;
	}
//postProcessAfterInitialization : 在bean初始化方法之后执行
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("执行bean后置处理器的后置方法.....");
		return bean;
	}
}
编写好了之后我们需要进行配置
- 配置bean的后置处理器:当做普通的bean一样配置到IOC容器中
<!-- 后置处理器和普通bean一样配置到IOC容器中 
beanPostProcessor对象名,com.wanbangee.life.MyBeanPostProcessor接口的实现类-->
<bean id="beanPostProcessor" class="com.wanbangee.life.MyBeanPostProcessor"></bean>
这个时候bean的后置处理器就可以使用了,测试一下,这个时候,bean的生命周期变成了:
- bean实例化
- 调用bean的setter方法
- 执行bean后置处理器的前置方法
- bean初始化
- 执行bean后置处理器的后置方法
- bean可以使用了
- bean销毁(容器关闭的时候bean进行销毁)
这个时候肯定会问:bean的后置处理器有啥作用?
- bean后置处理器的前置和后置方法可以获得bean的ID和bean的信息
- bean的后置处理器的后置方法返回的bean就是IOC容器中管理的bean,我们可以偷偷的将bean换掉
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
postProcessBeforeInitialization : 在bean初始化方法之前执行。
String beanName获取bean就是bean ID。
Object bean就是获取bean信息如控制台打印的Person [name=牛魔王]
*/
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("执行bean后置处理器的前置方法....." + beanName);
		System.out.println("bean..........." + bean);
		return bean;
	}

/**
postProcessAfterInitialization : 在bean初始化方法之后执行
返回值即是IOC容器中管理的bean
*/
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("执行bean后置处理器的后置方法....."  + beanName);
		System.out.println("bean..........." + bean);
		Person person = new Person();
	//这里的person就是个用来接收数据的参数
		person.setName("铁扇公主");
		return person;
	}
}
既然可以偷梁换柱了,我们就可以将bean进行一定的过滤:如果bean的name属性值为“狗蛋”,我们可以换成“Gog egg”
public class MyBeanPostProcessor implements BeanPostProcessor {
//postProcessBeforeInitialization : 在bean初始化方法之前执行
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
//	postProcessAfterInitialization : 在bean初始化方法之后执行
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//返回值即是IOC容器中管理的bean
		Person person = (Person) bean;
		if(person.getName().equals("狗蛋")) {
			person.setName("Gog egg");
		}
		return person;
	}
}

对于Bean的声明周期只需要了解一下,后期的开发中基本上不会使用

工厂方法和FactoryBean

在以后的开发中,最常用的配置bean的方式,就是通过全类名反射的方式,

这一章节内容工厂方法和FactoryBean,大家只需要了解。

静态工厂方式

通过静态工厂方式创建bean指的是:
- 调用静态方式方法创建bean是将对象的创建的过程封装到静态方法中,
当客户端需要对象时,只需要简单的调用静态方法,而不关系对象创建的细节。
- 要声明通过静态方式创建bean,需要在bean的class属性中指拥有该工厂的方式的类,
同时在Factory-method属性中,指定工厂方法名称, 最后使用<constructor-arg>标签为方法传递参数。

步骤一:新建类Car,brand,color,price
步骤二:创建静态工厂类,提供静态属性和静态块
package com.wanbangee.factorymehodandfactorybean;
import java.util.HashMap;
import java.util.Map;
public class StaticFactory {
	//定义静态属性Map
	private static Map<String,Car> cars = new HashMap<>();
	//定义静态块
	static {
		cars.put("小一", new Car("Bnze","white",500000.0));
		cars.put("小二", new Car("BMW","black",500000.0));
		cars.put("小三", new Car("Audi","red",500000.0));
	}
	//定义静态方法:通过key取得Car对象
	public static Car getCar(String key) {
		return cars.get(key);
	}
}
步骤三:配置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,需要在bean的class属性中指拥有该工厂的方式的类,
		同时在factory-method属性中,指定工厂方法名称, 最后使用<constructor-arg>元素为方法传递参数
		
		id:配置的bean实际上就是我们要获取的bean
		class:注意配置的不是要获取的bean的全类名,而是工厂方式的全类名
		factory-method:配置的是静态工厂方法名称,即获取bean的方法名称
		constructor-arg:如果静态工厂方法有入参,则使用此标签传递
	 -->
	<bean id="car" class="com.wanbangee.factorymehodandfactorybean.StaticFactory"        		factory-method="getCar">
		<constructor-arg value="小三"></constructor-arg>
	</bean>
</beans>

实例工厂方式

通过实例工厂方法创建bean,是将对象的创建过程封装到另外一个对象的实例【构造方法】方法中去,
当客户需要请求对象时,只需要简单的配置实例方法而不用去关心对象创建的细节。
步骤一:新建类Car,brand,color,price
步骤二:创建实例工厂
package com.wanbangee.factorymehodandfactorybean;
import java.util.HashMap;
import java.util.Map;
public class InstanceFactory {
	private Map<String,Car> cars = null;
	public InstanceFactory() {
		cars = new HashMap<>();
		cars.put("小一", new Car("Bnze","white",500000.0));
		cars.put("小二", new Car("BMW","black",500000.0));
		cars.put("小三", new Car("Audi","red",500000.0));
	}
	public Car getCar(String key) {
		return cars.get(key);
	}
}
步骤三:配置
<!-- 
		实例工厂本身是需要配置到IOC容器中
		factory-bean : 引用实例工厂的bean
		factory-method:配置的是实例工厂中获取bean的方法名称
		constructor-arg 标签: 如果实例工厂方法中获取bean的名称的方法有参数,则使用此标签传递
-->
	<bean id="instanceFactory" 		    			     class="com.wanbangee.factorymehodandfactorybean.InstanceFactory"></bean>
	 <bean id="car2" factory-bean="instanceFactory" factory-method="getCar">
	 	<constructor-arg value="小二"></constructor-arg>
	 </bean>

FactoryBean bean工厂

实现FactoryBean接口在SpringIOC容器中的配置:
- Spring中有两种类型的bean,一种就是普通bean,另外一种就是工厂bean,即FactoryBean
- 工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,
其返回的是该工厂Bean的getObject()方法所返回的对象。

步骤一:创建FactoryBean的实现类,目前我们需要管理的Bean的类型是Car,所以要指定泛型为Car
package com.wanbangee.factorymehodandfactorybean;
import org.springframework.beans.factory.FactoryBean;
public class CarFactoryBean implements FactoryBean<Car> {
//返回的对象即是IOC容器中管理的Bean
	@Override
	public Car getObject() throws Exception {
		return new Car("Chery","god",50000.0);
	}
//返回的是bean的类型
	@Override
	public Class<?> getObjectType() {
		return Car.class;
	}
//	返回的bean是否是单例的bean
	@Override
	public boolean isSingleton() {
		return true;
	}
}
步骤二:配置
 <!-- FactoryBean配置-->
<bean id="car3" class="com.wanbangee.factorymehodandfactorybean.CarFactoryBean">
</bean>

获取Bean的四种配置方式:

<!-- 第一种配置bean的方式:通过全类名反射-->
	 <bean id="car1" class="com.wanbangee.factorymehodandfactorybean.Car">
	 	<property name="brand" value="Ford"></property>
	 	<property name="color" value="Green"></property>
	 	<property name="price" value="100000"></property>
	 </bean>
	 
<!-- 第二种配置bean的方式:静态工厂方法
	要声明通过静态方式创建bean,需要在bean的class属性中指拥有该工厂的方式的类,
	同时在factory-method属性中,指定工厂方法名称, 最后使用<constructor-arg>元素为方法传递参数
		
	id:配置的bean实际上就是我们要获取的bean
	class:注意配置的不是要获取的bean的全类名,而是工厂方式的全类名
	factory-method:配置的是静态工厂方法名称,即获取bean的方法名称
	constructor-arg:如果静态工厂方法有入参,则使用此标签传递
-->
	<bean id="car" class="com.wanbangee.factorymehodandfactorybean.StaticFactory"     			factory-method="getCar">
		<constructor-arg value="小三"></constructor-arg>
	</bean>
	
<!-- 第三种配置bean的方式:实例工厂方法
	实例工厂本身是需要配置到IOC容器中
	factory-bean : 引用实例工厂的bean
	factory-method:配置的是实例工厂中获取bean的方法名称
	constructor-arg 标签: 如果实例工厂方法中获取bean的名称的方法有参数,则使用此标签传递
-->
	 <bean id="instanceFactory" class="com.wanbangee.factorymehodandfactorybean.InstanceFactory"></bean>
	 <bean id="car2" factory-bean="instanceFactory" factory-method="getCar">
	 	<constructor-arg value="小二"></constructor-arg>
	 </bean>
	 
<!-- 第四种配置bean的方式:FactoryBean配置-->
	  <bean id="car3" class="com.wanbangee.factorymehodandfactorybean.CarFactoryBean">
	  </bean>

虽然现在有4种Bean的获取方式,但是第一种肯定是最常用,也是最好用的。

就是之前一直用的最常用的配置bean的方式,就是通过全类名反射的方式。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值