持续学习&持续更新中…
守破离
【Java从零到架构师第③季】【13】Spring-scope_Converter_引入其他配置文件_SpEL表达式
一、引入其他配置文件
注意:
${username}
是Spring的保留字,如果有在applicationContext.xml中引入了properties文件的话,会获取到当前电脑的用户名
;如果没有引入properties文件的话,会获取到${username}
这个字符串本身,不能使用。classpath:
可省略,默认就是去classpath中去找
代码:
db.properties:
jdbc.url=jdbc:mysql://localhost:3306/bookstore
jdbc.username=root
jdbc.password=root
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="db.properties"/>
<!-- classpath: 可省略,默认就是去classpath中去找-->
<!-- <context:property-placeholder location="classpath:db.properties"/> -->
<bean id="connection" class="programmer.lp.obj.ConnectionFactoryBean">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
二、PropertySourcesPlaceholderConfigurer
property-placeholder:
<context:property-placeholder location="db.properties"/>
@PropertySource:
@Configuration
@PropertySource("db.properties")
public class MainConfiguration {
// ...
}
PropertySourcesPlaceholderConfigurer:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="db.properties"/>
</bean>
万变不离其宗:将bean放入IoC容器中即可:
@Configuration
public class MainConfiguration {
// @Bean必须配合@Configuration使用
@Bean
public PropertySourcesPlaceholderConfigurer configurer() {
PropertySourcesPlaceholderConfigurer configurer =
new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("db.properties"));
return configurer;
}
}
使用:
@Component
public class Dog {
private String name;
@Value("${dev.username}")
public void setName(String name) {
this.name = name;
}
// ...
}
三、SpEL表达式
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions
public class Food {
public String getTestName() {
return "小红苹果";
}
}
public class Human {
private String name;
private String favoriteFood;
private Date birthday;
private Food food;
private Integer fnByteLength;
public Human() {
}
// getter setter
}
<bean id="apple" class="programmer.lp.domain.Food"/>
<bean id="baby" class="programmer.lp.domain.Human">
<property name="name" value="小宝"/>
<property name="food" value="#{apple}"/>
<property name="favoriteFood" value="#{apple.testName}"/>
<!-- <property name="favoriteFood" value="#{apple.getTestName()}"/> -->
<property name="fnByteLength" value="#{apple.testName.bytes.length}"/>
<!-- <property name="foodNameLength" value="#{apple.getTestName().getBytes().length}"/> -->
<property name="birthday" value="#{new java.util.Date()}"/>
</bean>
四、scope
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-scopes
代码:
<?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="person" class="programmer.lp.domain.Person" lazy-init="true" /> -->
<bean id="person" class="programmer.lp.domain.Person" scope="prototype" />
</beans>
注意:
-
scope的默认值为singleton。
-
singleton的含义:通过同一个id,在同一个IoC容器中,永远只能获取到同一个实例(但并没有使用单例设计模式。单例设计模式:一个类在整个程序运行过程中只有1个实例)
-
当scope为
singleton
时,Spring在创建IoC容器时就会将其创建好并放入IoC容器,如果想要延迟其创建时机至初次调用时,可以使用lazy-init="true"
-
当scope为
prototype
时,每次调用getBean
时就会创建一个新的实例 -
也就是说,
lazy-init="true"
对于scope="prototype"
是没有意义的,lazy-init="true"
是搭配scope="singleton"
使用的。
五、Converter
1、内置的基本转换类型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="person" class="programmer.lp.domain.Person" p:age="10" p:birthday="2000/07/20">
<constructor-arg value="lpruoyu"/>
</bean>
</beans>
2、自定义Converter
注意:
- Spring会通过
conversionService
这个id从IoC容器中获取一个ConversionService
对象,通过ConversionService
对象去转换类型。所以id必须固定为conversionService
,不能使用其它的。 - 当bean的scope为singleton(默认)时,在同一个IoC容器中,使用同一个id获取到的永远会是同一个实例,所以自己注册的日期转换器会将Spring的日期转换器覆盖掉,因此如果项目中要用到
yyyy/MM/dd
这种格式的日期,就需要自己将yyyy/MM/dd
这种格式的日期添加至converters标签
。
代码:
DateConverter:
public class DateConverter implements Converter<String, Date> {
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String s) {
try {
return SIMPLE_DATE_FORMAT.parse(s);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
注册使用DateConverter:
方式一:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="programmer.lp.converter.DateConverter"/>
</set>
</property>
</bean>
<bean id="person" class="programmer.lp.domain.Person" p:birthday="2332-03-20">
<constructor-arg value="lpruoyu"/>
<property name="age" value="121"/>
</bean>
方式二:
<bean id="dateConverter" class="programmer.lp.converter.DateConverter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="dateConverter"/>
</set>
</property>
</bean>
<bean id="person" class="programmer.lp.domain.Person" p:birthday="2222-08-08">
<constructor-arg value="lpruoyu"/>
<property name="age" value="121"/>
</bean>
改进1:
上面的方式还是不够好,因为我们之所以使用IoC容器,让Spring去管理这些bean就是为了解耦和减少依赖,但是上面还是在类中把日期格式写死了。因此可以考虑做出以下改进:
DateConverter:
public class DateConverter implements Converter<String, Date> {
private String format;
public void setFormat(String format) {
this.format = format;
}
@Override
public Date convert(String dateStr) {
try {
return new SimpleDateFormat(format).parse(dateStr);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
注册使用DateConverter:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="programmer.lp.converter.DateConverter" p:format="yyyy-MM-dd"/>
<bean class="programmer.lp.converter.DateConverter">
<property name="format" value="yyyy年MM月dd日"/>
</bean>
</set>
</property>
</bean>
<bean id="person" class="programmer.lp.domain.Person"
p:age="121"
p:birthday="2888年3月2日">
<constructor-arg value="lpruoyu"/>
</bean>
改进2:
还可以考虑这样设计:
DateConverter:
public class DateConverter implements Converter<String, Date> {
private Set<String> formats;
public void setFormats(Set<String> formats) {
this.formats = formats;
}
@Override
public Date convert(String dateStr) {
if (null == formats || formats.isEmpty()) return null;
for (String format : formats) {
try {
return new SimpleDateFormat(format).parse(dateStr);
} catch (Exception e) {
System.out.println("不支持:<" + format + ">格式!");
}
}
return null;
}
}
注册使用DateConverter:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="programmer.lp.converter.DateConverter">
<property name="formats">
<set>
<value>yyyy/MM/dd</value>
<value>yyyy-MM-dd</value>
<value>yyyy_MM_dd</value>
<value>yyyy年MM月dd日</value>
</set>
</property>
</bean>
</set>
</property>
</bean>
<bean id="person1" class="programmer.lp.domain.Person" p:birthday="2000-12-11"/>
<bean id="person2" class="programmer.lp.domain.Person" p:birthday="2001_7_3"/>
<bean id="person3" class="programmer.lp.domain.Person" p:birthday="1111年11月11日"/>
参考
小码哥-李明杰: Java从0到架构师③进阶互联网架构师.
本文完,感谢您的关注支持!