Spring Framework学习小记

Spring IoC原理

Spring IoC原理——Marchon

 

Spring Expression Language(SpEL)

The Spring Expression Language (“SpEL” for short) is a powerful expression language that supports querying and manipulating an object graph at runtime. The language syntax is similar to Unified EL but offers additional features, most notably method invocation and basic string templating functionality.

详情参阅:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions

模式配置(SpelCompilerMode):解释执行模式(OFF)、编译模式(IMMEDIATE)、混合模式(MIXED)

使用示例:

Bean中使用(Expressions in Bean Definitions):

 1 //1
 2 public class FieldValueTestBean {
 3 
 4     @Value("#{ systemProperties['user.region'] }")
 5     private String defaultLocale;
 6 
 7     public void setDefaultLocale(String defaultLocale) {
 8         this.defaultLocale = defaultLocale;
 9     }
10 
11     public String getDefaultLocale() {
12         return this.defaultLocale;
13     }
14 }
15 
16 
17 //2
18 public class PropertyValueTestBean {
19 
20     private String defaultLocale;
21 
22     @Value("#{ systemProperties['user.region'] }")
23     public void setDefaultLocale(String defaultLocale) {
24         this.defaultLocale = defaultLocale;
25     }
26 
27     public String getDefaultLocale() {
28         return this.defaultLocale;
29     }
30 }
31 
32 //3
33 public class SimpleMovieLister {
34 
35     private MovieFinder movieFinder;
36     private String defaultLocale;
37 
38     @Autowired
39     public void configure(MovieFinder movieFinder,
40             @Value("#{ systemProperties['user.region'] }") String defaultLocale) {
41         this.movieFinder = movieFinder;
42         this.defaultLocale = defaultLocale;
43     }
44 
45     // ...
46 }
View Code

