【Spring-Framework】Spring-framework文档阅读笔记——Core(上)

传送门

内容没什么参考性,就是看文档时候的碎碎念,笔记之类的

1.2 Container Overview——bean的基本配置

1.2.2

容器可以一次性读取多个配置文件,这个ApplicationContext就可以看做一个spring容器,如果按这样读取,这些配置文件之间的bean是可以共享的

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

也可以对多个配置xml进行整合,如下所示,这样的情况下导入这一个文件就够了

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

All location paths are relative to the definition file doing the importing, so services.xml must be in the same directory or classpath location as the file doing the importing, while messageSource.xml and themeSource.xml must be in a resources location below the location of the importing file. As you can see, a leading slash is ignored.

1.2.3

直接加载xml配置

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

然后再加载xml配置

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

1.3 Bean Overview——注册bean

讲解注册bean的方法
代码里手动注册bean

ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("");
ac.getBeanFactory().registerSingleton("test",new Bean1());

1.3.1

n XML-based configuration metadata, you use the id attribute, the name attribute, or both to specify the bean identifiers. The id attribute lets you specify exactly one id. Conventionally, these names are alphanumeric (‘myBean’, ‘someService’, etc.), but they can contain special characters as well. If you want to introduce other aliases for the bean, you can also specify them in the name attribute, separated by a comma (,), semicolon (;), or white space.

取别名的方法如下,下面这个bean1除了id本身,一共有4个别名

<bean id="bean1" name="bean12 stupid" class="domain.Bean1">
        <property name="i" value="1"/>
    </bean>
    <alias name="bean1" alias="bean13"/>
    <alias name="bean12" alias="bean14"/>

1.3.2

Inner class names
If you want to configure a bean definition for a static nested class, you have to use the binary name of the nested class.
For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, the value of the class attribute on a bean definition would be com.example.SomeThing$OtherThing.
Notice the use of the $ character in the name to separate the nested class name from the outer class name.

Instantiation with a Constructor,不指定constructor-arg的话,使用的就是默认的空参构造函数

<bean id="bean1" name="bean12 stupid" class="domain.Bean1">
        <constructor-arg name="arg1" value="1" type="int"/>
    </bean>

Instantiation with a Static Factory Method,就是通过静态工厂方法构造对象,如下

public class Bean1Factory {
    public static Bean1 getBean1(int i){
        Bean1 bean1=new Bean1();
        bean1.setI(1);
        return bean1;
    }
}
<bean class="factory.Bean1Factory" factory-method="getBean1">
        <constructor-arg value="1"/>
</bean>

Instantiation by Using an Instance Factory Method,通过实例工厂方法构造对象

public class Bean1Factory {
    public  Bean1 getBean1(int i){
        Bean1 bean1=new Bean1();
        bean1.setI(1);
        return bean1;
    }
}
<bean id="bean1Factory" class="factory.Bean1Factory"/>
    <bean id="bean1" factory-bean="bean1Factory" factory-method="getBean1">
        <constructor-arg value="1"/>
    </bean>

1.4 Dependencies——依赖注入

讲解依赖注入的方法

1.4.1

依赖注入的方法
Constructor-based Dependency Injection
构造函数注入使用的是标签是constructor-arg,对应的方式有三种:Constructor argument type matching(根据类型解析)、Constructor argument index(根据排序解析)、Constructor argument name(根据参数名解析)。注意,如果挂了ConstructorProperties注解,那么bean里就必须按注解里的来,例如

@ConstructorProperties({"i", "jj"})
    public Bean1(int i, int j) {
        this.i = i + j;
    }

bean配置就得如下,第二个标签的name必须得是jj,不能是j

<bean id="bean1" class="domain.Bean1">
        <constructor-arg name="i" value="1"/>
        <constructor-arg name="jj" value="2"/>
    </bean>

Setter-based Dependency Injection

Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.

实际上也不一定非要无参构造函数,如下也可,即先调用public Bean1(int i, int j)构造函数,然后调用setI方法进行属性注入,最后i的值是100

