Spring -- 中文文檔摘要

1、IOC容器

  • 當對象實例化後依賴的對象才被創建,當創建bean後容器注入這些依賴對象。這個過程基本上是反向的,因此命名爲控制反轉(IoC)。
  • BeanFactory接口提供一種高級的配置機制能夠管理任何類型的對象。ApplicationContextBeanFactory的子接口。它能更容易集成Spring的AOP功能、消息資源處理(比如在國際化中使用)、事件發佈和特定的上下文應用層比如在網站應用中的WebApplicationContext。
  • 總之,BeanFactory提供了配置框架和基本方法,ApplicationContext添加更多的企業特定的功能。
  • @Bean和 element元素扮演了相同的角色。你可以在任何使用@Componen的地方使用@Bean,但是更常用的是在配置@Configuration的類中使用。
  • 當@Configuration註解的類作爲輸入時,@Configuration類本身會被註冊爲一個bean,在這個類中所有用@Bean註解的方法都會被定義爲一個bean。
  • 啓用組件掃描,只需要在你的@Configuration類中做如下配置:

    @Configuration
    @ComponentScan(basePackages = "com.acme")
    public class AppConfig  {
        ...
    }
    

    有Spring使用經驗的用戶,對Spring XML的context的聲明非常熟悉:

    <beans>
        <context:component-scan base-package="com.acme"/>
    </beans>
  • public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("com.acme");
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
    }
  • <web-app>
        <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <context-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </context-param>
    
        <!-- Configuration locations must consist of one or more comma- or space-delimited
            fully-qualified @Configuration classes. Fully-qualified packages may also be
            specified for component-scanning -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.AppConfig</param-value>
        </context-param>
    
        <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- Declare a Spring MVC DispatcherServlet as usual -->
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
                instead of the default XmlWebApplicationContext -->
            <init-param>
                <param-name>contextClass</param-name>
                <param-value>
                    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
                </param-value>
            </init-param>
            <!-- Again, config locations must consist of one or more comma- or space-delimited
                and fully-qualified @Configuration classes -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>com.acme.web.MvcConfig</param-value>
            </init-param>
        </servlet>
    
        <!-- map all requests for /app/* to the dispatcher servlet -->
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/app/*</url-pattern>
        </servlet-mapping>
    </web-app>
  • 生命週期回調

    任何使用了@Bean定義了的類都支持常規生命週期回調,並且可以使用JSR-250中的@PostConstruct和@PreDestroy註解,詳細信息,參考JSR-250註解。

    完全支持常規的Spring生命週期回調。如果一個bean實現了InitializingBean,DisposableBean或Lifecycle接口,它們的相關方法就會被容器調用。

    完全支持*Aware系列的接口,例如:BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等。

    @Bean註解支持任意的初始化和銷燬回調方法,這與Spring XML 中bean元素上的init方法和destroy-method屬性非常相似:

  • 可以使用@Description註解對Bean添加描述:

    @Configuration
    public class AppConfig {
    
        @Bean
        @Description("Provides a basic example of a bean")
        public Foo foo() {
            return new Foo();
        }
    
    }
  • 注意,從Spring 3.2開始,不再需要將CGLIB添加到類路徑中,因爲CGLIB類已經被打包在org.springframework.cglib下,直接包含在spring-core JAR中。
  • 記住@Configuration類最終只是容器中的另一個bean:這意味着它們可以像任何其他bean一樣利用@Autowired和@Value注入等!
  • @Configuration
    public class ServiceConfig {
    
        @Autowired
        private AccountRepository accountRepository;
    
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl(accountRepository);
        }
    
    }
    
    @Configuration
    public class RepositoryConfig {
    
        private final DataSource dataSource;
    
        @Autowired
        public RepositoryConfig(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Bean
        public AccountRepository accountRepository() {
            return new JdbcAccountRepository(dataSource);
        }
    
    }
    
    @Configuration
    @Import({ServiceConfig.class, RepositoryConfig.class})
    public class SystemTestConfig {
    
        @Bean
        public DataSource dataSource() {
            // return new DataSource
        }
    
    }
    
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
        // everything wires up across configuration classes...
        TransferService transferService = ctx.getBean(TransferService.class);
        transferService.transfer(100.00, "A123", "C456");
    }
  • 只有Spring Framework 4.3才支持@Configuration類中的構造方法注入。
  • 通常,有條件的開啓或者禁用一個完整的@Configuration類,一個常見的例子就是使用@Profile註解來激活僅在Spring 環境中啓用的特定的profile文件,@Profile註解是用一個更加靈活的@Conditional註解實現的。
  • 假設你可能會以XML包含@Configuration類的方式來啓動一個Spring容器。例如,在一個現有使用Spring XML的大型代碼庫中,根據需要從已有的XML文件中創建@Configuration類是很簡單的。
  • 因爲context:annotation-config/是打開的,容器會識別@Configuration,並且會處理AppConfig中聲明的@Bean方法。
  • 注意在個案例中,我們不需要明確的聲明context:annotation-config/,因爲開啓context:component-scan/,功能是相同的。
  • system-test-config.xml:

    <beans>
        <!-- enable processing of annotations such as @Autowired and @Configuration -->
        <context:annotation-config/>
        <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
    
        <bean class="com.acme.AppConfig"/>
    
        <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    </beans>
  • system-test-config.xml:

    <beans>
        <!-- picks up and registers AppConfig as a bean definition -->
        <context:component-scan base-package="com.acme"/>
        <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
    
        <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    </beans>
  • 在以@Configuration類爲主要機制的配置容器的應用程序中,仍然有必要使用一些XML。在這些場景中,只需使用@ImportResource,並根據需要定義一些XML。這樣實現了“以Java爲中心”方式來配置容器,並將XML保持在最低限度。
  • Bean定義profiles是在覈心容器中允許不同的bean在不同環境註冊的機制。通常依賴於系統環境變量和包含${placeholder}的XML 語句,根據環境變量的值可以解決正確的文件路徑配置。
  • @Configuration
    @Profile("dev")
    public class StandaloneDataConfig {
    
        @Bean
        public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build();
        }
    }
    

     

    @Configuration
    @Profile("production")
    public class JndiDataConfig {
    
        @Bean(destroyMethod="")
        public DataSource dataSource() throws Exception {
            Context ctx = new InitialContext();
            return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
        }
    }
  • @Profile可以被用作爲創建一個自定義組合註解的元註解。下面的例子定義了一個@Production註解,它可以被用作替換@Profile(“production”)的註解。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Profile("production")
    public @interface Production {
    }
  • 在僅僅包含一個特殊bean的配置類中,@Profile也可以被聲明在方法級別。
  • 如果一個@Configuration類被標記爲@Profile,那麼所有的@Bean方法和@Import註解相關的類都會被忽略,除非一個或多個特別的profiles被激活。
  • profiles也可以通過聲明spring.profiles.active屬性來激活,這個可以通過在系統環境變量,JVM系統屬性,web.xml中的servlet上下文環境參數,甚至JNDI的入口。
  • 環境對象對一組PropertySource對象執行搜索。一個PropertySource是對任何key-value資源的簡單抽象,並且Spring 的標準環境是由兩個PropertySource配置的,一個表示一系列的JVM 系統屬性(System.getProperties()),一個表示一系列的系統環境變量(System.getenv())。這些默認的屬性資源存在於StandardEnvironment,具體的說,當使用StandardEnvironment時,如果在運行時系統屬性或者環境變量中包括foo,那麼調用env.containsProperty(“foo”)方法將會返回true。
  • 整個機制都是可配置的。也許你有個自定義的屬性來源,你想把它集成到這個搜到裏面。這也沒問題,只需簡單的實現和實例化自己的PropertySource,並把它添加到當前環境的PropertySources集合中:

    ConfigurableApplicationContext ctx = new GenericApplicationContext();
    MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
    sources.addFirst(new MyPropertySource());
  • 在上面的代碼中,MyPropertySource被添加到搜索中的最高優先級。
  • @PropertySource註解對添加一個PropertySource到Spring的環境變量中提供了一個便捷的和聲明式的機制。
    給出一個名爲”app.properties”的文件,它含了testbean.name=myTestBean的鍵值對,下面的@Configuration類使用@PropertySource的方式來調用testBean.getName(),將會返回”myTestBean”。

    @Configuration
    @PropertySource("classpath:/com/myco/app.properties")
    public class AppConfig {
     @Autowired
     Environment env;
    
     @Bean
     public TestBean testBean() {
      TestBean testBean = new TestBean();
      testBean.setName(env.getProperty("testbean.name"));
      return testBean;
     }
    }
  • ApplicationContext接口繼承了一個叫做MessageSource的接口,因此它也提供了國際化(i18n)的功能。當ApplicationContext被載入的時候,它會自動的在上下文中去搜索定義的MessageSource bean。這個bean必須有messageSource的名稱。Spring提供了ResourceBundleMessageSource和StaticMessageSource兩個MessageSource實現。它們兩個都實現了HierarchicalMessageSource以便處理嵌套消息。
  • 展示ResourceBundleMessageSource使用的例子:

    <beans>
        <bean id="messageSource"
                class="org.springframework.context.support.ResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <value>format</value>
                    <value>exceptions</value>
                    <value>windows</value>
                </list>
            </property>
        </bean>
    </beans>
     

    在上面的例子中,假設在類路徑下定義了format,exceptions和windows三個資源包。解析消息的任何請求都會通過ResourceBundles被JDK以標準方式處理。爲了舉例說明,假設上述兩個資源包的文件內容是…

    # in format.properties
    message=Alligators rock!
    
    # in exceptions.properties
    argument.required=The {0} argument is required.
  • public class Example {
    
        private MessageSource messages;
    
        public void setMessages(MessageSource messages) {
            this.messages = messages;
        }
    
        public void execute() {
            String message = this.messages.getMessage("argument.required",
                new Object [] {"userDao"}, "Required", null);
            System.out.println(message);
        }
    
    }
     繼續用前面messageSource 爲例,如果你想根據英國(en-GB)解析消息,你可以創建這些文件format_en_GB.properties,exceptions_en_GB.properties, and windows_en_GB.properties。
    通常,地域設置通過應用周圍環境管理的。
  • argument.required=Ebagum lad, the {0} argument is required, I say, required.

    public static void main(final String[] args) {
        MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
        String message = resources.getMessage("argument.required",
            new Object [] {"userDao"}, "Required", Locale.UK);
        System.out.println(message);
    }
  • 你也可以使用MessageSourceAware接口來獲取對已定義MessageSource的引用。任何在ApplicationContext定義的bean都會實現MessageSourceAware,當bean被創建或者配置的時候,它會在應用上下文的MessageSource中被被注入。
  • ApplicationEvent類和ApplicationListener接口提供了ApplicationContext中的事件處理。如果一個bean實現了ApplicationListener接口,然後它被部署到上下問中,那麼每次ApplicationEvent發佈到ApplicationContext中時,bean都會收到通知。本質上,這是觀察者模型。
  • 你可以創建併發布自己的自定義事件。這個例子演示了一個繼承Spring ApplicationEvent的簡單類:

    public class BlackListEvent extends ApplicationEvent {
    
        private final String address;
        private final String test;
    
        public BlackListEvent(Object source, String address, String test) {
            super(source);
            this.address = address;
            this.test = test;
        }
    
        // accessor and other methods...
    
    }
    

    爲了發佈一個自定義的ApplicationEvent,在ApplicationEventPublisher中調用publishEvent()方法。通常在實現了ApplicationEventPublisherAware接口並把它註冊爲一個Spring bean的時候它就完成了。下面的例子展示了這麼一個類:

    public class EmailService implements ApplicationEventPublisherAware {
    
        private List<String> blackList;
        private ApplicationEventPublisher publisher;
    
        public void setBlackList(List<String> blackList) {
            this.blackList = blackList;
        }
    
        public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
            this.publisher = publisher;
        }
    
        public void sendEmail(String address, String text) {
            if (blackList.contains(address)) {
                BlackListEvent event = new BlackListEvent(this, address, text);
                publisher.publishEvent(event);
                return;
            }
            // send email...
        }
    
    }
  • 創建一個試下了ApplicationListener的類並把他註冊爲一個Spring bean。下面例子展示這樣一個類:

    public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
    
        private String notificationAddress;
    
        public void setNotificationAddress(String notificationAddress) {
            this.notificationAddress = notificationAddress;
        }
    
        public void onApplicationEvent(BlackListEvent event) {
            // notify appropriate parties via notificationAddress...
        }
    
    }
  •  從Spring 4.2開始,一個事件監聽器可以通過EventListener註解註冊在任何managed bean的公共方法上。
  • @EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
    public void handleContextStart() {
    
    }
  • 一個應用上下文就是一個ResourceLoader,它可以用來載入資源。一個資源本質上講就是JDK類java.net.URL功能更豐富的版本。實際上,Resource的實現在合適的地方包裝了一個java.net.URL實例。資源可以以透明的方式從任何位置獲得獲取低優先級的資源,包括一個標準的URL,本地文件系統,任何描述標準URL的地方,其他的一些擴展。如果資源位置的字符串是一個沒有任何特殊字符前綴的簡單路徑,那麼這些資源就來自特定的並適合實際應用程序上下文類型。
  • 你可以將bean的配置部署到一個實現了應用上下文的特殊回調接口ResourceLoaderAware中,以便在初始化的時候應用上下文把自己作爲ResourceLoader傳遞進去可以自動調用。
  • 你可以像下面一樣通過ContextLoaderListener來註冊一個ApplicationContext:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  • 支持Ant風格的路徑模式。例如:/WEB-INF/*Context.xml,在WEB-INF目錄下,/WEB-INF/**/*Context.xml,WEB-INF子目錄的所有這樣類似的文件都會被發現。

