I-o-C 一篇概览
一、ioC 容器和 Bean介绍
所谓控制翻转即对象通过构造函数参数、工厂方法参数或者属性字段设置来定义依赖,然后容器在创建 bean 的时候注入依赖。这个过程和对象自己管理依赖是完全相反的。
BeanFactory 接口提供了丰富的配置机制来管理各种类型的对象。
ApplicationContext 是 BeanFactory 的子接口,在其原有基础上添加了如下特性:
- 和 Spring AOP 集成更容易。
- 消息处理(国际化应用)
- 事件分发
- 特有的应用层 contexts,例如 web 应用中的 WebApplicationContext。
二、容器总览
配置元数据可以是 XML、Java 注解或者 Java 代码。

三、Bean 总览
BeanDefinition 包含如下元数据:全限定类名(包含包名)、Bean 行为特性(作用域、生命周期回调等)、依赖描述及其它设置。
这些元数据可以通过如下一系列属性来描述:类名、名称、作用域、构造函数参数、属性、Autowire、懒加载、初始化方法、销毁方法。
四、Bean 作用域
a)singleton
默认,每个 IoC 容器一个 Bean 实例。应用于无状态 Bean 场景。
附:对于 singleton 类型 bean 依赖 prototype 类型 bean 的场景,因为容器实例化对象时只会处理一次依赖,所以 singleton 实例依赖的 prototype 对象只是其一。
b)prototype
每次需要 Bean 对象时即创建新的实例。应用于有状态 Bean 场景。
附:对于 prototype 类型 bean,Spring 并没有管理其完整地生命周期,容器只负责实例化、配置及组装依赖。配置的销毁,生命周期回调并不会被调用。
可以通过自定义 bean post-processor 来处理。
c)request
Spring web 应用,对应每次 HTTP 请求生命周期,不同请求之间是隔离的。
d)session
Spring web 应用,对应每次 HTTP Session 生命周期,不同 Session 之间是隔离的。
e)application
Spring web 应用,对应 ServletContext 生命周期,不同 ServletContext 之间是隔离的。
f)websocket
Spring web 应用,对应 WebSocket Session 生命周期。
五、自定义 Bean 特性
1、生命周期回调
- bean 初始化:InitializingBean() -> afterPropertiesSet() 调用。
- bean 销毁:DisposableBean() -> destroy() 调用。
同注解应用:@PostConstruct、@PreDestroy
我们也可以通过实现 BeanPostProcessor 来处理任何回调接口。
除了初始化和销毁回调,Spring 管理的对象也可以通过实现 Lifecycle 接口来参与启动及关闭过程回调。
a)Initialization Callbacks
void afterPropertiesSet() throws Exception;
初始化方法声明如下:
// 或者 @Bean(initMethod = "")
b)Destruction Callbacks
void destroy() throws Exception;
或者 @Bean(destroyMethod = "")
如果存在多种声明机制,则执行顺序为:@PreDestroy -> DisposableBean.destroy() -> 自定义销毁方法
c)Startup and Shutdown Callbacks
public interface Lifecycle { void start(); void stop(); boolean isRunning(); }
这一过程会委托代理给 LifecycleProcessor 进行处理,其定义如下:
public interface LifecycleProcessor extends Lifecycle { void onRefresh(); void onClose(); }
对于存在依赖关系的不同对象,启动及关闭的相应调用顺序就要遵循一定的规则。
如果是直接依赖:依赖方要先于被依赖方启动,并后于被依赖方关闭。
对于非直接依赖关系,如只知道一类类型的对象需要依赖另一类类型的对象,以上的接口将无法满足使用。因此这里需要引入另外一个接口 SmartLifecycle,它的定义如下:
public interface Phased { int getPhase(); } public interface SmartLifecycle extends Lifecycle, Phased { boolean isAutoStartup(); void stop(Runnable callback); }
对于实现了 SmartLifecycle 接口的对象,启动时,phase 小的先启动,关闭时,phase 大的先关闭。
对于未实现 SmartLifecycle 接口的对象,我们可以认定它的 phase 为 0,如此,phase 小于 0 的对象则将先于其启动,后于其关闭。
SmartLifecycle 接口 stop() 方法会接收一个回调,所有实现此接口的对象都需要在其关闭过程执行完毕后调用一次 run() 方法。
LifecycleProcessor 接口的默认实现 DefaultLifecycleProcessor, 会等待所有对象执行完回调(可以通过 timeoutPerShutdownPhase 设置超时),藉由此机制,我们可以在需要的时候的时候实现应用异步关闭逻辑。
d)非 web 应用 IoC 容器的优雅关闭
注册 JVM shutdown hook:ConfigurableApplicationContext.registerShutdownHook(),实例如下:
public final class Boot { public static void main(final String[] args) throws Exception { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); // add a shutdown hook for the above context... ctx.registerShutdownHook(); // app runs here... // main method exits, hook is called prior to the app shutting down... } }
2、ApplicationContextAware 和 BeanNameAware
a)ApplicationContextAware
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
这种机制虽然在某些场景会很有用,但是它引发了代码的耦合,破坏了 IoC 机制,因此并不推荐。
另外一种获取 ApplicationContext 资源的方法是通过自动装配的方式引入 ApplicationContext 对象依赖,如构造函数或者 setter。 推荐使用 @Autowired 注解,更加灵活方便。
b)BeanNameAware
public interface BeanNameAware { void setBeanName(String name) throws BeansException; }
3、其它 Aware 资源接口
ApplicationEventPublisherAware、BeanClassLoaderAware、BeanFactoryAware、LoadTimeWeaverAware、MessageSourceAware、NotificationPublisherAware、ResourceLoaderAware、ServletConfigAware、ServletContextAware。
六、容器扩展
1、BeanPostProcessor 自定义 bean 特性
org.springframework.beans.factory.config.BeanPostProcessor 接口包含两个回调方法:
package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; import org.springframework.lang.Nullable; public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
分别对应初始化方法(InitializingBean.afterPropertiesSet() 或自定义初始化方法)前后需要执行的逻辑。
需要注意的是:如果是使用 @Bean 注解工厂方法。那么返回的结果需要是实现了 BeanPostProcessor 接口的类对象类型。否则 ApplicationContext 在创建它前无法自动检测其类型。
可以通过 addBeanPostProcessor 方法向 ConfigurableBeanFactory 注册。通过此方式注册实例 Ordered 接口作用将失效,会按照注册的顺序执行,并且优先于所有自动检测注册的前置处理器。
2、BeanFactoryPostProcessor 自定义 bean 定义
可以配置多个 BeanFactoryPostProcessor 实例并通过实现 Ordered 接口设置调用顺序。
应用:PropertySourcesPlaceholderConfigurer、PropertyOverrideConfigurer
七、基于注解的容器配置
基于注解的注入先于 XML 配置。同样的注入会产生覆盖。
1、@Autowired 自动装配
用在哪里注入装配哪里。byType。
a)构造器:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
对象只有多个构造器的时候,用以标明哪个构造器供容器使用。
b)setter 方法:
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
c)任意方法,任意参数:
public class MovieRecommender { private MovieCatalog movieCatalog; private CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
d)属性字段:
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired private MovieCatalog movieCatalog; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
e)获取容器内特定类型对象集合
默认需要至少有一个特定类型对象,否则会发生装配失败。
数据和列表
public class MovieRecommender { @Autowired private MovieCatalog[] movieCatalogs; // ... } 或 public class MovieRecommender { private SetmovieCatalogs; @Autowired public void setMovieCatalogs(Set movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
如果需要列表里的元素排序,则可以对收集的对象应用 Ordered 接口,或者添加 @Order 注解。
Map 类型收集:key 为 bean 名称,value 为 bean 对象。
public class MovieRecommender { private MapmovieCatalogs; @Autowired public void setMovieCatalogs(Map movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ... }
f)默认装配行为
对于方法及属性字段的注解默认行为为必须。可以通过配置变更:
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required = false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
public class SimpleMovieLister { @Autowired public void setMovieFinder(OptionalmovieFinder) { ... } } 或者 public class SimpleMovieLister { @Autowired public void setMovieFinder(@Nullable MovieFinder movieFinder) { ... } }
g)装配使用特定已知框架资源
例如 BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher 及 MessageSource 等。
如上接口的扩展接口,如ConfigurableApplicationContext、ResourcePatternResolver 等。
如下:
public class MovieRecommender { @Autowired private ApplicationContext context; public MovieRecommender() { } // ... }
h)附注
@Autowired、@Inject、@Value 及@Resource 注解都是通过 BeanPostProcessor 接口实现处理的。因此不能应用于自定义的 BeanPostProcessor 或者 BeanFactoryPostProcessor 类型实现。
2、@Primary
当存在多个同类型装配对象时,可以通过 @Primary 来标示使用哪个对象。如下:
@Configuration public class MovieConfiguration { @Bean @Primary public MovieCatalog firstMovieCatalog() { ... } @Bean public MovieCatalog secondMovieCatalog() { ... } // ... } 。。。 public class MovieRecommender { @Autowired private MovieCatalog movieCatalog; //firstMovieCatalog
// ... }
3、Qualifiers
@Qualifier 缩小符合装配的对象范围。
public class MovieRecommender { @Autowired @Qualifier("main") private MovieCatalog movieCatalog; // ... } 或 public class MovieRecommender { private final MovieCatalog movieCatalog; private final CustomerPreferenceDao customerPreferenceDao; @Autowired public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) { this.movieCatalog = movieCatalog; this.customerPreferenceDao = customerPreferenceDao; } // ... }
符合 Qualifier 值得对象可以不唯一。
4、@Resource
JSR-250 注解。byName。
注解 bean 属性字段或者 setter 方法。
public class SimpleMovieLister { private MovieFinder movieFinder; @Resource(name="myMovieFinder") public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
如果不指定名称,丢与属性字段则使用字段名,对于 setter 方法,则使用 bean 属性名。
首先通过名称查找,如果找不到则通过类型查找。
5、@Value
用于注入外部配置。
配置文件:application.properties
catalog.name=MovieCatalog
配置:
@Configuration @PropertySource("classpath:application.properties") public class AppConfig { }
使用:
@Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; } }
八、基于 Java 代码的容器配置
1、核心概念
@Bean 通常和 @Configuration 结合使用(也可以用在任何 Spring @Component 注解管理的对象内)。
@Configuration public class AppConfig { @Bean public MyServiceImpl myService() { return new MyServiceImpl(); } }
等同于:
2、AnnotationConfigApplicationContext
对于 @Component 及 JSR-330 注解的类:除了类本身会被注册为 bean 定义,并且会处理其内 @Autowired 或者 @Inject 注解相关的注入逻辑。
a)如下基于构造器示例:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); } // public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
b)如下基于编码注册处理示例:
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(); }
c)基于自动扫描处理示例:
@Configuration @ComponentScan(basePackages = "com.acme") (1) public class AppConfig { // ... } // 或
AnnotationConfigApplicationContext 暴露了相应的 scan 接口,用于编码方式执行扫描操作:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.acme"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); }
d、AnnotationConfigWebApplicationContext
Spring web 应用。包括注册 Spring ContextLoaderListener servlet listener、Spring MVC DispatcherServlet 等等。
3、@Bean 注解
a)bean 定义
注解方法,用于 bean 定义注册。方法名称默认为 bean 名称。返回值类型为 bean 对象类型(具体实现类或者对应接口类型)。
示例见 1、基础概念处。
也可以通过接口默认方法定义:
public interface BaseConfig { @Bean default TransferServiceImpl transferService() { return new TransferServiceImpl(); } } @Configuration public class AppConfig implements BaseConfig { }
b)bean 依赖
bean 定义方法可以有任意多个参数,通过如下方式定义依赖:
@Configuration public class AppConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); } }
c)生命周期回调
public class BeanOne { public void init() { // initialization logic } } public class BeanTwo { public void cleanup() { // destruction logic } } @Configuration public class AppConfig { @Bean(initMethod = "init") public BeanOne beanOne() { return new BeanOne(); } @Bean(destroyMethod = "cleanup") public BeanTwo beanTwo() { return new BeanTwo(); } }
c)bean 作用域定义
@Configuration public class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... } }
scope-procy:
@Configuration public class MyConfiguration { @Bean @Scope(proxyMode = ScopedProxyMode.INTERFACES) public Encryptor encryptor() { // ... } }
d)自定义 bean 名称
定义单个名称或者多个:
@Configuration public class AppConfig { @Bean("myThing") public Thing thing() { return new Thing(); } } // @Configuration public class AppConfig { @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"}) public DataSource dataSource() { // instantiate, configure and return DataSource bean... } }
4、@Configuration 注解
如下:通过方法调用注入依赖
@Configuration public class AppConfig { @Bean public ClientService clientService1() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientService clientService2() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientDao clientDao() { return new ClientDaoImpl(); } }
默认单例模式管理的 bean 定义。如上,两次 clientDao() 方法调用并不会产生两个 ClientDao 对象。在对象实例化时会首先检查容器相应 bean 实例对象缓存,然后再决定是否需要调用相应的实例化方法。
查找方法注入:
public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); } // @Bean @Scope("prototype") public AsyncCommand asyncCommand() { AsyncCommand command = new AsyncCommand(); // inject dependencies here as required return command; } @Bean public CommandManager commandManager() { // return new anonymous implementation of CommandManager with createCommand() // overridden to return a new prototype Command object return new CommandManager() { protected Command createCommand() { return asyncCommand(); } } }
5、Import 注解
用于引入配置类,如下:
@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(); } }
如上,容器实例化时,只需要处理 ConfigB 即可:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); // now both beans A and B will be available... A a = ctx.getBean(A.class); B b = ctx.getBean(B.class); }
Spring Framework 4.2 开始,除了 @Configuration 注解对象,@Import 可以同时处理其它类型对象。
当需要精细处理对象依赖引入时,可以使用此注解,避免大包扫描。
九、环境抽象
1、Bean Definition Profiles
- 测试环境使用基于内存的数据源,QA 及 生产环境使用 JNDI 数据源。
- 只在线上环境启动监控功能。
- 针对不同用户注册不同的功能 bean 对象。
如下:JndiDataConfig 配置类只在 profile 为 production 时进行容器注册,使用
@Configuration @Profile("production") public class JndiDataConfig { @Bean(destroyMethod = "") (1) public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } }
注解 value 可以使单一 profile 值,也可以是多个值得数组,亦或者为组合表达式。
@Profile("production") @Profile({"QA", "production"}) @Profile("QA&production")
自定义 profile 环境注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Profile("production") public @interface Production { } //等价于 @Profile("production")
@Profile 作为顶层环境配置,控制所有组合使用的注解资源,如@Configuration、@Import等。
profile 激活
直接编码方式设置:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("development"); ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); ctx.refresh();
或环境变量属性配置:
-Dspring.profiles.active="profile1,profile2"
2、PropertySource 抽象
Spring Environment 抽象提供属性查询操作(基于可配置的,层级的属性源),如下:
ApplicationContext ctx = new GenericApplicationContext(); Environment env = ctx.getEnvironment(); boolean containsMyProperty = env.containsProperty("my-property"); System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
PropertySource:k-v 组配置资源抽象。
StandardEnvironment 基于两类属性源配置:JVM 系统属性(System.getProperties())和系统环境变量(System.getenv())。
StandardServletEnvironment 除了上述两项配置外,还包括 servlet config、servlet context parameters 及 JndiPropertySource(如果存在 JNDI 资源需求)。
- ServletConfig parameters (if applicable — for example, in case of a DispatcherServlet context)
- ServletContext parameters (web.xml context-param entries)
- JNDI environment variables (java:comp/env/ entries)
- JVM system properties (-D command-line arguments)
- JVM system environment (operating system environment variables)
我们也可以添加自定义的 PropertySource 并将其添加当前环境 PropertySource 组,如下:
ConfigurableApplicationContext ctx = new GenericApplicationContext(); MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); sources.addFirst(new MyPropertySource());
通过 MutablePropertySources 暴露的方法 addFirst(),将自定义的 MyPropertySource 添加到资源最上层位置,优先供给查询。
@PropertySource 使用
app.properties 文件:
testbean.name=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; } }
@PropertySource 位置 ${…} 占位符会使用环境内其它已注册的 PropertySource 资源处理。如下:
@Configuration @PropertySource("classpath:/com/${my.placeholder:default/path}/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 资源,查询不到则使用默认 "default/path",如果不存则抛出异常 IllegalArgumentException。
十、ApplicationContext 扩展功能
1、MessageSource 国际化(i18n)
ApplicationContext 通过实现 MessageSource 接口来提供国际化(i18n)功能。
Spring 同时也提供了 HierarchicalMessageSource 接口,用以逐层获取特定消息。
标准方法:
String getMessage(String code, Object[] args, String default, Locale loc)
ApplicationContext 加载时会自动查找容器内定义的 MessageSource bean,且 bean 的名称必须为 messageSource。
当执行消息查找获取操作时,Spring 会将操作代理给命名为 messageSource 的 bean。如果不存在此 bean,则从父类中查找,如果找不到则实例化一个空的 DelegatingMessageSource 用以执行相应的方法操作。
ResourceBundleMessageSource 使用如下:
定义:
@Bean public ResourceBundleMessageSource resourceBundleMessageSource() { ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); resourceBundleMessageSource.setBasenames("format", "exceptions", "windows")
return resourceBundleMessageSource; } // 或
format exceptions windows
format.properties:
message=Alligators rock!
exception.properties
argument.required=The {0} argument is required.
使用:
public static void main(String[] args) { MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); String message = resources.getMessage("message", null, "Default", Locale.ENGLISH); System.out.println(message); }
对于不同的 Locale,则定义相应的不同资源文件:
Locale 示例:
public final class Locale implements Cloneable, Serializable { static private final Cache LOCALECACHE = new Cache(); /** Useful constant for language. */ static public final Locale ENGLISH = createConstant("en", ""); /** Useful constant for language. */ static public final Locale JAPANESE = createConstant("ja", ""); /** Useful constant for language. */ static public final Locale CHINESE = createConstant("zh", ""); /** Useful constant for language. */ static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN"); /** Useful constant for language. */ static public final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");
... ...
资源文件示例:
exceptions_zh_CN.properties、exceptions_ja.properties
Spring MessageSource 是基于 JAVA MessageSource,所以并不会合并相同 basename 的 bundle。
Spring 另外提供了 ReloadableResourceBundleMessageSource 接口。除了可以提供如上基本功能外,它可以从任意 Spring 定义的资源位置读取文件,并且支持热加载。
2、标准的及自定义的事件
ApplicationContext 通过 ApplicationEvent 类及 ApplicationListener 接口来处理事件。容器内实现了 ApplicationListener 接口的对象能够获取任何 ApplicationEvent 发布的事件。 这种属于标准的观察者模式应用。
Spring 4.2 之后,事件框架做了显著升级,包括基于注解的实现及事件对象不再需要显式的继承 ApplicationEvent。
如下为 Spring 提供的标准事件:
ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent、ServletRequestHandledEvent。
自定义事件:
事件定义:
public class BlockedListEvent extends ApplicationEvent { private final String address; private final String content; public BlockedListEvent(Object source, String address, String content) { super(source); this.address = address; this.content = content; } // accessor and other methods... }
事件发布:
public class EmailService implements ApplicationEventPublisherAware { private ListblockedList; private ApplicationEventPublisher publisher; public void setBlockedList(List blockedList) { this.blockedList = blockedList; } public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } public void sendEmail(String address, String content) { if (blockedList.contains(address)) { publisher.publishEvent(new BlockedListEvent(this, address, content)); return; } // send email... } }
事件监听:
public class BlockedListNotifier implements ApplicationListener{ private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } public void onApplicationEvent(BlockedListEvent event) { // notify appropriate parties via notificationAddress... } }
基于注解的事件监听:
public class BlockedListNotifier { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } @EventListener public void processBlockedListEvent(BlockedListEvent event) { // notify appropriate parties via notificationAddress... } }
异步监听:
@EventListener @Async public void processBlockedListEvent(BlockedListEvent event) { // BlockedListEvent is processed in a separate thread }
顺序监听:
@EventListener @Order(42) public void processBlockedListEvent(BlockedListEvent event) { // notify appropriate parties via notificationAddress... }
3、应用启动追踪
- application context lifecycle (base packages scanning, config classes management)
- beans lifecycle (instantiation, smart initialization, post processing)
- application events processing
// create a startup step and start recording StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan"); // add tagging information to the current step scanPackages.tag("packages", () -> Arrays.toString(basePackages)); // perform the actual phase we're instrumenting this.scanner.scan(basePackages); // end the current step scanPackages.end();
可以通过实现 ApplicationStartup 接口,自定义启动追踪类。如下:
.setApplicationStartup(new MyApplicationStartup())
文章来源于互联网:I-o-C 一篇概览