<bean id="bean1" class="domain.Bean1">
        <constructor-arg name="i" value="1"/>
        <constructor-arg name="j" value="2"/>
        <property name="i" value="100"/>
    </bean>

It also supports setter-based DI after some dependencies have already been injected through the constructor approach.


Dependency Resolution Process

Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container that has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies

为什么spring创建bean的默认方式是pre-instantiate singleton beans,根本原因是spring会尽量晚的进行属性注入,例如,所有bean都创建成功了之后,才进行property注入。这样可以在创建bean的时候就发现DI过程中的错误,从而保证错误发生在容器初始化阶段,而不是实际使用bean的时候。假如是懒加载的情况,使用到某个bean才进行创建和注入,那么假如这个bean或者其依赖有问题,那错误会在使用的时候后才会暴露出来。

1.4.2

依赖注入的细节
Straight Values (Primitives, Strings, and so on)
直接使用value进行注入
下面是两种注入方法

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>
<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
    https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="masterkaoli"/>
</beans>

如果属性是property类型,可以使用另一种方式

private Properties properties;
<bean id="bean1" class="domain.Bean1">
        <property name="properties">
            <value>
                a=1
                b=2
            </value>
        </property>
    </bean>

References to Other Beans (Collaborators)

<property name="bean2" >
	<ref bean="bean2"/>
</property>

1.4.3

讲解了depends-on这个属性的用法,功能为

The depends-on attribute can explicitly force one or more beans to be initialized before the bean using this element is initialized.

就比如数据库操作中,需要先注册驱动(例如com.mysql.jdbc.Driver),然后才能进行其他的数据库操作,所以需要优先加载注册驱动的bean,例如下面的代码,register需要先加载,然后才能加载connection,但如果不写depends-on这个标签的话,spring是无法获知加载的先后关系的。

public class DriverRegister {
    public DriverRegister(String driverName) throws ClassNotFoundException {
        Class.forName(driverName);
    }
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionFactory {
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("");
    }
}
<bean id="register" class="DriverRegister">
        <constructor-arg index="0" value="com.mysql.jdbc.Driver"/>
    </bean>
    <bean id="connection" class="ConnectionFactory" factory-method="getConnection" depends-on="register"/>

1.4.5

讲解的是autowire自动装配的用法,就是不用我们手动去写依赖注入(如property标签),spring自动去注入,注入的模式有四种:

  1. 不自动注入;
  2. 根据名称自动注入;
  3. 根据类型自动注入;
  4. 只进行构造函数自动注入;

例如

public class Bean1 {
    private int i;
    private Bean2 bean2;}//忽略getter和setter
<bean id="b2" class="domain.Bean2">
        <property name="i" value="2"/>
</bean>
<bean id="b1" class="domain.Bean1" autowire="byType"/>

那么b1里的bean2就会按照类型进行自动装配;如果不想让b2被用来自动装配,如下即可完成,这个b2就不会被用来进行自动装配,b1里的bean2对象为null

<bean id="b2" class="domain.Bean2" autowire-candidate="false">
        <property name="i" value="2"/>
</bean>
<bean id="b1" class="domain.Bean1" autowire="byType"/>

autowire-candidate只会防止这个bean在byType模式下的被用来进行自动注入;The autowire-candidate attribute is designed to only affect type-based autowiring.

1.4.6

讲的是方法注入,顾名思义,就是针对特定的方法进行依赖注入
首先提出的问题是:如果一个单例对象A的方法m,该方法每次执行的时候都需要一个全新的多例对象B,那按下面这种写法肯定不行,因为这样的话b1持有的总是同一个b2

<bean id="b2" class="domain.Author" scope="prototype"/>
    <bean id="b1" class="domain.Blog" >
        <property name="author" ref="b2"/>
    </bean>

于是有了文档中提到的ApplicationContextAware接口的方法,这个接口非常简单,只有一个setApplicationContext方法,如下所示,功能为:实现了这个接口的bean对象,在容器生成完毕后,会自动将这个bean所在的容器applicationContext作为参数,去调用这个bean的setApplicationContext;那么每当需要调用newB2方法的时候,我们去手动生成一个新的Bean2就可以了

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Blog implements ApplicationContextAware {
    private ApplicationContext ac;
    private int i;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "i=" + i +
                '}';
    }
    public void testB2(){
        Author author = this.ac.getBean("b2", Author.class);
        author.setI(this.i+1);
        System.out.println(author);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ac=applicationContext;
    }
}