代码路径中动态使用SpEL:(参阅:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref

  1 package com.sensetime.sensestudy.exptool.web;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Arrays;
  5 import java.util.HashMap;
  6 import java.util.List;
  7 import java.util.Map;
  8 
  9 import org.springframework.expression.EvaluationContext;
 10 import org.springframework.expression.ExpressionParser;
 11 import org.springframework.expression.common.TemplateParserContext;
 12 import org.springframework.expression.spel.SpelParserConfiguration;
 13 import org.springframework.expression.spel.standard.SpelExpressionParser;
 14 import org.springframework.expression.spel.support.SimpleEvaluationContext;
 15 
 16 import lombok.Data;
 17 
 18 /**
 19  * @see <a href=
 20  *      'https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref'>https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref</a>
 21  */
 22 public class SpELTest {
 23     @Data
 24     public static class Person {
 25         private String name;
 26         private List<Boolean> booleans = new ArrayList<>();
 27         private List<String> nickNames;
 28         private Map<String, Integer> friendAge = new HashMap<>();
 29     }
 30 
 31     public static void main(String[] args) throws NoSuchMethodException, SecurityException {
 32 
 33         ExpressionParser parser = new SpelExpressionParser();
 34 
 35         // 1 Literal Expressions
 36         System.err.println(parser.parseExpression("1+7").getValue());// 8
 37         System.err.println(parser.parseExpression("-6.0221415E+23").getValue(Double.class));// -6.0221415E23
 38         System.err.println(parser.parseExpression("0x7FFFFFFF").getValue(Integer.class));// 2147483647
 39         System.err.println(parser.parseExpression("true").getValue(Boolean.class));// true
 40         System.err.println(parser.parseExpression("null").getValue(Double.class));// null
 41 
 42         System.err.println(parser.parseExpression("'Hello World'").getValue());// Hello World
 43         System.err.println(parser.parseExpression("'Hello World'").getValue(String.class));// Hello World
 44         System.err.println();
 45 
 46         Person p = new Person();
 47         p.setName("张三丰");
 48         p.getBooleans().add(true);
 49         p.getFriendAge().put("小李", 10);
 50 
 51         System.err.println(parser.parseExpression("name").getValue(p));// 张三丰
 52         System.err.println(parser.parseExpression("getName()").getValue(p));// 张三丰
 53         System.err.println(parser.parseExpression("name").getValue(p, String.class));// 张三丰
 54         System.err.println();
 55 
 56         // 2 Properties, Arrays, Lists, Maps, and Indexers
 57         System.err.println(parser.parseExpression("getBooleans()[0]").getValue(p));// true
 58         System.err.println(parser.parseExpression("getFriendAge()['小李']").getValue(p));// 10
 59 
 60         parser.parseExpression("booleans[0]").setValue(SimpleEvaluationContext.forReadOnlyDataBinding().build(), p, "false");// 会自动进行类型转换
 61         System.err.println(p.getBooleans().get(0));// false
 62 
 63         new SpelExpressionParser(new SpelParserConfiguration(true, true)).parseExpression("nickNames[3]").getValue(p);// 通过SpelParserConfiguration配置自动对Array或Collection进行容量扩展和元素初始化
 64         System.err.println(p.getNickNames().size());// 4
 65         System.err.println();
 66 
 67         // 3 Inline lists, Inline Maps
 68         Object val1 = parser.parseExpression("{1,2,3,4}").getValue();
 69         System.err.println(val1);// [1, 2, 3, 4]
 70         System.err.println(val1.getClass());// class java.util.Collections$UnmodifiableRandomAccessList
 71         System.err.println(parser.parseExpression("{{1,2},{3,4}}").getValue());// [[1, 2], [3, 4]]
 72 
 73         Object val2 = parser.parseExpression("{name:'zhangsan', age:20}").getValue();
 74         System.err.println(val2);// {name=zhangsan, age=20}
 75         System.err.println(val2.getClass());// class java.util.Collections$UnmodifiableMap
 76         System.err.println(parser.parseExpression("{p1:{1,2}, p2:{3,4}}").getValue());// {p1=[1, 2], p2=[3, 4]}
 77         System.err.println();
 78 
 79         // 4 Array Construction
 80         int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue();
 81         int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue();
 82         int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue();
 83         System.err.println();
 84 
 85         // 5 Methods
 86         System.err.println(parser.parseExpression("'Hello World'.concat('!')").getValue());// Hello World!
 87         System.err.println(parser.parseExpression("'Hello World'.bytes.length").getValue());// 11
 88         System.err.println();
 89 
 90         // 6 Operators: Relational Operators, Logical Operators, Mathematical Operators
 91         System.err.println(parser.parseExpression("name=='张三丰'").getValue(p));// true
 92         System.err.println(parser.parseExpression("1 < 2").getValue());// true
 93         System.err.println(parser.parseExpression("1 lt 2").getValue());// true
 94         System.err.println(parser.parseExpression("'a' < 'b'").getValue());// true
 95         System.err.println(parser.parseExpression("2 instanceof T(Integer)").getValue());// true
 96         System.err.println(parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue());// true
 97 
 98         System.err.println(parser.parseExpression("true and false").getValue(Boolean.class));// false
 99         System.err.println(parser.parseExpression("true or false").getValue(Boolean.class));// true
100         System.err.println(parser.parseExpression("not false").getValue(Boolean.class));// true
101         System.err.println(parser.parseExpression("! false").getValue(Boolean.class));// true
102 
103         System.err.println(parser.parseExpression(" 1 + -1*8 + 7%4 +8/3 - -1").getValue());// -1
104         System.err.println();
105 
106         // 7 Types
107         System.err.println(parser.parseExpression("T(System).getProperty('user.dir')").getValue());/// Users/zhangsan/software/eclipse-workspace/CourseDesignServer
108         System.err.println(parser.parseExpression("T(java.util.Date)").getValue(Class.class));// class java.util.Date
109         System.err.println(parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue());// true
110         System.err.println();
111 
112         // 8 Variables
113         EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();// new StandardEvaluationContext();
114         context.setVariable("newName", "张君宝");
115         System.err.println(parser.parseExpression("name = #newName").getValue(context, p)); // 张君宝
116         System.err.println(p.getName());// 张君宝
117 
118         context.setVariable("primes", Arrays.asList(2, 3, 5, 7, 11, 13, 17));
119         System.err.println(parser.parseExpression("#primes.?[#this>10]").getValue(context));// [11, 13, 17]
120         System.err.println(parser.parseExpression("#primes.#this").getValue(context, p));// [2, 3, 5, 7, 11, 13, 17]
121         System.err.println(parser.parseExpression("#primes.#root").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ],
122                                                                                             // friendAge={小李=10})
123         System.err.println(parser.parseExpression("#this").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ],
124                                                                                     // friendAge={小李=10})
125         System.err.println(parser.parseExpression("#root").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ],
126                                                                                     // friendAge={小李=10})
127         // The #this variable is always defined and refers to the current evaluation
128         // object, The #root variable is always defined and refers to the root context
129         // object.
130         System.err.println();
131 
132         // 9 Register Functions
133         context.setVariable("myReverseString", SpELTest.class.getDeclaredMethod("reverseString", String.class));
134         System.err.println(parser.parseExpression("#myReverseString('hello')").getValue(context));// olleh
135         System.err.println();
136 
137         // 10 Ternary Operator (If-Then-Else), Elvis Operator
138         System.err.println(parser.parseExpression("name?:'Unknown'").getValue(p));// 张君宝
139         System.err.println(parser.parseExpression("1>0?'yes':'no' ").getValue());// yes
140         System.err.println(parser.parseExpression("1>0?:'no' ").getValue());// true
141         System.err.println();
142 
143         // 11 Collection Selection. Selection is possible upon both lists and maps.
144         // .?[selectionExpression] all matched elements
145         // .^[selectionExpression] first matched elements
146         // .$[selectionExpression] last matched elements
147 
148 //        System.err.println(parser.parseExpression("booleans.?[#this=true]").getValue(p));//
149 
150         p.getFriendAge().put("小张", 20);
151         System.err.println(parser.parseExpression("friendAge.?[value>10] ").getValue(p));// {小张=20}
152         System.err.println(parser.parseExpression("friendAge.?[key!=null] ").getValue(p));// {小李=10, 小张=20}
153         System.err.println(parser.parseExpression("friendAge.^[key!=null] ").getValue(p));// {小李=10}
154         System.err.println(parser.parseExpression("friendAge.$[key!=null] ").getValue(p));// {小张=20}
155         System.err.println();
156 
157         // 12 Collection Projection. .![projectionExpression]
158         System.err.println(parser.parseExpression("friendAge.![key+value] ").getValue(p));// [小李10, 小张20]
159         System.err.println();
160 
161         // 13 Expression templating
162         System.err.println(parser.parseExpression("random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue());// random number is 0.7043883689415746
163         System.err.println();
164 
165     }
166 
167     public static String reverseString(String input) {
168         StringBuilder backwards = new StringBuilder(input.length());
169         for (int i = 0; i < input.length(); i++) {
170             backwards.append(input.charAt(input.length() - 1 - i));
171         }
172         return backwards.toString();
173     }
174 }
View Code

 

String类型自动转换

强烈推荐阅读下面列出的各文章,写得深入易懂。

Spring Framework中的String类型转换器、格式化器经过了多个发展阶段,概述如下(详情参阅:Spring类型转换-框架设计的基石):

1 PropertyEditor: (名字的由来:早期JDK AWT中为了解析GUI界面用户输入的值而引入,故叫ProperEditor,Spring基于PropertyEditor做增强)

Spring PropertyEditor及其各种实现:ProperyEditor、ProperyEditorSupport

Spring对PropertyEditor的注册和管理机制:PropertyEditorRegistry、PropertyEditorRegistrySupport、PropertyEditorRegistrar。

PropertyEditorRegistrar在Spring内的唯一实现为ResourceEditorRegistrar,配置文件转为Resource(如配置classpath:xxx.xml用来启动Spring容器的配置文件,String -> Resource转换)就是它的功劳。

2 新一代类型转换器:Spring 新一代类型转换机制。Spring中对HTTP请求参数的自动解析转换、@Value参数的解析等都是基于此。

转换器接口及其实现:

Converter<S, T>:  Source -> Target类型转换接口,适用于1:1转换

ConverterFactory<S, R>:  Source -> R类型转换接口,适用于1:N转换

GenericConverter:  更为通用的类型转换接口,适用于N:N转换

ConditionalConverter:  可跟上面3个接口搭配组合使用,提供前置条件判断验证

3 ConversionService:上述转换器的注册和管理,见 注册中心服务

ConverterRegistry、ConversionService、DefaultConversionService、ConversionServiceFactoryBean

4 JDK的format(java.text.format):DateFormat、NumberFormat、MessageFormat。详情参阅 JDK 格式化器

DateFormat:日期时间格式化——日期字符串与Java日期类型间的解析或格式化

NumberFormat:数值格式化

DecimalFormat:数值的有效位保留、科学技术法、分组分隔、百分数表示、货币符表示

ChoiceFormat:维护数字与字符串间的对应关系。用的很少。

MessageFormat:字符串格式——用于在字符串中插入参数

缺点:设计上存在一定缺陷,过于底层无法标准化对使用者不够友好等。

 、

5 大一统的Spring Format

Spring格式化器:org.springframework.format.Formatter,进行了应用层面的封装,更友好易用,内部还是借助 java.text.Format、java.time下的format 、java.text.NumberFormat 等实现。

Spring格式化器的注册中心:FormatterRegistry,此接口继承自类型转换器注册中心 ConverterRegistry,所以格式化注册中心是转换器注册中心的加强版,是其超集,功能更多更强大。

FormatterRegistry、FormattingConversionService、DefaultFormattingConversionService

  、  

 

 

Spring中的资源加载(Resource)

Resource、ResourceLoader、ResourceLoaderAware

这几个概念是SpringFramework中对资源及资源加载的抽象,并提供了多种实现。详情可参阅官方文档-Resources,这里是摘录总结。

Resource

Sping中对资源的抽象,虽然Java自身的URL支持http ftp file 协议,但在Spring中不够用,例如不能加载classpath下文件。因此Spring的Resource进行了进一步封装加强。

Spring's Resource interface is meant to be a more capable interface for abstracting access to low-level resources.

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}
public interface InputStreamSource {

    InputStream getInputStream() throws IOException;
}
View Code

The Resource abstraction is used extensively in Spring itself, as an argument type in many method signatures when a resource is needed.
It is important to note that the Resource abstraction does not replace functionality: it wraps it where possible. For example, a UrlResource wraps a URL, and uses the wrapped URL to do its work.

内部提供了多种Resource实现,例如:

UrlResource:内部借助Java自身的URL实现,因此与URL一样支持 ftp, http, file 协议表示的资源;此外还支持classpath协议表示的资源,如 classpath:some/resource/path/myTemplate.txt 。

ClassPathResource:读取classpath下直接位于文件系统的文件,若文件不是直接在文件系统上(例如在依赖库的jar包里则读不到,在IDE里运行能读到classpath下的文件但项目打成jar包后读不到)则读取不到。

FileSystemResource:从文件系统上读取文件。

ServletContextResource

InputStreamResource

ByteArrayResource

 

ResourceLoader

The ResourceLoader interface is meant to be implemented by objects that can return (i.e. load) Resource instances.

public interface ResourceLoader {
    Resource getResource(String location);
}

All application contexts implement the ResourceLoader interface (interface ApplicationContext implements ResourcePatternResolver implements ResourceLoader, so ApplicationContext is a ResourceLoader), and therefore all application contexts may be used to obtain Resource instances.

When you call getResource() on a specific application context, and the location path specified doesn't have a specific prefix (e.g.  Resource template = ctx.getResource("some/resource/path/myTemplate.txt");  ), you will get back a Resource type that is appropriate to that particular application context, such as:

ClassPathXmlApplicationContext -> ClassPathResource

FileSystemXmlApplicationContext -> FileSystemResource

WebApplicationContext -> ServletContextResource)

As such, you can load resources in a fashion appropriate to the particular application context.

You may also force specifial type to be used, regardless of the application context type, by specifying the special location prefix (classpath, ftp, http, file, jar ...), for example:  Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); 

 