2、資源

  • 相對標準 url 訪問機制,spring 的 Resource 接口對抽象底層資源的訪問提供了一套更好的機制。
  • UrlResource 封裝了一個 java.net.URL 對象,用來訪問 URL 可以正常訪問的任意對象,包括用於訪問文件系統路徑的 file:,通過 http 協議訪問資源的 http:,通過 ftp 協議訪問資源的 ftp:。
  • ClassPathResource 可以從類路徑上加載資源,其可以使用線程上下文加載器、指定加載器或指定的 class 類型中的任意一個來加載資源。
  • FileSystemResource 是針對 java.io.File 提供的 Resource 實現。
  • ServletContextResource 是爲了獲取 web 根路徑的 ServletContext 資源而提供的 Resource 實現。
  • ResourceLoader 接口是用來加載 Resource 對象的,spring 裏所有的應用上下文都是實現了 ResourceLoader 接口,當你在指定應用上下文調用 getResource() 方法時,而指定的位置路徑又沒有包含特定的前綴,spring 會根據當前應用上下文來決定返回哪一種類型 Resource。
  • 強制需要獲取一個 ClassPathResource 對象,這個時候,你可以通過加上指定的前綴來實現這一需求,如:

    Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
  • 類似的,你可以通過其他任意的 url 前綴來強制獲取 UrlResource 對象:

    Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
    
    Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt");
  •  ResourceLoaderAware 是一個特殊的標記接口,用來標記提供 ResourceLoader 引用的對象。但總的來說,在需求滿足都滿足的情況下,最好是使用的專用 ResourceLoader 接口,因爲這樣代碼只會與接口耦合,而不會與整個 spring ApplicationContext 耦合。
  • 所有應用程序上下文註冊和使用一個特殊的JavaBeans PropertyEditor,它可以將String路徑轉換爲Resource對象。 因此,如果myBean具有“資源”類型的模板屬性,則可以使用該資源的簡單字符串進行配置,如下所示:

    <bean id="myBean" class="...">
        <property name="template" value="some/resource/path/myTemplate.txt"/>
    </bean>