<bean id="b2" class="domain.Author" scope="prototype"/>
<bean id="b1" class="domain.Blog" />

但是这种方法不好,于是有了下面两种方法


Lookup Method Injection
功能一句话解释:就是createAuthor方法返回的就是一个由spring容器创建的b2对象;细节请见文档

public abstract class Blog{
    private int i;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }
   
    public void testAuthor(){
        Author author = createAuthor();
        author.setI(this.i+1);
        System.out.println(author);
    }

    public abstract Author createAuthor();
}
<bean id="b1" class="domain.Blog" >
	<lookup-method name="createAuthor" bean="b2"/>
</bean>
<bean id="b2" class="domain.Author" scope="prototype"/>

Arbitrary Method Replacement
这个方法就是运用反射的方法,把某个bean的某个方法直接替换为其他方法,解释文档代码:myValueCalculatorcomputeValue方法被替换为replacementComputeValuereimplement方法

1.5 Bean scope——Bean的作用范围

1.5.4

Request scope
见spring-积累


Scoped Beans as Dependencies
见spring-积累
Choosing the Type of Proxy to Create
默认情况下使用的cglib进行代理对象生成,proxy-target-class="false"配置可以生成jdk风格的动态代理对象,不过我觉得没啥用= =

1.5.5

自定义scope

1.6 Customing the nature of a Bean——Bean的生命周期函数

1.6.1

The Spring container guarantees that a configured initialization callback is called immediately after a bean is supplied with all dependencies.

讲解生命周期函数的调用顺序
Shutting Down the Spring IoC Container Gracefully in Non-Web Applications
下面的代码不完整,具体实现类略了,下面代码中,ac.registerShutdownHook()的功能就是告诉容器,你关闭的时候执行相应的生命周期函数,如果不执行这个方法,必须手动调用ac.close才会调用stop方法

public class Blog implements SmartLifecycle{}
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
ac.registerShutdownHook();
Blog b1 = ac.getBean("b1", Blog.class);

ApplicationContextAware and BeanNameAware
功能:When an ApplicationContext creates an object instance that implements the org.springframework.context.ApplicationContextAware interface, the instance is provided with a reference to that ApplicationContext.

1.7 Bean Definition Inheritance——bean的模板

1.8. Container Extension Points——容器生成bean过程中的自定义拓展功能

1.8.1 Customizing Beans by Using a BeanPostProcessor

init-method之前执行postProcessBeforeInitialization方法,在init-method之后执行postProcessAfterInitialization方法,

The org.springframework.beans.factory.config.BeanPostProcessor interface consists of exactly two callback methods. When such a class is registered as a post-processor with the container, for each bean instance that is created by the container, the post-processor gets a callback from the container both before container initialization methods (such as InitializingBean.afterPropertiesSet() or any declared init method) are called, and after any bean initialization callbacks.
在initialization methods之前接受回调信号执行postProcessBeforeInitialization方法,在any bean initialization callbacks之后接受回调信号,执行postProcessAfterInitialization

你甚至可以直接修改返回的值,例如下面代码,所有bean返回的对象就都是123

@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization:" + beanName);
        System.out.println(bean);
        return "123";
    }

1.8.2. Customizing Configuration Metadata with a BeanFactoryPostProcessor

这个BeanFactoryPostProcessor就是在容器创建bean之前对这个bean的一些配置进行修改

BeanFactoryPostProcessor operates on the bean configuration metadata. That is, the Spring IoC container lets a BeanFactoryPostProcessor read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessor instances.

<context:property-placeholder location="xxx"/>PropertySourcesPlaceholderConfigurer就是利用BeanFactoryPostProcessor实现spring配置文件读取properties文件的,以便在spring配置文件读取在另一个文件里写好的属性值
<context:property-override location="classpath:override.properties"/>PropertyOverrideConfigurer也是同理,只不过他们俩的功能是覆盖重写一些bean的属性值

