【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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章