1、IOC容器
- 當對象實例化後依賴的對象才被創建,當創建bean後容器注入這些依賴對象。這個過程基本上是反向的,因此命名爲控制反轉(IoC)。
BeanFactory
接口提供一種高級的配置機制能夠管理任何類型的對象。ApplicationContext
是BeanFactory
的子接口。它能更容易集成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); } }
通常,地域設置通過應用周圍環境管理的。 -
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
來處理這個。Validator
和DataBinder
組成了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
通常不會被應用程序的代碼直接使用,而是由DataBinder
和BeanFactory
使用。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; } }
以下的代碼片段展示瞭如何檢索和操縱實例化的
Companies
和Employees
的某些屬性: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
的概念來實現Object
和String
之間的轉換。舉個例子,一個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 an
OPTIONS
method) 被自動調度到各種已註冊的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; } }