世界新动态:I-o-C 一篇概览
IoC(Inversion of Control)也被称之为 DI(dependency injection),名称侧重点略有不同。
所谓控制翻转即对象通过构造函数参数、工厂方法参数或者属性字段设置来定义依赖,然后容器在创建 bean 的时候注入依赖。这个过程和对象自己管理依赖是完全相反的。
org.springframework.beans和org.springframework.context是 Spring 框架 IoC 容器的基础包。
(相关资料图)
BeanFactory接口提供了丰富的配置机制来管理各种类型的对象。
ApplicationContext是BeanFactory的子接口,在其原有基础上添加了如下特性:
和 Spring AOP 集成更容易。
消息处理(国际化应用)
事件分发
特有的应用层 contexts,例如 web 应用中的WebApplicationContext。
BeanFactory 提供了配置框架和基本的功能,ApplicationContext在此基础上添加了更多企业应用特性,是BeanFactory的超集。
Spring 有很多对象,其中 IoC 容器管理的实例化的对象称之为 bean。
二、容器总览org.springframework.context.ApplicationContext接口相当于 Spring IoC 容器,负责实例化,配置及组装 bean 对象。
配置元数据可以是 XML、Java 注解或者 Java 代码。
如下图,概括的展示了 Spring 的作用过程:
配置元数据用于描述 bean 及其依赖的关系,IoC 容器基于此来实例化,配置及组装 bean 对象。配置元数据和IoC 容器是相互独立,彼此不耦合的,它可以有多种形式,包括基于 XML配置、基于注解配置及基于Java配置等。
三、Bean 总览容器内部 bean 通过BeanDefinition 定义。
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)requestSpring web 应用,对应每次 HTTP 请求生命周期,不同请求之间是隔离的。
d)sessionSpring web 应用,对应每次 HTTP Session 生命周期,不同 Session 之间是隔离的。
e)applicationSpring web 应用,对应ServletContext 生命周期,不同 ServletContext 之间是隔离的。
f)websocketSpring web 应用,对应WebSocket Session 生命周期。
五、自定义 Bean 特性1、生命周期回调可以通过实现SpringInitializingBean和DisposableBean接口来和容器 bean 生命周期管理过程进行交互。
bean 初始化:InitializingBean() ->afterPropertiesSet() 调用。bean 销毁:DisposableBean() -> destroy() 调用。同注解应用:@PostConstruct、@PreDestroy
我们也可以通过实现 BeanPostProcessor 来处理任何回调接口。
除了初始化和销毁回调,Spring 管理的对象也可以通过实现Lifecycle接口来参与启动及关闭过程回调。
a)Initialization Callbacksorg.springframework.beans.factory.InitializingBean接口可以让 bean 对象在容器设置完所有必要的属性后执行初始化操作。他只有一个方法:
void afterPropertiesSet() throws Exception;
通常我们不建议使用此接口,因为它会使得我们的代码和 Spring 代码产生耦合。推荐使用@PostConstruct注解或者声明一个 POLO 初始化方法。
初始化方法声明如下:
// 或者@Bean(initMethod = "")
如果存在多种声明机制,则执行顺序为:@PostConstruct -> InitializingBean.afterPropertiesSet() -> 自定义初始化方法
b)Destruction Callbacksorg.springframework.beans.factory.DisposableBean接口可以使得 bean 在容器销毁时收到一个回调,它也只有一个方法:
void destroy() throws Exception;
同样不建议使用,原因如InitializingBean,推荐使用@PreDestroy 注解或者定义销毁方法。如下:
或者@Bean(destroyMethod = "")
如果存在多种声明机制,则执行顺序为:@PreDestroy -> DisposableBean.destroy() -> 自定义销毁方法
c)Startup and Shutdown CallbacksLifecycle接口定义如下:
public interface Lifecycle { void start(); void stop(); boolean isRunning();}
ApplicationContext 容器在接收到启动或者关闭信号之后,会顺序的把这一消息传递给所有容器内 Spring 管理的实现了Lifecycle接口的对象。
这一过程会委托代理给 LifecycleProcessor进行处理,其定义如下:
public interface LifecycleProcessor extends Lifecycle { void onRefresh(); void onClose();}
LifecycleProcessor是对Lifecycle接口的拓展,引入了处理 context 刷新及关闭的两个方法。
对于存在依赖关系的不同对象,启动及关闭的相应调用顺序就要遵循一定的规则。
如果是直接依赖:依赖方要先于被依赖方启动,并后于被依赖方关闭。
对于非直接依赖关系,如只知道一类类型的对象需要依赖另一类类型的对象,以上的接口将无法满足使用。因此这里需要引入另外一个接口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和BeanNameAwarea)ApplicationContextAware
对象可以通过实现org.springframework.context.ApplicationContextAware接口来获取 ApplicationContext 资源。接口定义如下:
public interface ApplicationContextAware { void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}
藉由此,ApplicationContext 创建的 bean 也可以反过来对它执行特定的操作。例如,对其它 bean 的访问。
这种机制虽然在某些场景会很有用,但是它引发了代码的耦合,破坏了 IoC 机制,因此并不推荐。
另外一种获取ApplicationContext资源的方法是通过自动装配的方式引入ApplicationContext 对象依赖,如构造函数或者 setter。 推荐使用@Autowired 注解,更加灵活方便。
b)BeanNameAware接口定义如下:
public interface BeanNameAware { void setBeanName(String name) throws BeansException;}
其方法调用时机为:bean 属性组装完毕,初始化方法(InitializingBean.afterPropertiesSet() 或自定义初始化方法)调用之前。
3、其它 Aware 资源接口ApplicationEventPublisherAware、BeanClassLoaderAware、BeanFactoryAware、LoadTimeWeaverAware、MessageSourceAware、NotificationPublisherAware、ResourceLoaderAware、ServletConfigAware、ServletContextAware。
六、容器扩展1、BeanPostProcessor 自定义 bean 特性BeanPostProcessor接口通过提供回调方法来实现用户自定义逻辑。可以根据需要配置多个不同的实现,调用顺序通过Ordered设置。
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() 或自定义初始化方法)前后需要执行的逻辑。
ApplicationContext能够自动检测实现了 BeanPostProcessor 接口的 bean,并把它们注册为前置处理器,以便之后在其它 bean 创建的时候调用。
需要注意的是:如果是使用 @Bean 注解工厂方法。那么返回的结果需要是实现了BeanPostProcessor 接口的类对象类型。否则ApplicationContext在创建它前无法自动检测其类型。
附:编程方式注册BeanPostProcessor实例(不推荐):
可以通过addBeanPostProcessor方法向ConfigurableBeanFactory注册。通过此方式注册实例 Ordered接口作用将失效,会按照注册的顺序执行,并且优先于所有自动检测注册的前置处理器。
应用实例:AutowiredAnnotationBeanPostProcessor
2、BeanFactoryPostProcessor 自定义 bean 定义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 Mapf)默认装配行为movieCatalogs; @Autowired public void setMovieCatalogs(Map movieCatalogs) { this.movieCatalogs = movieCatalogs; } // ...}
对于方法及属性字段的注解默认行为为必须。可以通过配置变更:
public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required = false) public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ...}也可以通过java.util.Optional(>= Java 8) 或者@Nullable(Spring Framework 5.0)来标示可选。
public class SimpleMovieLister { @Autowired public void setMovieFinder(Optionalg)装配使用特定已知框架资源movieFinder) { ... }}或者public class SimpleMovieLister { @Autowired public void setMovieFinder(@Nullable MovieFinder movieFinder) { ... }}
例如 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来标示使用哪个对象。如下:
@Configurationpublic 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、@ResourceJSR-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 { }
使用:
@Componentpublic class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; }}八、基于 Java 代码的容器配置1、核心概念
基于 @Configuration注解的类 和 基于@Bean注解的方法。
@Bean注解作用于方法,用于实例化,配置及初始化一个新对象,并且新对象将由 Spring 容器管理。其和基于 XML 配置的
@Bean通常和@Configuration结合使用(也可以用在任何Spring@Component注解管理的对象内)。
@Configuration注解的类主要用作 bean 定义。包括相关依赖 bean 的定义。简单实例如下:
@Configurationpublic class AppConfig { @Bean public MyServiceImpl myService() { return new MyServiceImpl(); }}
等同于:
2、AnnotationConfigApplicationContext
ApplicationContext的一种实现,Spring 3.0 引入。可以同时处理 @Configuration、@Component及 JSR-330 注解的类。
@Configuration 注解的类:包括其内所有@Bean注解的方法都被注册为 bean 定义。
对于@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 应用。包括注册 SpringContextLoaderListenerservlet listener、Spring MVCDispatcherServlet 等等。
3、@Bean 注解a)bean 定义注解方法,用于 bean 定义注册。方法名称默认为 bean 名称。返回值类型为 bean 对象类型(具体实现类或者对应接口类型)。
示例见 1、基础概念处。
也可以通过接口默认方法定义:
public interface BaseConfig { @Bean default TransferServiceImpl transferService() { return new TransferServiceImpl(); }}@Configurationpublic class AppConfig implements BaseConfig {}b)bean 依赖
bean 定义方法可以有任意多个参数,通过如下方式定义依赖:
@Configurationpublic 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 }}@Configurationpublic class AppConfig { @Bean(initMethod = "init") public BeanOne beanOne() { return new BeanOne(); } @Bean(destroyMethod = "cleanup") public BeanTwo beanTwo() { return new BeanTwo(); }}c)bean 作用域定义
@Configurationpublic class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... }}
scope-procy:
@Configurationpublic class MyConfiguration { @Bean @Scope(proxyMode = ScopedProxyMode.INTERFACES) public Encryptor encryptor() { // ... }}d)自定义 bean 名称
定义单个名称或者多个:
@Configurationpublic class AppConfig { @Bean("myThing") public Thing thing() { return new Thing(); }}//@Configurationpublic class AppConfig { @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"}) public DataSource dataSource() { // instantiate, configure and return DataSource bean... }}4、@Configuration 注解
如下:通过方法调用注入依赖
@Configurationpublic 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;}@Beanpublic 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 注解
用于引入配置类,如下:
@Configurationpublic 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 可以同时处理其它类型对象。
当需要精细处理对象依赖引入时,可以使用此注解,避免大包扫描。
九、环境抽象Environment接口为容器环境资源抽象,主要包括:profiles和properties两方面。
profile:一种命名的逻辑组。可以设置 bean 定义率属于哪个特定 profile,从而在相应 profile 被激活时进行注册。profile 可以设置当前活跃及默认活跃。
Properties:资源服务接口,提供获取及配置相应资源能力。 源包括:properties 文件、JVM system properties、system environment variables、JNDI、servlet context parameters、ad-hocPropertiesobjects、Mapobjects等。
1、Bean Definition Profiles提供机制实现容器不同环境注册不同的 bean。藉由此,我们可以实现一些类似如下场景功能:
测试环境使用基于内存的数据源,QA 及 生产环境使用 JNDI 数据源。
只在线上环境启动监控功能。
针对不同用户注册不同的功能 bean 对象。
@Profile 注解
如下: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 抽象
SpringEnvironment抽象提供属性查询操作(基于可配置的,层级的属性源),如下:
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 资源需求)。
搜索操作是层级执行的,默认情况下,系统属性优先于环境变量,如果同一个属性在两个地方都有设置,则系统属性优先返回。其它的都会被忽略。
StandardServletEnvironment的层次结构如下:
ServletConfig parameters (if applicable — for example, in case of aDispatcherServletcontext)
ServletContext parameters (web.xml context-param entries)
JNDI environment variables (java:comp/env/entries)
JVM system properties (-Dcommand-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加载时会自动查找容器内定义的MessageSourcebean,且 bean 的名称必须为messageSource。
当执行消息查找获取操作时,Spring 会将操作代理给命名为 messageSource 的 bean。如果不存在此 bean,则从父类中查找,如果找不到则实例化一个空的DelegatingMessageSource用以执行相应的方法操作。
Spring 提供了三种MessageSource接口实现:ResourceBundleMessageSource、ReloadableResourceBundleMessageSource及StaticMessageSource。它们也都实现了HierarchicalMessageSource接口用以执行嵌套消息查询。StaticMessageSource很少使用,主要用于提供编码方式添加消息。
ResourceBundleMessageSource 使用如下:
定义:
@Beanpublic 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@Asyncpublic 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、应用启动追踪
ApplicationContext负责管理 Spring 应用的生命周期。应用启动追踪主要用于测量不同启动步骤时间花费。
AbstractApplicationContext通过ApplicationStartup接口,收集各个启动阶段StartupStep数据:
application context lifecycle (base packages scanning, config classes management)
beans lifecycle (instantiation, smart initialization, post processing)
application events processing
AnnotationConfigApplicationContext应用示例:
// create a startup step and start recordingStartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");// add tagging information to the current stepscanPackages.tag("packages", () -> Arrays.toString(basePackages));// perform the actual phase we"re instrumentingthis.scanner.scan(basePackages);// end the current stepscanPackages.end();
可以通过实现ApplicationStartup接口,自定义启动追踪类。如下:
.setApplicationStartup(new MyApplicationStartup())
标签:
相关阅读
-
世界新动态:I-o-C 一篇概览
一、ioC容器和Bean介绍IoC(InversionofControl& 160;)也被称之为... -
港股汽车股开盘走强 小鹏汽车涨近4%|全...
南方财经5月5日电,港股汽车股开盘走强。截至发稿,小鹏汽车-W(0986... -
鸭子喜欢吃什么水果?-焦点消息
小鸭子可以吃些苹果碎和梨碎。如果是刚出生的小鸭子尽量不要喂它吃... -
全球实时:北大人民医院张小明:血管外...
北大人民医院张小明:血管外科医生应胆大心细勇于突破“手术禁区”-... -
中国区营收下滑 库克:多亏印度拯救了...
快科技5月5日讯,苹果今日发布截止4月1日的2023财年第二财季财报。... -
当前快播:弘扬五四精神 3名四川残疾青...
5月4日,来自四川残疾人励志报告团、新时代文明实践青年宣讲分团的3...
精彩放送
-
美容仪有了“智慧大脑”
以人工智能为核心的第四次科技革命浪潮正席卷各行业,探究AI、前沿科... -
好客柏塘!“五一”我们有备而来!让您...
千年茶香、好客柏塘,变流量为留量志愿者的服务很热情,现场工作人... -
世界新动态:I-o-C 一篇概览
一、ioC容器和Bean介绍IoC(InversionofControl& 160;)也被称之为... -
锡价 辗转反弹逻辑仍在
受缅甸佤邦暂停一切矿产资源开采的消息影响,锡期货于4月17日涨停,... -
低库存叠加弱美元 铜价支撑加强-每日快播
美元弱势将持续,美联储在银行业动荡之际呵护金融市场稳定,或从6月... -
图灵波浪5.5-黄金高位震荡、短期或继续...
图灵波浪55黄金高位震荡短期或继续回落休整黄金黄金昨日从高位回撤5... -
吕万亮受邀出席清华大学航天航空学院奖...
4月28日上午,感恩奋进清华大学航天航空学院奖学金颁奖暨捐赠答谢会... -
张文辉:黄金修正 原油反弹-天天新消息
黄金昨日探高回落日线收盘十字小阳K线早盘开盘急涨高开上涨至近期新... -
马斯克再削减 Twitter 员工福利:未兑...
甚至还要从事更多工作。知情人士表示,由于人手短缺,加之马斯克经... -
《王国之泪》新情报介绍:天空岛上的迷...
《塞尔达传说:王国之泪》官方分享了游戏的全新情报:“在天空岛上... -
易维哲:黄金等待非农,原油暂时震荡 ...
黄金上个交易日走出冲高之后的调整日线报收一根带长上影线的中等实... -
王杨:大非农来袭,黄金早盘2035支撑多!
黄金昨日回踩后继续收回涨幅虽然没有继续新高但也看出多头非常强势... -
【世界新要闻】红枣 重心将稳步抬升
目前来看,一方面,从红枣企业的物理库存与仓单数据来看,红枣产业... -
多股被实施退市风险警示 ST板块40多只...
证券时报e公司讯,多家公司发布2022年年报后被实施退市风险警示和其... -
港股汽车股开盘走强 小鹏汽车涨近4%|全...
南方财经5月5日电,港股汽车股开盘走强。截至发稿,小鹏汽车-W(0986... -
鸭子喜欢吃什么水果?-焦点消息
小鸭子可以吃些苹果碎和梨碎。如果是刚出生的小鸭子尽量不要喂它吃... -
前行者一共多少集?前行者陈亨礼真实身...
前行者一共多少集?《前行者》一共四十集,主要讲述了上世纪三十年代... -
晨会聚焦
晨会聚焦 -
上海车展,国产电车令人忧心的四个事实...
硬实力上的差距决定短跑与长跑选手在不同阶段的表现会不一样。 -
斛珠夫人海市最后到底跟谁在一起?方海...
斛珠夫人海市最后到底跟谁在一起?小说《斛珠夫人》海市最后没有和任... -
全民清洁消毒意识提升 稳健医疗“55护...
在5月5日世界手卫生日到来之际,大健康企业稳健医疗打造了55护手节... -
金汇得手:黄金日线十字星 非农高空...
美元指数依旧没有跑出区间日内关注10081015区间上破看102下破看1004... -
天天热头条丨生猪消费表现仍偏弱 玉米...
研报正文【生猪】周五生猪期现货震荡上涨,主力LH2307合约暂报16545... -
全球实时:北大人民医院张小明:血管外...
北大人民医院张小明:血管外科医生应胆大心细勇于突破“手术禁区”-... -
全球播报:头狼:黄金2048现价多,非农之...
非农准备破新高最近因为美国银行频频出事情大部分银行股价都是暴跌... -
综治微视角②石冲口镇:探索“3+3+3+1”...
编者按:娄底市各级综治中心紧紧围绕市委、市政府和市委政法委中心... -
【老夏论金】黄金2034多如期大涨,今天...
黄金昨天黄金2034做多如期大涨2060美元附近一单横扫2600点再次大赚... -
环球微速讯:暴雨蓝色预警:湖南江西等9...
中央气象台5月5日06时继续发布暴雨蓝色预警:预计,5月5日08时至6日... -
荣获全国五一劳动奖章 这些外卖骑手走...
“送外卖7年多,只想送好每一单。这次能到北京领奖,其实是全社会对... -
美股集体收跌 银行股走低,西太平洋合...
中新经纬5月5日电美东时间周四,美股三大指数低开低走。道指收跌286... -
樱花草原唱是哪两个人?樱花草歌词是什么?
樱花草原唱是哪两个人?《樱花草》是Sweety唱的,由李天龙作词作曲,... -
听说棉花要减产(^w^)
五一假期结束了,很多朋友被原油暴跌打了个措手不及,特别是和原油... -
环球头条:油脂节后需求放缓 豆粕短期...
研报正文【油脂】观点:整体油脂外盘较节前变化不大,但宏观方面原... -
小兰花和赤地女子的关系是什么?苍兰诀...
小兰花和赤地女子的关系是什么?1、《苍兰诀》中小兰花和赤地女子并... -
全球快看点丨我国快递业务量增速逐月向...
业务量增速逐月向好,已超300亿件——快递业整体企稳回升本报记者吉... -
微头条丨国内库存仍处低位 预计沪铜短...
研报正文行情走势:4月28日,沪铜(66540,-450 00,-0 67%)主力震荡... -
董镇元:横盘区间待非农 金银倒锤依旧多
今日上午9001100本人在中金路演希望平时关注并支持我的投资者能积极... -
张良点金:隔黄金多单获利持有中继,原...
隔黄金多单获利持有中继原油镑日日内操作思路如下看大做小具体而言... -
世界快看:公安部:“五一”假期刑事警...
中新网北京5月4日电(记者郭超凯)记者3日从公安部获悉,“五一”假期... -
ProCap保险公司创新商业模式堪称保险业界首创
在2023年4月29日,ProCap保险公司将在伦敦掀起保险业风暴。在公司初... -
印尼允许2024年之前运输铜精矿
据外媒报道,印尼矿业部长ArifinTasrif4月28日表示,尽管从今年6月... -
乡村爱心食堂带给老人家门口的“幸福味”
5月4日,在东白湖镇湖山村爱心食堂,党员志愿者为老人们打餐。近日... -
中国区营收下滑 库克:多亏印度拯救了...
快科技5月5日讯,苹果今日发布截止4月1日的2023财年第二财季财报。... -
当前快播:弘扬五四精神 3名四川残疾青...
5月4日,来自四川残疾人励志报告团、新时代文明实践青年宣讲分团的3... -
“五一”游客量同比增长3125.7% 三峡水...
“五一”游客量同比增长3125 7%三峡水上游强劲复苏---5月4日来自长... -
回来的女儿讲的什么故事?回来的女儿萌...
回来的女儿讲的什么故事?1、《回来的女儿》讲的孤女是陈佑希为寻找... -
快资讯丨“五一”北京新房进入第二波流量期
相较于二手房市场的降温,北京新房市场“人气”尚有余热。“五一”... -
覆流年一共多少集?覆流年第三世嫁给了谁?
覆流年一共多少集?《覆流年》一共三十集,该剧定档于8月31日在芒果T... -
二十不惑2官配都有谁?二十不惑2皓史成...
二十不惑2官配都有谁?1、《二十不惑2》中四位女孩各自都有官配,梁... -
人世间中周秉昆几次入狱?人世间周秉昆...
人世间中周秉昆几次入狱?1、《人世间》周秉昆2次入狱。第一次入狱是... -
一张地图如何挽救无数人生命|天天快播
《致命地图:席卷全球的重大传染病及流行病》桑德拉·亨佩尔著吴勐... -
深圳公司法人变更_公司法人变更后果严重...
1、法定代表人是公司的实际执行人,依据民法通则、合同法等相关规定... -
俄克里姆林宫遭无人机袭击 俄方称美国...
每经AI快讯,俄罗斯总统新闻秘书佩斯科夫4日表示,美国是无人机袭击... -
曲阜市时庄街道开展“扬青春风采 享文...
曲阜市时庄街道开展“扬青春风采享文化盛宴”主题活动大众网记者朱... -
当前热文:篮球造犯规有哪些(篮球造犯规)
1、主要就是造对方带球撞人概念篮球比赛中的一种犯规动作,是持球队... -
AMD史上最强核显性能离谱!轻松超越GTX...
AMD史上最强核显性能离谱!轻松超越GTX1650 -
当前时讯:河北香河:微度假 慢生活
为推进文化和旅游深度融合发展,丰富活跃文化和旅游市场,近日,香... -
GT STEEL GROUP(08402.HK):要约人持...
格隆汇5月4日丨GTSTEELGROUP08402HK公告金利丰证券为及代表要约人作... -
玻璃脏了用什么方法清洗最干净_擦洗玻璃...
欢迎观看本篇文章,小勉来为大家解答以上问题。玻璃脏了用什么方法... -
AI时代,如何训练自己提问的能力?|环球快报
「配图摄影by辉友华昕」匿名用户@辉哥奇谭知识星球,提问:辉哥好,... -
环球微资讯!雪人股份(002639):5月4...
5月4日北向资金增持9 92万股雪人股份。近5个交易日中,获北向资金... -
阿里概念股票概念一览(2023/5/4)
阿里概念股票概念一览(2023 5 4),阿里概念股票概念一览(2023 ... -
天天热文:古蒂:无论梅西回不回巴萨,...
直播吧5月4日讯皇马名宿古蒂接受采访时谈到了梅西的情况,他个人并... -
环球微动态丨歌唱家方琼生日_歌唱家方琼
1、歌唱家方琼,湖南岳阳人,毕业于上海音乐学院声乐系。2、中国内... -
全球热点!江西省景德镇市2023-05-05 0...
一、江西省景德镇市天气预报1、乐平市气象台2023年05月05日01时56分... -
5月4日基金净值:嘉实优化红利混合A最新...
5月4日,嘉实优化红利混合A最新单位净值为1 551元,累计净值为3 4... -
环球今头条!严守一手机铃声mp3_手机铃声mp3
1、可以自已做!用酷狗就行!也可以用mpTrim。本文就为大家分享到这... -
我的世界NULL_我的世界null 今日视点
1、常和黑暗在一起的人,不需要对黑暗辩解。2、你内心的恐惧便是我... -
天天简讯:激斗战士_关于激斗战士介绍
1、打斗游戏,故事描写AMX研究所因某次意外,导致3个在研究中的战略... -
这朵电力“小花”,用青春守护万家灯火
新华社郑州5月4日电(记者翟濯刘振坤)晚上11时,夜色中,两道光束... -
环球简讯:长安,开封为何现在不在繁荣?
唐朝时期,长安是当时世界最繁荣的城市,北宋时期,开封是当时世界... -
北京特色的小吃_北京特色小吃排行榜
你们好,最近小未来发现有诸多的小伙伴们对于北京特色的小吃,北京... -
天天快讯:秦氏金升:5.4金价阶梯型回调...
黄金下午给出分析是2030做多看2048的阻力行情反弹最该204524多单布... -
焦点快看:深圳瑞捷:公司确有中标浙江...
同花顺(300033)金融研究中心5月4日讯,有投资者向深圳瑞捷(30097... -
天天快报!盈趣科技:公司与亚马逊目前暂...
有投资者在投资者互动平台提问:贵司目前和亚马逊合作情况如何?是... -
友人称巴厘岛身亡两人已恋爱多年是什么情况
友人称巴厘岛身亡两人已恋爱多年今天的热度非常高,现在也是在热搜... -
世界热点!各地广泛开展“学习二十大 ...
新华社北京5月4日电(记者黄玥、冯家顺)记者4日从共青团中央获悉,... -
【五四特别致敬·我还是从前那个少年】...
五四青年节,青春不打烊。他是中国载人航天工程首任总设计师。青年... -
“五一”档15.19亿元票房位列影史第三,...
今年五一,还有谁没看电影? -
粉笔发布22年全线业务业绩向好,实现扭...
港股研究社消息,4月25日,粉笔有限公司对外发布了2022年度报告。业... -
天天日报丨冯哥论金:黄金双多获利,继...
冯哥论金黄金双多获利继续看涨一线我们昨日全网公开给的2014做空最... -
美国劳工部:3家麦当劳特许经营商非法雇...
新华社华盛顿5月3日电(记者孙丁)美国劳工部日前宣布,该部门一项... -
云展云销全年GMV超11亿美元,世界商品网...
2020年世界商品网以让企业不出国门就能轻松开拓全球市场为宗旨,推... -
环球时讯:「节日我在岗」相隔一千多公...
“五一”假期一道道忙碌的身影一幅幅感动的画面在许多不经意的地方... -
头狼:黄金2033多,在2041收割离场,同...
多空切换随意收割下午狼哥在2031一线公开干多同时2033一线也是加仓... -
外汇百晓生:黄金1945空,原油68多
外汇百晓生黄金1945空原油68多五一假期游玩还是很快乐的平时除了投... -
深蓝SL03试驾体验,颜值既正义还是金玉...
PS:试驾时忘记开启体验,所以不确定是个例还是 下图就是卖出... -
面神经麻痹治疗指南_面神经麻痹是怎么回...
1、面瘫的原因有以下几点:1。非特异性炎症感染:发病前有感冒、风... -
浙江大学“组团式”医疗援疆见成效:50...
援疆专家、浙江大学医学院附属第二医院神经外科主任医师徐锦芳,从... -
图灵波浪5.4晚-黄金快速上冲、涨势接踵而来
图灵波浪54晚黄金快速上冲涨势接踵而来黄金黄金受数据影响短期波动... -
windows未激活影响使用吗 windows未激...
今天来聊聊关于windows未激活影响使用吗,windows未激活怎么解决的... -
世界聚焦:方萍萍:多头主力开始进仓黄...
通过对黄金小时图解析得知行情从高位冲高回落以来如期在下方2030一... -
易极:5.4原油下午盘思路追踪,切勿追单!
原油4小时结构图原油4小时在收出长下影之后亚盘和欧盘的早盘走出震... -
大田环球贵金属:现货黄金操作建议2023-05-04
现货黄金市场行情现状简述黄金价格本周已经大幅走高在美联储利率决... -
当前快报:4月中国百城房价小幅降温 环...
中新社北京5月4日电(记者庞无忌)在经过一季度的“小阳春”后,4月份... -
失之交臂的反义词是什么_失之交臂的反义...
想必现在有很多小伙伴对于失之交臂的反义词是什么方面的知识都比较... -
第八届玛丽莱杯足球精英赛贵州预选赛结...
5月3日下午,2023第八届“玛丽莱杯”青少年(U12)足球精英赛贵州预... -
五四青年节:青春正当时 奋进不停歇 ...
新华社北京5月4日电题:五四青年节:青春正当时奋进不停歇新华社记... -
Honor Pad V8首次亮相 配备12.1英寸...
荣耀正式宣布在市场推出一款名为HonorPadV8的新型中端平板电脑。该... -
环球速看:最高法新规:变相羁押赔偿等...
中国最高法发布新规“变相羁押赔偿”等被列入司法赔偿案件案由中新...