3、數據轉換

  • 具體的驗證不應該捆綁在web層,應該容易本地化並且它應該能夠插入任何可用的驗證器。考慮到以上這些,Spring想出了一個Validator接口,它在應用程序的每一層基本都是可用的。Spring提供了所謂的DataBinder來處理這個。ValidatorDataBinder組成了validation包,其主要用於但並不侷限於MVC框架。
  • Spring的DataBinder和底層的BeanWrapper都使用PropertyEditor來解析和格式化屬性值。
  • 實現一個Validator是相當簡單的,特別是當你知道Spring框架還提供了ValidationUtils輔助類:

    public class PersonValidator implements Validator {
    
        /**
         * This Validator validates *just* Person instances
         */
        public boolean supports(Class clazz) {
            return Person.class.equals(clazz);
        }
    
        public void validate(Object obj, Errors e) {
            ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
            Person p = (Person) obj;
            if (p.getAge() < 0) {
                e.rejectValue("age", "negativevalue");
            } else if (p.getAge() > 110) {
                e.rejectValue("age", "too.darn.old");
            }
        }
    }
  • BeanWrapper提供了設置和獲取屬性值(單獨或批量)、獲取屬性描述符以及查詢屬性以確定它們是可讀還是可寫的功能。BeanWrapper通常不會被應用程序的代碼直接使用,而是由DataBinderBeanFactory使用。BeanWrapper的名字已經部分暗示了它的工作方式:它包裝一個bean以對其執行操作,比如設置和獲取屬性。
  • 考慮下面兩個類:

    public class Company {
    
        private String name;
        private Employee managingDirector;
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Employee getManagingDirector() {
            return this.managingDirector;
        }
    
        public void setManagingDirector(Employee managingDirector) {
            this.managingDirector = managingDirector;
        }
    }
    
    public class Employee {
    
        private String name;
    
        private float salary;
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public float getSalary() {
            return salary;
        }
    
        public void setSalary(float salary) {
            this.salary = salary;
        }
    }
    

    以下的代碼片段展示瞭如何檢索和操縱實例化的CompaniesEmployees的某些屬性:

    BeanWrapper company = new BeanWrapperImpl(new Company());
    // setting the company name..
    company.setPropertyValue("name", "Some Company Inc.");
    // ... can also be done like this:
    PropertyValue value = new PropertyValue("name", "Some Company Inc.");
    company.setPropertyValue(value);
    
    // ok, let's create the director and tie it to the company:
    BeanWrapper jim = new BeanWrapperImpl(new Employee());
    jim.setPropertyValue("name", "Jim Stravinsky");
    company.setPropertyValue("managingDirector", jim.getWrappedInstance());
    
    // retrieving the salary of the managingDirector through the company
    Float salary = (Float) company.getPropertyValue("managingDirector.salary");
  • Spring使用PropertyEditor的概念來實現ObjectString之間的轉換。舉個例子,一個Date可以以人類可讀的方式表示(如String'2007-14-09'),同時我們依然能把人類可讀的形式轉換回原始的時間(甚至可能更好:將任何以人類可讀形式輸入的時間轉換回Date對象)。這種行爲可以通過註冊類型爲PropertyEditor的自定義編輯器來實現。
  • java.lang.String作爲你在XML文件中聲明的某些bean的屬性值時,使用ClassEditor嘗試將參數解析成Class對象。
  • 假設你有一個BeanFactory引用,最人工化的方式(但通常並不方便或者推薦)是直接使用ConfigurableBeanFactory接口的registerCustomEditor()方法,另一種略爲方便的機制是使用一個被稱爲CustomEditorConfigurer的特殊的bean factory後處理器(post-processor)。
  • 考慮一個用戶類ExoticType和另外一個需要將ExoticType設爲屬性的類DependsOnExoticType

    package example;
    
    public class ExoticType {
    
        private String name;
    
        public ExoticType(String name) {
            this.name = name;
        }
    }
    
    public class DependsOnExoticType {
    
        private ExoticType type;
    
        public void setType(ExoticType type) {
            this.type = type;
        }
    }
    

    當東西都被正確設置時,我們希望能夠分配字符串給type屬性,而PropertyEditor會在背後將其轉換成實際的ExoticType實例:

    <bean id="sample" class="example.DependsOnExoticType">
        <property name="type" value="aNameForExoticType"/>
    </bean>
    

    PropertyEditor實現可能與此類似:

    // converts string representation to ExoticType object
    package example;
    
    public class ExoticTypeEditor extends PropertyEditorSupport {
    
        public void setAsText(String text) {
            setValue(new ExoticType(text.toUpperCase()));
        }
    }
    

    最後,我們使用CustomEditorConfigurer將一個新的PropertyEditor註冊到ApplicationContext,那麼在需要的時候就能夠使用它:

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
            </map>
        </property>
    </bean>
  • 另一種將屬性編輯器註冊到Spring容器的機制是創建和使用一個PropertyEditorRegistrar
  • 使用PropertyEditorRegistrar可能最好還是以一個例子來說明。首先,你需要創建你自己的PropertyEditorRegistrar實現:

    package com.foo.editors.spring;
    
    public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    
        public void registerCustomEditors(PropertyEditorRegistry registry) {
    
            // it is expected that new PropertyEditor instances are created
            registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
    
            // you could register as many custom property editors as are required here...
        }
    }
  • 下面是一個在initBinder(..)方法的實現裏使用PropertyEditorRegistrar的例子:

    public final class RegisterUserController extends SimpleFormController {
    
        private final PropertyEditorRegistrar customPropertyEditorRegistrar;
    
        public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
            this.customPropertyEditorRegistrar = propertyEditorRegistrar;
        }
    
        protected void initBinder(HttpServletRequest request,
                ServletRequestDataBinder binder) throws Exception {
            this.customPropertyEditorRegistrar.registerCustomEditors(binder);
            this.customPropertyEditorRegistrar.registerCustomEditor(Date.class, new MyEditor());
        }
    
        // other methods to do with registering a User
        @InitBinder
        public void initBinder(WebDataBinder binder) {
            binder.registerCustomEditor(Date.class, new MyEditor());
        }
    }
  • Spring 3引入了core.convert包來提供一個一般類型的轉換系統。這個系統可以當作是PropertyEditor的替代選擇,用於將外部bean的屬性值字符串轉換成所需的屬性類型。

  • 爲方便起見,core.convert.support包已經提供了一些轉換器實現,這些實現包括了從字符串到數字以及其他常見類型的轉換。考慮將StringToInteger作爲一個典型的Converter實現示例:

    package org.springframework.core.convert.support;
    
    final class StringToInteger implements Converter<String, Integer> {
    
        public Integer convert(String source) {
            return Integer.valueOf(source);
        }
    
    }
  • 當你需要集中整個類層次結構的轉換邏輯時,例如,碰到將String轉換到java.lang.Enum對象的時候,請實現ConverterFactory

    package org.springframework.core.convert.converter;
    
    public interface ConverterFactory<S, R> {
    
        <T extends R> Converter<S, T> getConverter(Class<T> targetType);
    
    }