ResourceLoaderAware

The ResourceLoaderAware interface is a special marker interface, identifying objects that expect to be provided with a ResourceLoader reference.

public interface ResourceLoaderAware {
   void setResourceLoader(ResourceLoader resourceLoader);
}

All application contexts in Spring implement the ResourceLoader interface.

  

实践经验

Spring框架中对资源的加载进行了抽象(例如  Resource、ResourceLoader、ResourceUtils  等类),使得可以加载很多种类型资源(classpath、filesystem、network上的等)。详情可参阅这篇文章

其中,借助 ResourceLoader 的实现类 PathMatchingResourcePatternResolver  可以读取如下路径的资源(其他两种尚未探究,不确定可否):

基本使用(三种方式,通过加 "classpath:"、"file:"、"url:" 前缀):

classpath下:"classpath:a/b.txt"、""classpath:/a/b.txt"、"a/b.txt"、"/a/b.txt"。

OS文件系统下:"file:/a/b.txt"

网络上:"url:http://www.test.com/a/b.txt"

默认方式:路径中未加前缀时会默认被加上"classpath:",因此对于"a/b.txt"、"/a/b.txt" 都将从classpath下找文件。

支持目录:上述三种方式中路径也可以是目录格式,如"/a/b/c"、"/a/b/c/"、"file:/a/b/c"。

classpath递归查找这点很有用):对于classpath方式,优先从当前jar包的classpath找,若未找到则从该包依赖的其他包中找,直到找到就结束,因此若两个其他jar包中都有,则只会有一个被返回。原理可参阅这篇文章

代码示例

        ResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();
        Resource[] resources = resourceLoader.getResources(directoryPath.concat("/*"));
View Code

实践:例如对于滑动验证码模块,其底图和拼图的文件夹路径是通过配置文件配置的,此时可用ResourceLoader来读取该路径下的图片集。这样的话,该路径既可以是验证码模块自身的默认图片集的路径,也可以是引用了验证码模块的具体模块(如auth模块)的自定义图片集路径,还可以是OS 文件系统的绝对路径,可以说是非常灵活了。

注意,借助 ClassPathResource 也可读文件,但其不能读取jar包里面的文件。因此,在IDE里启动时能读到、但将项目打成jar包后就读不到了,除非在jar包同级目录下放该资源文件。

 

 

 

 

 

 

 

posted @ 2021-01-31 20:17  March On  阅读(110)  评论(0编辑  收藏  举报
top last
Welcome user from
(since 2020.6.1)