1.8.3. Customizing Instantiation Logic with a FactoryBean

最好的例子就是Mybatis-spring里的各种FactoryBean,例如,<bean id='bean' MapperFactoryBean/>,然后getBean("bean")返回的结果是MapperFactoryBeangetObject方法的返回结果

1.9 Annotation-based Container Configuration——使用注解对Bean进行依赖注入

配置<context:annotation-config/>只会影响容器管理的类对象里的注解,

1.10. Classpath Scanning and Managed Components——使用注解注册Bean

1.10.9

只要把下面这个依赖添加进去,然后rebuild就可以了

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.2.5.RELEASE</version>
        <optional>true</optional>
    </dependency>

在这里插入图片描述

1.12. Java-based Container Configuration——使用注解配置容器

1.12.3. Using the @Bean Annotation

实际上下面的代码还是能运行的= =,虽然不太规范

@Bean("domainInterface")
    @Qualifier("di")
    public DomainInterface getDominInterface(){
        Author author=new Author();
        author.setName("good");
        return author;
    }

    @Bean("blog")
    public Blog getBlog(@Qualifier("di") Author author) {
        Blog blog = new Blog();
        blog.setAuthor(author);
        blog.setId(100);
        return blog;
    }

1.13. Environment Abstraction——properties和profile的用法

1.15. Additional Capabilities of the ApplicationContext——容器的拓展功能

1.15.2

定义事件功能分这么几步

  1. 定义事件。通过继承ApplicationEvent,该抽象类只有一个构造函数public ApplicationEvent(Object source),这个source代表的是The object on which the Event initially occurred.
  2. 定义事件发布的逻辑。发布事件需要ApplicationEventPublisher对象,可以通过ApplicationEventPublisherAware来自动注入,例如if(xxx){publisher.publishEvent(xxx)}
  3. 定义事件监听对象并在容器中注册。通过继承ApplicationListener<T>,其中onApplicationEvent方法即为事件处理逻辑。

基于@EventListener的事件监听要记得在配置里开<context:annotation-config/>

1.15.4. Convenient ApplicationContext Instantiation for Web Applications

在web应用里部署spring的ioc容器

2.3. Built-in Resource Implementations

@Test
    public void testResource(){
        ClassPathResource classPathResource=new ClassPathResource("format.properties");
        System.out.println(classPathResource.exists());
        
        FileSystemResource fileSystemResource=new FileSystemResource("src/main/resources/format.properties");
        System.out.println(fileSystemResource.exists());

        try(InputStream inputStream=new FileInputStream("src/main/resources/format.properties")){
            InputStreamResource inputStreamResource=new InputStreamResource(inputStream);
            System.out.println(inputStreamResource.exists());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
@RequestMapping("/a")
    public ModelAndView aLink(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("singletonController: "+this.controller.hashCode());
        ServletContextResource servletContextResource=new ServletContextResource(request.getServletContext(),"index.jsp");
        System.out.println(servletContextResource.exists());
        return this.controller.aLink();
    }

2.7. Application Contexts and Resource Paths

2.7.3. FileSystemResource Caveats

A FileSystemResource that is not attached to a FileSystemApplicationContext (that is, when a FileSystemApplicationContext is not the actual ResourceLoader) treats absolute and relative paths as you would expect.
For backwards compatibility (historical) reasons however, this changes when the FileSystemApplicationContext is the ResourceLoader. The FileSystemApplicationContext forces all attached FileSystemResource instances to treat all location paths as relative, whether they start with a leading slash or not.

3.1. Validation by Using Spring’s Validator Interface——校验器

3.3 Bean Manipulation and the BeanWrapper——字符串与属性转换

文档中描述了三种注册自定义PropertyEditor的方式
第二种:

package propertyEditor;

import domain.Author;

import java.beans.PropertyEditorSupport;

public class AuthorEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        System.out.println("propertyEditor.AuthorEditor: "+text);
        String[] args=text.split(" ");
        Author author=new Author();
        author.setId(Integer.parseInt(args[0]));
        author.setName(args[1]);
        setValue(author);
    }
}
<bean id="blog" class="domain.Blog">
        <property name="id" value="1"/>
        <property name="author" value="3 Tom"/>
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="domain.Author" value="propertyEditor.AuthorEditor"/>
            </map>
        </property>
    </bean>