4、CROS

  • 出於安全考慮,瀏覽器禁止AJAX調用駐留在當前來源之外的資源。Cross-origin resource sharing(CORS) 是 大多數瀏覽器實現的W3C規範。
  • 從Spring Framework 4.2開始,CORS支持開箱即用。. CORS 請求(including preflight ones with anOPTIONSmethod) 被自動調度到各種已註冊的HandlerMappings. 
  • @RequestMapping上增加@CrossOrigin註解

    @RestController
    @RequestMapping("/account")
    public class AccountController {
    
    	@CrossOrigin
    	@RequestMapping("/{id}")
    	public Account retrieve(@PathVariable Long id) {
    		// ...
    	}
    
    	@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
    	public void remove(@PathVariable Long id) {
    		// ...
    	}
    }
  • 對於整個 controller的CORS 的支持:

    @CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
    @RestController
    @RequestMapping("/account")
    public class AccountController {
    
    	@RequestMapping("/{id}")
    	public Account retrieve(@PathVariable Long id) {
    		// ...
    	}
    
    	@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
    	public void remove(@PathVariable Long id) {
    		// ...
    	}
    }
  • 配置全局CORS 如下所示
    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
    
    	@Override
    	public void addCorsMappings(CorsRegistry registry) {
    		registry.addMapping("/api/**")
    			.allowedOrigins("http://domain2.com")
    			.allowedMethods("PUT", "DELETE")
    			.allowedHeaders("header1", "header2", "header3")
    			.exposedHeaders("header1", "header2")
    			.allowCredentials(false).maxAge(3600);
    	}
    }
  • Spring Framework還提供了一個 CorsFilter
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    public class MyCorsFilter extends CorsFilter {
    
    	public MyCorsFilter() {
    		super(configurationSource());
    	}
    
    	private static UrlBasedCorsConfigurationSource configurationSource() {
    		CorsConfiguration config = new CorsConfiguration();
    		config.setAllowCredentials(true);
    		config.addAllowedOrigin("http://domain1.com");
    		config.addAllowedHeader("*");
    		config.addAllowedMethod("*");
    		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    		source.registerCorsConfiguration("/**", config);
    		return source;
    	}
    }

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章