2023年6月21日发(作者:)
盘点SpringFramework中的那些特殊符号在使⽤spring framework过程中你会遇到形形⾊⾊的特殊符号:$、
#、
{ }、
[]、?、:,它们在不同的场合有不同的含义,笔者试着做⼀个集中对⽐,⽅便记忆。0. ⽬录1. 配置参数引⽤符:${}2. Url Path 变量占位符:{}3. Expand Url Template:{}4. SQL查询占位符:?与:5. 属性访问表达式(Bean Manipulation Expression)6. SpEL中的表达式( Spring Expression Language)7. Slf4j Logger 消息占位符1. 配置参数引⽤符:${}1. 配置参数引⽤符:${}1.1 property-placeholderSpring framework的核⼼是对象⼯⼚(BeanFactory ),⽤于按照图纸构造对象、装配依赖、注⼊参数。这份图纸,在以前是⽤xml(),现在习惯直接⽤注解来表⽰。装配⼯⼚是应当⽀持动态化配置的,那⾸先要解决的问题就是设置参数⽂件并引⽤其中的参数。Java编程中习惯使⽤properties做参数⽂件,spring同样⽀持读取参数⽂件,并且还能将参数⽤在对象构造过程。
ties中⼤约是这样ClassName==jdbc:hsqldb:hsql://production:me=rd=root如此⼀来,spring在构造dataSource时,会将真正的值jdbc:hsqldb:hsql://production:9002填⼊类的setUrl⽅法中。1.2 @Valuexml⽂件⾥参数替换的机制在注解配置下⼀脉相承:@Configuration
@ImportResource("classpath:/config/ties")
public class AppConfig {
@Value("${}")
private String url;
@Value("${me}")
private String username;
@Value("${rd}")
private String password;
@Bean public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}}这个类在spring⾥构造时,String url的值会设置为参数⽂件中的值jdbc:hsqldb:hsql://production:9002。注意,如果不加${},则相当于传递⼀个字符串常量值:String url = "${}",不会报异常,但这样就没起到参数值注⼊的作⽤。1.3 指定默认值⽀持在引⽤参数名时给定⼀个默认值:@Value("${me:root}")
private String username;
上例中,冒号后⾯的root就是默认值,如果外部参数缺少了,那么默认值就会⽣效,默认值始终是字符串常量。1.4 延伸阅读1. 参数引⽤符号中,前缀和后缀实际上是可以⾃定义的。可以参考class PropertySourcesPlaceholderConfigurer 的说明。2.
${}不仅仅是简单的字符串引⽤。spring在拿到参数的字符串值以后,还会根据⽬标的类型来做转换。这套转换机制的实现是:PropertyEditor、Converter、ConversionService。例如:@Value("${}")URL url;Spring在拿到字符串值以后,会先做类型转换,这样url字段就能正确拿到URL类型的值。常规的类型、格式,spring都已经内建⽀持,例如:数字、字符、⽇期、货币等等。你也可以⾃⼰定义⼀个转换⼯具来转换⾃⼰需要的类型,在@Configuration中定义⼀个@Bean,继承Converterinterface即可。这样,我们就能实现下⾯的转换:@Value("1,2")Point point;3. 许多的编程语⾔(例如bash、groovy、javascript),也是采⽤${}作为变量值引⽤的。2. Url Path 变量占位符{}2.1 PathVariable在web api中我们有时需要设计从path中提取变量值的情况,例如:@GetMapping("/user/{id}")public UserDto queryUserDetail(@PathVariable("id") String id);如此声明的话,将会匹配类似
/user/1 的url,并且spring在调⽤⽅法时,会将1作为参数值传⼊⽅法中。如果在引⽤时没有指定名字,则是按照顺序来提取。这个也在其他场合适⽤。2.2 延伸阅读Url 匹配机制的判断逻辑在chingCondition(HttpServletRequest)。中url中提取参数值的逻辑在tUriTemplateVariables(String, String)。3. Expand Url Template: {}在构造http url时,可以使⽤url expand feature:restTemplate .getForObject( "/hotels/{hotel}" , , "golden");也可以使⽤Map传名字和值://按照hash map key替换Map
Object(
"/hotels/{hotel}/rooms/{hotel}", , vars);//按照顺序替换String result = Object(
"/hotels/{hotel}/bookings/{booking}",
, "42", "21");这个设计背后使⽤的是UriComponentsBuilder类:URI uri = UriComponentsBuilder .fromUriString("/hotels/{hotel}") .queryParam("q", "{q}")
.encode() .buildAndExpand("Westin", "123")
.toUri();///hotels/Westin?q=1234. SQL查询占位符:“?”4.1 顺序占位符“?”熟悉SQL的朋友应该知道,SQL查询中有⼀类叫参数化查询(Prepared Statements)。使⽤参数化查询既能避免⾃⾏拼接SQL不当导致的注⼊漏洞,也能⽅便数据库去优化查询。SQL查询占位符采⽤的是问号的形式: (
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");该例中出现两个问号,在实际执⾏SQL时,会按顺序替换成Leonor、Watlling。另外记得⼀点,SQL占位符下标是从1开始的,在使⽤更底层的API时会遇到:ing(1,"Leonor")。4.2 具名占位符:param原⽣SQL仅⽀持问号占位符,spring在其基础上设计了具名占位符的机制。这个要使⽤ classNamedParameterJdbcTemplate :private NamedParameterJdbcTemplate namedParameterJdbcTemplate;public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return orObject(sql, namedParameters, );
}4.3 JPA 查询占位符JPA也同样⽀持参数占位符,但与SQL稍微不同,使⽤的是1、?2的形式:try (EntityManager em = EntityManager()) {
Query query = Query("from Product as p where ry = ?1");
ameter(1, category);
return ultList();
}4.4 @Query在spring-data⼯具包中的@Query也是⽀持JPA查询占位符的:public interface UserRepository extends JpaRepository
@Query("select u from User u where ddress = ?1")
User findByEmailAddress(String emailAddress);
}%是sql中的like匹配,
%?1相当于在 firstname的开头添加%来做like匹配,
?1%相当于在firstname的末尾添加%做like匹配。public interface UserRepository extends JpaRepository
@Query("select u from User u where ame like %?1")
List
pertyValue("username", "Alex");
pertyValue("roels[0]");pertyValue("eated");表达式count[2]account[COMPANYNAME]含义访问get、set⽅法,getName() setName()getAccount().setName() or getAccount().getName() methods.访问Array, list中的元素访问HashMap元素,没有引号Spring 中的bean wrap 与其他⼯具中的 bean util(apache commons、jodd)实现的是类似的功能,⽽spring的优势在于值类型转换这⼀块是重⽤⽂章之前提到的conversion service,类型转换友好。6. Spring ELSpring 从3.0开始引⼊表达式(spring expression, SpEL for short)引擎,⽤于整个spring ⽣态体系。6.1 #{}SpEL 围绕 spring ⽣态的需求⽽设计,可以在各个层次集成。如下的例⼦所⽰,⽤来从系统属性中查询值:
表达式之间的依赖关系也能处理:
⽰例中的变量引⽤是有依赖关系的,必须先计算randomNumber才能有numberGuess。6.2 @Value在@Value注解中也能直接使⽤SpEL,同样的#{}标记@Value("#{ systemProperties[''] }")
private Locale defaultLocale;6.3 延伸阅读SpEL⽀持众多特性,典型的有:运算: + 1900取值:members[0].name、officers['president'].构造List:{1,2,3,4}构造Map:{name:'Nikola',dob:'10-July-1856'}调⽤⽅法:'abc'.substring(1, 3)⾃定义函数:#myFn(1)引⽤Bean:@dataSource安全访问:placeOfBirth?.city更多的语法规则请看:spring el 的语法参考7. Slf4j Logger 消息占位符7. Slf4j Logger 消息占位符这个不算是spring的特性,但写代码经常⽤到。和其他的符号交织在⼀起容易混淆,因此值得拿出来⽐较。在slf4j⾥⽇志消息参数占位符是{}:import ;import Factory;static final Logger logger = ger();("user {} login success, time {}","张三",())设计采⽤顺序对应的匹配⽅法,占位符的次序和⼊参的顺序对应。最后可以在⽇志中看到:“user 张三 login success,time 2020-12-29T15:32:08.626Z”其中负责最终执⾏格式化的⼯具是:eFormatter :ormat(message,argumentArray).getMessage();,这个⼯具也可以拿来⾃⼰⽤
2023年6月21日发(作者:)
盘点SpringFramework中的那些特殊符号在使⽤spring framework过程中你会遇到形形⾊⾊的特殊符号:$、
#、
{ }、
[]、?、:,它们在不同的场合有不同的含义,笔者试着做⼀个集中对⽐,⽅便记忆。0. ⽬录1. 配置参数引⽤符:${}2. Url Path 变量占位符:{}3. Expand Url Template:{}4. SQL查询占位符:?与:5. 属性访问表达式(Bean Manipulation Expression)6. SpEL中的表达式( Spring Expression Language)7. Slf4j Logger 消息占位符1. 配置参数引⽤符:${}1. 配置参数引⽤符:${}1.1 property-placeholderSpring framework的核⼼是对象⼯⼚(BeanFactory ),⽤于按照图纸构造对象、装配依赖、注⼊参数。这份图纸,在以前是⽤xml(),现在习惯直接⽤注解来表⽰。装配⼯⼚是应当⽀持动态化配置的,那⾸先要解决的问题就是设置参数⽂件并引⽤其中的参数。Java编程中习惯使⽤properties做参数⽂件,spring同样⽀持读取参数⽂件,并且还能将参数⽤在对象构造过程。
ties中⼤约是这样ClassName==jdbc:hsqldb:hsql://production:me=rd=root如此⼀来,spring在构造dataSource时,会将真正的值jdbc:hsqldb:hsql://production:9002填⼊类的setUrl⽅法中。1.2 @Valuexml⽂件⾥参数替换的机制在注解配置下⼀脉相承:@Configuration
@ImportResource("classpath:/config/ties")
public class AppConfig {
@Value("${}")
private String url;
@Value("${me}")
private String username;
@Value("${rd}")
private String password;
@Bean public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}}这个类在spring⾥构造时,String url的值会设置为参数⽂件中的值jdbc:hsqldb:hsql://production:9002。注意,如果不加${},则相当于传递⼀个字符串常量值:String url = "${}",不会报异常,但这样就没起到参数值注⼊的作⽤。1.3 指定默认值⽀持在引⽤参数名时给定⼀个默认值:@Value("${me:root}")
private String username;
上例中,冒号后⾯的root就是默认值,如果外部参数缺少了,那么默认值就会⽣效,默认值始终是字符串常量。1.4 延伸阅读1. 参数引⽤符号中,前缀和后缀实际上是可以⾃定义的。可以参考class PropertySourcesPlaceholderConfigurer 的说明。2.
${}不仅仅是简单的字符串引⽤。spring在拿到参数的字符串值以后,还会根据⽬标的类型来做转换。这套转换机制的实现是:PropertyEditor、Converter、ConversionService。例如:@Value("${}")URL url;Spring在拿到字符串值以后,会先做类型转换,这样url字段就能正确拿到URL类型的值。常规的类型、格式,spring都已经内建⽀持,例如:数字、字符、⽇期、货币等等。你也可以⾃⼰定义⼀个转换⼯具来转换⾃⼰需要的类型,在@Configuration中定义⼀个@Bean,继承Converterinterface即可。这样,我们就能实现下⾯的转换:@Value("1,2")Point point;3. 许多的编程语⾔(例如bash、groovy、javascript),也是采⽤${}作为变量值引⽤的。2. Url Path 变量占位符{}2.1 PathVariable在web api中我们有时需要设计从path中提取变量值的情况,例如:@GetMapping("/user/{id}")public UserDto queryUserDetail(@PathVariable("id") String id);如此声明的话,将会匹配类似
/user/1 的url,并且spring在调⽤⽅法时,会将1作为参数值传⼊⽅法中。如果在引⽤时没有指定名字,则是按照顺序来提取。这个也在其他场合适⽤。2.2 延伸阅读Url 匹配机制的判断逻辑在chingCondition(HttpServletRequest)。中url中提取参数值的逻辑在tUriTemplateVariables(String, String)。3. Expand Url Template: {}在构造http url时,可以使⽤url expand feature:restTemplate .getForObject( "/hotels/{hotel}" , , "golden");也可以使⽤Map传名字和值://按照hash map key替换Map
Object(
"/hotels/{hotel}/rooms/{hotel}", , vars);//按照顺序替换String result = Object(
"/hotels/{hotel}/bookings/{booking}",
, "42", "21");这个设计背后使⽤的是UriComponentsBuilder类:URI uri = UriComponentsBuilder .fromUriString("/hotels/{hotel}") .queryParam("q", "{q}")
.encode() .buildAndExpand("Westin", "123")
.toUri();///hotels/Westin?q=1234. SQL查询占位符:“?”4.1 顺序占位符“?”熟悉SQL的朋友应该知道,SQL查询中有⼀类叫参数化查询(Prepared Statements)。使⽤参数化查询既能避免⾃⾏拼接SQL不当导致的注⼊漏洞,也能⽅便数据库去优化查询。SQL查询占位符采⽤的是问号的形式: (
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");该例中出现两个问号,在实际执⾏SQL时,会按顺序替换成Leonor、Watlling。另外记得⼀点,SQL占位符下标是从1开始的,在使⽤更底层的API时会遇到:ing(1,"Leonor")。4.2 具名占位符:param原⽣SQL仅⽀持问号占位符,spring在其基础上设计了具名占位符的机制。这个要使⽤ classNamedParameterJdbcTemplate :private NamedParameterJdbcTemplate namedParameterJdbcTemplate;public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return orObject(sql, namedParameters, );
}4.3 JPA 查询占位符JPA也同样⽀持参数占位符,但与SQL稍微不同,使⽤的是1、?2的形式:try (EntityManager em = EntityManager()) {
Query query = Query("from Product as p where ry = ?1");
ameter(1, category);
return ultList();
}4.4 @Query在spring-data⼯具包中的@Query也是⽀持JPA查询占位符的:public interface UserRepository extends JpaRepository
@Query("select u from User u where ddress = ?1")
User findByEmailAddress(String emailAddress);
}%是sql中的like匹配,
%?1相当于在 firstname的开头添加%来做like匹配,
?1%相当于在firstname的末尾添加%做like匹配。public interface UserRepository extends JpaRepository
@Query("select u from User u where ame like %?1")
List
pertyValue("username", "Alex");
pertyValue("roels[0]");pertyValue("eated");表达式count[2]account[COMPANYNAME]含义访问get、set⽅法,getName() setName()getAccount().setName() or getAccount().getName() methods.访问Array, list中的元素访问HashMap元素,没有引号Spring 中的bean wrap 与其他⼯具中的 bean util(apache commons、jodd)实现的是类似的功能,⽽spring的优势在于值类型转换这⼀块是重⽤⽂章之前提到的conversion service,类型转换友好。6. Spring ELSpring 从3.0开始引⼊表达式(spring expression, SpEL for short)引擎,⽤于整个spring ⽣态体系。6.1 #{}SpEL 围绕 spring ⽣态的需求⽽设计,可以在各个层次集成。如下的例⼦所⽰,⽤来从系统属性中查询值:
表达式之间的依赖关系也能处理:
⽰例中的变量引⽤是有依赖关系的,必须先计算randomNumber才能有numberGuess。6.2 @Value在@Value注解中也能直接使⽤SpEL,同样的#{}标记@Value("#{ systemProperties[''] }")
private Locale defaultLocale;6.3 延伸阅读SpEL⽀持众多特性,典型的有:运算: + 1900取值:members[0].name、officers['president'].构造List:{1,2,3,4}构造Map:{name:'Nikola',dob:'10-July-1856'}调⽤⽅法:'abc'.substring(1, 3)⾃定义函数:#myFn(1)引⽤Bean:@dataSource安全访问:placeOfBirth?.city更多的语法规则请看:spring el 的语法参考7. Slf4j Logger 消息占位符7. Slf4j Logger 消息占位符这个不算是spring的特性,但写代码经常⽤到。和其他的符号交织在⼀起容易混淆,因此值得拿出来⽐较。在slf4j⾥⽇志消息参数占位符是{}:import ;import Factory;static final Logger logger = ger();("user {} login success, time {}","张三",())设计采⽤顺序对应的匹配⽅法,占位符的次序和⼊参的顺序对应。最后可以在⽇志中看到:“user 张三 login success,time 2020-12-29T15:32:08.626Z”其中负责最终执⾏格式化的⼯具是:eFormatter :ormat(message,argumentArray).getMessage();,这个⼯具也可以拿来⾃⼰⽤
发布评论