第三种:

package propertyEditor;

import domain.Author;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {

        // it is expected that new PropertyEditor instances are created
        registry.registerCustomEditor(Author.class, new AuthorEditor());

        // you could register as many custom property editors as are required here...
    }
}
    <bean id="customPropertyEditorRegistrar" class="propertyEditor.CustomPropertyEditorRegistrar"/>
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <ref bean="customPropertyEditorRegistrar"/>
            </list>
        </property>
    </bean>

3.4. Spring Type Conversion——类型转换

Within a Spring container, you can use this system as an alternative to PropertyEditor implementations to convert externalized bean property value strings to the required property types.

看样子这个功能是PropertyEditor的替代方案,但他不仅仅能实现属性解析,这个转换器功能还可以使用在其他的需要类型转换的地方。如果仅仅是用于属性解析,用PropertiesEditor更方便,但如果需要其他的类型转换,那么后者更灵活。

3.4.1. Converter SPI

将一个对象转换成另一个类对象的接口

3.4.2. Using ConverterFactory

转换器工厂,顾名思义,生成转换器的方法。多用于centralize the conversion logic for an entire class hierarchy。简单说就是,当需要将一个类对象,根据情况转换成某个类或者其子类的时候,需要的转换器有可能不太一样。
文档里的例子是StringToEnumConverterFactory,即将一个String转换成对应的枚举类型(注意所有枚举类型都是Enum的子类),那具体需要转换成哪一个枚举类型,就需要不同的转换器了。这个不同类型的转换器就由转换器工厂生成

3.4.3. Using GenericConverter

可以理解成泛型转换器,将array转换成对应的collection对象。
这里出现了一个TypeDescriptor类,具体功能看API去。文档中举例类为EntityConverter,但我只发现了IdToEntityConverter这个类,这个类的核心功能是convert方法,假如说源类型是Integer,目标类型为Account,那么如果Account类里有静态的、需要1个参数findAccount方法,那么convert方法首先会将Integer转换成findAccount方法的参数类型,然后以之为参数,使用反射调用findAccount。由于是静态方法,所以反射invoke调用的时候第一个参数没用了。

3.4.4. The ConversionService API

使用这个接口来调用转换功能

3.4.5. Configuring a ConversionService

If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.

用起来和PropertyEditor感觉差不太多

<bean id="blog" class="domain.Blog">
        <property name="id" value="1"/>
        <property name="author" value="3 Tom"/>
    </bean>
<bean id="conversionService"
          class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="converter.MyCustomConverter"/>
            </set>
        </property>
    </bean>
package converter;

import domain.Author;
import org.springframework.core.convert.converter.Converter;

public class MyCustomConverter implements Converter<String , Author> {
    @Override
    public Author convert(String s) {
        System.out.println("MyCustomConverter: "+s);
        String[] args=s.split(" ");
        Author author=new Author();
        author.setId(Integer.parseInt(args[0]));
        author.setName(args[1]);
        return author;
    }
}

其中ConversionServiceFactoryBeanafterPropertiesSet方法会自动完成对我们自定义转换器的注册

3.5. Spring Field Formatting

3.5.1. The Formatter SPI

示例
这就是个对某些对象的格式化表示器

@Test
    public void testOther() throws ParseException {
        DateFormatter dateFormatter=new DateFormatter("yyyy-MM-dd HH:mm:ss");
        Date date=new Date();
        String dateStr=dateFormatter.print(date,Locale.US);
        System.out.println(dateStr);
        Date parsedDate = dateFormatter.parse("2019-12-25 12:01:01", Locale.CHINA);
        System.out.println(parsedDate);


        CurrencyStyleFormatter currencyStyleFormatter=new CurrencyStyleFormatter();
        String currencyString=currencyStyleFormatter.print(123,Locale.CHINA);
        System.out.println(currencyString);
        BigDecimal parsedCurrency = currencyStyleFormatter.parse("¥123",Locale.CHINA);
        System.out.println(parsedCurrency);
    }

