javascript
Spring--IoC(2)
目錄
?
容器擴展點
BeanPostProcessor
編程式注冊BeanPostProcessor實例
BeanPostProcessor實例和AOP自動代理
示例
BeanFactoryPostProcessor
FactoryBean
基于注解的配置
@Required
@Autowired
@Primary
@Qualifier
CustomAutowireConfigurer
@Resource
@PostConstruct和@PreDestroy
ClassPath掃描和受管組件
@Component及其延伸
元注解
自動檢測類并注冊bean定義
使用過濾器來自定義掃描
在組件中定義Bean元數據
自動檢測組件命名
提供自動檢查組件的Scope
JSR-330 規范注解
@Inject
@Named and @ManagedBean
基于Java 的容器配置
AnnotationConfigApplicationContext
簡單構造
使用register(Class…?)編程式構建容器
開啟組件掃描
AnnotationConfigWebApplicationContext
@Bean
@Configuration
組合基于Java的配置
@Import
Conditionally Include @Configuration Classes or @Bean Methods
環境抽象
bean定義的profile
@Profile
Xml定義profile
Activating a Profile
默認profile
PropertySource抽象
@PropertySource
占位符解析
注冊LoadTimeWeaver
ApplicationContext的附加能力
MessageSource接口--國際化
標準和自定義事件
標準事件
自定義事件
基于注解的事件監聽器
異步監聽器
排序監聽器
泛型事件
容器擴展點
通常,應用程序開發者,不需要繼承ApplicationContext的實現類。相反,Spring IoC 容器可以通過插入特殊的集成接口來實現擴展。
BeanPostProcessor
BeanPostProcessor定義了回調方法,通過實現這個回調方法,可以提供自己的(或重寫容器默認的)實例化邏輯、依賴分析邏輯等。如果想在Spring 容器完成實例化、配置和初始化bean 后,實例化一些自定義的邏輯,可以插入一個或多個BeanPostProcessor的實現。
開發者可以配置多個BeanPostProcessor 實例, 并通過設置order屬性來控制這些BeanPostProcessor執行的順序,僅BeanPostProcessor實現Ordered接口才可以設置order屬性。如果編寫自己的BeanPostProcessor ,也應該考慮實現Ordered 接口。
接口方法:
@Nullable
default Object postProcessAfterInitialization(Object bean,String beanName)throws BeansException
@Nullable
default Object postProcessBeforeInitialization(Object bean,String beanName)throws BeansException
BeanPostProcessor實例在bean(或對象)實例上操作。也就是說,spring ioc容器實例化一個bean實例,然后BeanPostProcessor實例執行它們的工作。
BeanPostProcessor實例的作用域是每個容器。只有在使用容器層次結構時,這才相關。如果在一個容器中定義BeanPostProcessor,則它只對該容器中的bean進行后期處理。換句話說,在一個容器中定義的bean不會由在另一個容器中定義的BeanPostProcessor進行后期處理,即使兩個容器都是同一層次結構的一部分。
要更改實際的bean定義(即定義bean的藍圖),您需要使用BeanFactoryPostProcessor。
BeanPostProcessor會在容器的初始化方法(InitializingBean.afterPropertiesSet() 或者任何init方法)調用前和所有初始化回調之后被調用。
BeanPostProcessor可以對bean采取任何措施,包括完全忽略回調。一個BeanPostProcessor,通常會檢查回調接口或使用代理包裝一個bean 。一些Spring AOP 基礎設施類,為了提供包裝式的代理邏輯,被實現為BeanPostProcessor。
ApplicationContext 會自動地檢測所有定義在配置元文件中,并實現了BeanPostProcessor 接口的bean。該ApplicationContext注冊這些bean作為后置處理器,使它們可以在bean 創建完成后被調用。bean后置處理器可以像其他bean一樣被部署到容器中。
注意,當通過在配置類上使用@bean注解工廠方法聲明BeanPostProcessor時,工廠方法的返回類型應該是實現類本身,或者至少是org.springframework.beans.factory.config.BeanPostProcessor接口,清楚地指示后置處理器特性。否則,ApplicationContext在完全創建之前無法按類型自動檢測它。由于BeanPostProcessor需要提前實例化,以便應用于上下文中其他bean的初始化,因此這種早期類型檢測非常關鍵。
編程式注冊BeanPostProcessor實例
雖然官方推薦注冊BeanPostProcessor的方法是通過ApplicationContext自動檢測(如前所述),但可以使用ConfigurableBeanFactory 的addBeanPostProcessor 方法注冊它們。當您需要在注冊之前評估條件邏輯,甚至需要跨層次結構中的上下文復制bean后處理器時,這一點非常有用。但是,以編程方式添加的BeanPostProcessor實例不遵守Ordered 接口。在這里,注冊的順序決定了執行的順序。還要注意,無論顯式排序如何指定,以編程方式注冊的BeanPostProcessor實例總是在通過自動檢測注冊的實例之前進行處理。
BeanPostProcessor實例和AOP自動代理
實現BeanPostProcessor接口的類是特殊的,容器對它們的處理方式不同。所有BeanPostProcessor實例和它們直接引用的bean都在啟動時實例化,作為ApplicationContext的特殊啟動階段的一部分。接下來,所有BeanPostProcessor實例都以有序方式注冊,并應用于容器中的所有其他bean。由于AOP自動代理是作為BeanPostProcessor本身實現的,因此BeanPostProcessor實例或它們直接引用的bean都不符合自動代理的條件,因此,它們中沒有Aspect被weave進去。
對于任何這樣的bean,您都應該看到一條信息日志消息:bean somebean不適合由所有BeanPostProcessor接口處理(例如:不適合自動代理)。
如果通過使用autowiring或@Resource(可能會返回到autowiring)將bean注入到BeanPostProcessor中,spring可能會在搜索類型匹配的依賴項候選者時訪問意外的bean,因此使它們不符合自動代理或其他類型的bean后置處理。
例如,如果有一個用@Resource注解的依賴項,其中字段或setter名稱與bean的聲明名稱不直接對應,并且沒有使用name屬性,那么spring將訪問其他bean以按類型匹配它們。
示例
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
??? // simply return the instantiated bean as-is
??? public Object postProcessBeforeInitialization(Object bean, String beanName) {
??????? return bean; // we could potentially return any object reference here...
??? }
??? public Object postProcessAfterInitialization(Object bean, String beanName) {
??????? System.out.println("Bean '" + beanName + "' created : " + bean.toString());
??????? return bean;
??? }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
??? <lang:groovy id="messenger"
??????????? script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
??????? <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
??? </lang:groovy>
??? <!--
??? when the above bean (messenger) is instantiated, this custom
??? BeanPostProcessor implementation will output the fact to the system console
??? -->
??? <bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
BeanFactoryPostProcessor
BeanFactoryPostProcessor操作bean的配置元數據,也就是說,Spring 的IoC容器允許BeanFactoryPostProcessor來讀取配置元數據,并可以在容器實例化任何bean ( 除了BeanFactoryPostProcessor )之前修改它.
可以設置多個BeanFactoryPostProcessor實例,如果實現了Ordered接口,則可以通過order屬性按序執行。
Spring預置的BeanFactoryPostProcessor有PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。
FactoryBean
當開發者需要向容器請求一個真實的FactoryBean 實例(而不是由它生成的bean ),且調用ApplicationContext 的getBean()方法時,在bean 的id 之前加連字符"&" 。所以對于一個給定id為myBean 的FactoryBean ,調用容器的getBean("myBean")方法返回的是FactoryBean 的產品;而調用getBean("&myBean")方法則返回FactoryBean 實例本身。
基于注解的配置
Spring 2.5 也添加了對JSR-250 注解的支持,如@Resource 、@PostConstruct 和@PreDestroy 。Spring3.0添加了對JSR-330 注解的支持,包含在javax.inject 包下,如@Inject 、@Qualifier、@Named 和@Provided等。使用這些注解也需要在Spring 容器中注冊特定的BeanPostProcessor 。
注意: 基于注解的配置注入會在基于XML 配置注入之前執行,因此同時使用兩種方式,會使后面的配置覆蓋前面裝配的屬性
<context:annotation-config/>
隱式注冊的后處理器包括AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor
@Required
@Required 注解應用于bean 屬性的setter 方法。
@Autowired
可以使用@Autowired 注解到傳統的setter 方法中。JSR-330的@Inject 注解可以代替以上示例中Spring 的@Autowired 注解
推薦使用@Autowired 的required屬性(不是@Required )注解。一方面,required 屬性表示了屬性對于自動裝配目的不是必需的,如果它不能被自動裝配,那么屬性就會被忽略
@Primary
因為通過類型的自動裝配可能有多個候選者,那么在選擇過程中通常是需要更多控制的。達成這個目的的一種做法是Spring 的@Primary 注解。當一個依賴有多個候選者bean 時,@Primary 指定了一個優先提供的特殊bean。
@Configuration
public class MovieConfiguration {
??? @Bean
??? @Primary
??? public MovieCatalog firstMovieCatalog() { ... }
??? @Bean
??? public MovieCatalog secondMovieCatalog() { ... }
??? // ...
}
??? <bean class="example.SimpleMovieCatalog" primary="true">
??????? <!-- inject any dependencies required by this bean -->
??? </bean>
??? <bean class="example.SimpleMovieCatalog">
??????? <!-- inject any dependencies required by this bean -->
??? </bean>
@Qualifier
達成更多控制目的的另一種做法是Spring的@Qualifier 注解。開發者可以用特定的參數來關聯限定符的值,縮小類型的集合匹配,為每一個參數來選擇一個特定的bean。
?@Qualifier可以應用于字段,構造函數參數或方法參數。
public class MovieRecommender {
??? @Autowired
??? @Qualifier("main")
??? private MovieCatalog movieCatalog;
??? // ...
}
public class MovieRecommender {
??? private MovieCatalog movieCatalog;
??? private CustomerPreferenceDao customerPreferenceDao;
??? @Autowired
??? public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
??????????? CustomerPreferenceDao customerPreferenceDao) {
??????? this.movieCatalog = movieCatalog;
??????? this.customerPreferenceDao = customerPreferenceDao;
??? }
??? // ...
}
?<bean class="example.SimpleMovieCatalog">
??? <qualifier value="main"/>
??? <!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
??? <qualifier value="action"/>
??? <!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
具有@Qualifier("main")的bean僅會注入具有相同qualifier的字段或方法參數中。
可以創建自己的自定義Qualifier注解。
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
??? String value();
}
public class MovieRecommender {
??? @Autowired
??? @Genre("Action")
??? private MovieCatalog actionCatalog;
??? private MovieCatalog comedyCatalog;
??? @Autowired
??? public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
??????? this.comedyCatalog = comedyCatalog;
??? }
??? // ...
}
可以添加<qualifier/>到bean元素。
<bean class="example.SimpleMovieCatalog">
??? <qualifier type="Genre" value="Action"/>
??? <!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
??? <qualifier type="example.Genre" value="Comedy"/>
??? <!-- inject any dependencies required by this bean -->
</bean>
自定義@Qualifier也可以不指定value
???
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
public class MovieRecommender {
??? @Autowired
??? @Offline
??? private MovieCatalog offlineCatalog;
??? // ...
}???
自定義@Qualifier也可以定義其他屬性
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
??? String genre();
??? Format format();
}
public class MovieRecommender {
??? @Autowired
??? @MovieQualifier(format=Format.VHS, genre="Action")
??? private MovieCatalog actionVhsCatalog;
??? @Autowired
??? @MovieQualifier(format=Format.VHS, genre="Comedy")
??? private MovieCatalog comedyVhsCatalog;
??? // ...
}
Xml配置:
??? <bean class="example.SimpleMovieCatalog">
??????? <qualifier type="MovieQualifier">
??????????? <attribute key="format" value="VHS"/>
??????????? <attribute key="genre" value="Action"/>
??????? </qualifier>
??????? <!-- inject any dependencies required by this bean -->
??? </bean>
CustomAutowireConfigurer
CustomAutowireConfigurer 是一個BeanFactoryPostProcessor,允許您注冊自己的自定義限定符注解類型,即使它們沒有用spring的@ualifier注解進行注解
<bean id="customAutowireConfigurer"
??????? class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
??? <property name="customQualifierTypes">
??????? <set>
??????????? <value>example.CustomQualifier</value>
??????? </set>
??? </property>
</bean>
?
AutowireCandidateResolver決定自動注入候選:
@Resource
Spring 也支持使用JSR-250的@Resource 注解在字段或bean屬性的setter 方法上注入
@Resource使用name屬性,默認情況下Spring 解析這個值作為要注入的bean的名稱
public class SimpleMovieLister {
??? private MovieFinder movieFinder;
??? @Resource(name="myMovieFinder")
??? public void setMovieFinder(MovieFinder movieFinder) {
??????? this.movieFinder = movieFinder;
??? }
}
如果沒有明確地指定name值,那么默認的名稱就從字段名稱或setter 方法中派生出來。
當@Resource未指定name屬性時,和@Autowired類似,會優先使用primary匹配。
@PostConstruct和@PreDestroy
ClassPath掃描和受管組件
自Spring3.0 開始,很多由Spring JavaConfig 項目提供的特性成為Spring 框架核心的一部分。這就允許開發人員使用Java 來定義bean
@Component及其延伸
- @Repository
- @Service
- @Controller
元注解
Spring 提供了很多元注解。元注解就是能被應用到另一個注解上的注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
??? // ....
}
//Service注解將被像Component注解一樣被對待。
元注解也可以被用于創建組合注解。例如, Spring MVC 的@RestController注解就是@Controller和@ResponseBody的組合 。
另外,組合注解可能從元注解中任意重新聲明屬性來允許用戶自定義。這個在開發者只想暴露一個元注解的子集時會特別有用
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
??? /**
???? * Alias for {@link Scope#proxyMode}.
???? * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
???? */
??? @AliasFor(annotation = Scope.class)
??? ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
自動檢測類并注冊bean定義
需要添加@ComponentScan到自己的@Configuration類上,其中的base-package元素是這兩個類的公共父類包。
@Configuration
@ComponentScan(basePackages = "org.example")
//或者 @ComponentScan("org.example")
public class AppConfig? {
??? ...
}
<context:component-scan base-package="org.example"/>
<context:component-scan>隱式啟用了<context:annotation-config>。AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor也被隱式啟用。
開發人員可以任意選擇使用逗號、分號、空格分隔的列表將每個類引人父包。
使用過濾器來自定義掃描
在@ComponentScan 注解中添加includeFilters 或excludeFilters 參數(或者作為component-scan元素的include-filter或exclude-filter子元素),可以擴展掃描 。每個過濾器元素需要type 和expression 屬性。
| 過濾類型 | 示例 | 描述 |
| annotation (default) | org.example.SomeAnnotation | 匹配應用了指定注解的類 |
| assignable | org.example.SomeClass | 指定類和接口的超類(接口) |
| aspectj | org.example..*Service+ | Aspectj |
| regex | org\.example\.Default.* | 正則表達式匹配類名稱。 |
| custom | org.example.MyTypeFilter | 自定義Filter的實現類 |
@Configuration
@ComponentScan(basePackages = "org.example",
??????? includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
??????? excludeFilters = @Filter(Repository.class))
public class AppConfig {
??? ...
}
?
<beans>
??? <context:component-scan base-package="org.example">
??????? <context:include-filter type="regex"
??????????????? expression=".*Stub.*Repository"/>
??????? <context:exclude-filter type="annotation"
??????????????? expression="org.springframework.stereotype.Repository"/>
??? </context:component-scan>
</beans>
?
在組件中定義Bean元數據
@Bean
@Qualifier
自動檢測組件命名
組件命名由BeanNameGenerator策略執行,默認情況,Spring的原型注解(@Component, @Repository, @Service, @Controller)的value屬性提供了組件的名稱。如果沒有指定,則使用Java Bean規范生成。也可以指定@ComponentScan的nameGenerator屬性,此屬性為BeanNameGenerator的實現。
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
??? ...
}
?
<beans>
??? <context:component-scan base-package="org.example"
??????? name-generator="org.example.MyNameGenerator" />
</beans>
提供自動檢查組件的Scope
@Scope注解。默認為singleton,
也可以指定@ComponentScan的scopeResolver屬性,此屬性為ScopeMetadataResolver的實現
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
??? ...
}
<beans>
??? <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
JSR-330 規范注解
@Inject
@javax.inject.Inject可以代替@Autowired
@Named and @ManagedBean
@javax.inject.Named or javax.annotation.ManagedBean可以代替@Component
基于Java 的容器配置
Spring 中新的Java 配置支持的核心就是@Configuration注解的類和@Bean注解的方法。
AnnotationConfigApplicationContext
簡單構造
public static void main(String[] args) {
??? ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
??? MyService myService = ctx.getBean(MyService.class);
??? myService.doStuff();
}
AnnotationConfigApplicationContext不僅僅局限于與@Configuration類合作,任意@Component或JSR-330 注解的類都可以作為構造方法的輸入
使用register(Class<?>…?)編程式構建容器
public static void main(String[] args) {
??? AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
??? ctx.register(AppConfig.class, OtherConfig.class);
??? ctx.register(AdditionalConfig.class);
??? ctx.refresh();
??? MyService myService = ctx.getBean(MyService.class);
??? myService.doStuff();
}
開啟組件掃描
在@Configuration注解的類上加@ComponentScan
使用scan
public static void main(String[] args) {
??? AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
??? ctx.scan("com.acme");
??? ctx.refresh();
??? MyService myService = ctx.getBean(MyService.class);
}
AnnotationConfigWebApplicationContext
@Bean
@Configuration
組合基于Java的配置
@Import
與<import>一樣,@Import 注解允許從其他配置類中加載@Bean 的配置。
@Configuration
public class ConfigA {
??? @Bean
??? public A a() {
??????? return new A();
??? }
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
??? @Bean
??? public B b() {
??????? return new B();
??? }
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
??? @Bean
??? public DataSource dataSource() {
??????? // return new DataSource
??? }
}
Conditionally Include @Configuration Classes or @Bean Methods
@Profile
@Conditional
組合基于Java的和基于XML的配置
環境抽象
bean定義的profile
@Profile
@Profile定義僅在定義的profile中,bean生效。
@Configuration
@Profile("development")
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();
??? }
}
支持:!,& ,| 操作符
@Profile({"p1", "p2"})?? #如果p1,p2是active,則生效
@Profile({"p1", "!p2"}) #如果p1 active 或者p2不是active,則生效
Xml定義profile
<beans profile="development" ...>
??? <jdbc:embedded-database id="dataSource">
??????? <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
??????? <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
??? </jdbc:embedded-database>
</beans>
Activating a Profile
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
-Dspring.profiles.active="profile1,profile2"
默認profile
@Configuration
@Profile("default")
public class DefaultDataConfig {
??? @Bean
??? public DataSource dataSource() {
??????? return new EmbeddedDatabaseBuilder()
??????????? .setType(EmbeddedDatabaseType.HSQL)
??????????? .addScript("classpath:com/bank/config/sql/schema.sql")
??????????? .build();
??? }
}
-Dspring.profiles.default=
PropertySource抽象
PropertySource 是key-value的一個簡單抽象,StandardEnvironment包含2個PropertySource對象:JVM屬性(System.getProperties()),系統環境變量(System.getenv())
StandardServletEnvironment變量優先級
- 1、ServletConfig parameters (if applicable — for example, in case of a DispatcherServlet context)
- 2、ServletContext parameters (web.xml context-param entries)
- 3、JNDI environment variables (java:comp/env/ entries)
- 4、JVM system properties (-D command-line arguments)
- 5、JVM system environment (operating system environment variables)
@PropertySource
@PropertySource注解提供了一種方便的機制來將PropertySource增加到Spring的Environment中
@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;
??? }
}
任何@PropertySource中形如${...}的占位符,都可以被解析為Environment中的屬性資源。
占位符解析
以前,占位符的值是只能對JVM系統屬性或環境變量來解析的。如今,因為環境抽象已經繼承到了容器中,很容易通過容器將占位柯:解析集成。這意味著開發者可以任意地配置占位符。
注冊LoadTimeWeaver
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
?
<beans>
??? <context:load-time-weaver/>
</beans>
ApplicationContext的附加能力
MessageSource接口--國際化
標準和自定義事件
標準事件
- ContextRefreshedEvent
- ContextStartedEvent
- ContextStoppedEvent
- ContextClosedEvent
- RequestHandledEvent
自定義事件
繼承ApplicationEvent
基于注解的事件監聽器
@EventListener
異步監聽器
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
??? // BlackListEvent is processed in a separate thread
}
排序監聽器
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
??? // notify appropriate parties via notificationAddress...
}
?
泛型事件
?
總結
以上是生活随笔為你收集整理的Spring--IoC(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring--IoC(1)
- 下一篇: Spring Cloud异常