3.5.2. Annotation-driven Formatting

介绍了AnnotationFormatterFactory
NumberFormatAnnotationFormatterFactory为例,他是个工厂类,可以根据注解生成对应的PrinterParser
假如Author类的一个属性如下

    @NumberFormat(style= NumberFormat.Style.CURRENCY)
    private BigDecimal salary;
@Test
    public void testOther() throws ParseException, NoSuchFieldException, IllegalAccessException {
        Field field = Author.class.getDeclaredField("salary");
        NumberFormat annotation = (NumberFormat)field.getAnnotations()[0];
        System.out.println(annotation.style());

        NumberFormatAnnotationFormatterFactory factory=new NumberFormatAnnotationFormatterFactory();
        Printer<Number> printer = factory.getPrinter(annotation, BigDecimal.class);
        String print = printer.print(123, Locale.CHINA);
        System.out.println(print);
    }

3.5.3. The FormatterRegistry SPI

3.5.4. The FormatterRegistrar SPI

上面这两个玩意没啥差别,就是注册Formatter的方式不太一样;类似于在PropertiesEditor中提到的那样,前者是直接一个个直接将Formatter进行注册,后者是先将各个Formatter注册进Registrar,然后将这个Registrar整体进行注册

A FormatterRegistrar is useful when registering multiple related converters and formatters for a given formatting category, such as date formatting.

3.7. Java Bean Validation

对bean进行校验

4.1 Evaluation

4.1.3. SpEL Compilation

这个功能就是spring el的预编译功能,如果某个spEL会被高频使用,预编译之后执行会提升运行速度

@Test
    public void testEL() {
        SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
                this.getClass().getClassLoader());

        SpelExpressionParser parser = new SpelExpressionParser(config);

        Expression expr = parser.parseExpression("placeOfBirth.country");

        GregorianCalendar c = new GregorianCalendar();
        c.set(1856, 7, 9);
        Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
        tesla.setPlaceOfBirth(new PlaceOfBirth("xxx","Serbian"));
        System.out.println(new Date());
        for(int i=0;i<1000000;i++){
            Object payload = expr.getValue(tesla);
        }
        System.out.println(new Date());
    }

结果如下
在这里插入图片描述
如果不用立刻编译SpelExpressionParser parser = new SpelExpressionParser();,结果如下
在这里插入图片描述

4.2. Expressions in Bean Definitions

用spEL进行DI

4.3 Language Reference

4.3.2. Properties, Arrays, Lists, Maps, and Indexers

看到这里,我一直没发现context有啥用

@Test
public void testEL() {
        SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
                this.getClass().getClassLoader());

        SpelExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

        Expression expr = parser.parseExpression("placeOfBirth.country");
        GregorianCalendar c = new GregorianCalendar();
        c.set(1856, 7, 9);
        Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
        tesla.setInventions(new String[]{"Induction Motor","alternating current"});
        tesla.setPlaceOfBirth(new PlaceOfBirth("xxx", "Serbian"));
        Object value = parser.parseExpression("inventions[0]").getValue(context,tesla);
        System.out.println(value);

        Inventor[] ieee={new Inventor("Nikola Tesla1", c.getTime(), "Serbian1"),
                new Inventor("Nikola Tesla2", c.getTime(), "Serbian2"),
                new Inventor("Nikola Tesla3", c.getTime(), "Serbian3")};
        value=parser.parseExpression("[0].name").getValue(ieee);
        System.out.println(value);

        HashMap<String,Inventor> yellowPage=new HashMap<>();
        Stream.of(ieee).forEach(inventor -> {yellowPage.put(inventor.getName(),inventor);});
        value=parser.parseExpression("['Nikola Tesla3'].nationality").getValue(yellowPage);
        System.out.println(value);
    }

4.3.10 Variables

看到了上下文的功能,可以存储变量

// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值