您的位置:首页 >资讯 > 科技 >

世界新动态:I-o-C 一篇概览

2023-05-05 10:31:17    来源:博客园
一、ioC 容器和 Bean介绍

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)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、生命周期回调

可以通过实现SpringInitializingBean和DisposableBean接口来和容器 bean 生命周期管理过程进行交互。

bean 初始化:InitializingBean() ->afterPropertiesSet() 调用。bean 销毁:DisposableBean() -> destroy() 调用。

同注解应用:@PostConstruct、@PreDestroy

我们也可以通过实现 BeanPostProcessor 来处理任何回调接口。

除了初始化和销毁回调,Spring 管理的对象也可以通过实现Lifecycle接口来参与启动及关闭过程回调。

a)Initialization Callbacks

org.springframework.beans.factory.InitializingBean接口可以让 bean 对象在容器设置完所有必要的属性后执行初始化操作。他只有一个方法:

void afterPropertiesSet() throws Exception;

通常我们不建议使用此接口,因为它会使得我们的代码和 Spring 代码产生耦合。推荐使用@PostConstruct注解或者声明一个 POLO 初始化方法。

初始化方法声明如下:

// 或者@Bean(initMethod = "")

如果存在多种声明机制,则执行顺序为:@PostConstruct -> InitializingBean.afterPropertiesSet() -> 自定义初始化方法

b)Destruction Callbacks

org.springframework.beans.factory.DisposableBean接口可以使得 bean 在容器销毁时收到一个回调,它也只有一个方法:

void destroy() throws Exception;

同样不建议使用,原因如InitializingBean,推荐使用@PreDestroy 注解或者定义销毁方法。如下:

或者@Bean(destroyMethod = "")

如果存在多种声明机制,则执行顺序为:@PreDestroy -> DisposableBean.destroy() -> 自定义销毁方法

c)Startup and Shutdown Callbacks

Lifecycle接口定义如下:

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 Set movieCatalogs;    @Autowired    public void setMovieCatalogs(Set movieCatalogs) {        this.movieCatalogs = movieCatalogs;    }    // ...}

如果需要列表里的元素排序,则可以对收集的对象应用 Ordered 接口,或者添加@Order 注解。

Map 类型收集:key 为 bean 名称,value 为 bean 对象。

public class MovieRecommender {    private Map movieCatalogs;    @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;    }    // ...}
也可以通过java.util.Optional(>= Java 8) 或者@Nullable(Spring Framework 5.0)来标示可选。
public class SimpleMovieLister {    @Autowired    public void setMovieFinder(Optional movieFinder) {        ...    }}或者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来标示使用哪个对象。如下:

@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、@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 { }

使用:

@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 List blockedList;    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())

标签:

相关阅读

精彩放送

深圳公司法人变更_公司法人变更后果严重_今热点

曲阜市时庄街道开展“扬青春风采 享文化盛宴”主题活动

AMD史上最强核显性能离谱!轻松超越GTX 1650 天天快看点

玻璃脏了用什么方法清洗最干净_擦洗玻璃的小妙招

天天热文:古蒂:无论梅西回不回巴萨,巴萨都无法获得欧冠冠军

5月4日基金净值:嘉实优化红利混合A最新净值1.551,跌0.96%|当前速讯

天天简讯:激斗战士_关于激斗战士介绍

环球简讯:长安,开封为何现在不在繁荣?

外交部发言人:美方应当停止利用网络武器在全球实施窃密和网络攻击|天天最资讯

强降雨天气影响南方大部地区 公安部交管局发布提示

微视频|青春答卷 今日观点

剑道独尊txt下载全文阅读_剑道独尊txt下载

全球快看:史上最穷上市公司!账户里只有178元现金,罪魁祸首是其最大股东

空气消毒机称可灭新冠病毒?广告公司因虚假宣传被罚20万

环球报道:《龙马精神》:浮光掠影的故事,无所依归的情怀

衡水市民花样过“五一” 尽享美好时光|世界报道

陈水扁保外就医期间受访疑似违规,“矫正署”5月12日前提出报告

离谱!罗云熙《长月烬明》花絮比正片好看,狂删18集是“原罪”|当前资讯

郑氏点银:黄金避险再起刷历史新高,见回踩继续保持看涨 天天速看

今晚播出!《奔跑的青春——2023五四青年节特别节目》传递闪闪发光的青春力量

今日播报!成都复耕对楼市的影响,可比想象中大的多!

全球今日报丨窗户漏水的处理方法有哪些_窗户漏水的原因和处理方法?

矿床经济评价

市场监管总局等十一部门联合印发《关于进一步加强医疗美容行业监管工作的指导意见》-当前讯息

炬华科技:截止2023年4月30日公司股东总户数27,350户

上实发展8亿元公司债提前摘牌 票面利率2.74% 世界观速讯

【世界独家】6日立夏:燕子衔春去,薰风带夏来

金信诺一季度净利润大增159% 通信领域景气度恢复 数据中心、6G迎新机遇

榴莲价格迎“腰斩”?进口国增多、旺季到来多重原因-天天视点

第133届中国进出口商品交易会:重环保拼创意 办公文具展出新“花样” 全球新消息

环球信息:头狼:黄金2031现价多,看2080上方

秦氏金升:5.4金价创历史新高,黄金行情走势分析及操作建议

哈登送恩比德手表,祝贺其获得NBA常规赛MVP

当前焦点!黄力晨:黄金出现顶部信号 警惕金价回调修正

世界热门:新郑机场到郑州东站多远 新郑机场到郑州东站

今热点:加息是长远规律结构均衡为主非舆论短视

伟星新材:公司在外部环境非常残酷、严峻的情况下,逆势实现营收和利润双增长

中文传媒(600373):经营业绩稳中有进 主业与多元产业协同赋能

假日里的劳动者

PHP-web框架Laravel-队列(一)

全球新资讯:美原油交易策略:油价探底回升,短线或陷入震荡

意媒:检察官将调查穆帅批评裁判言论,可能处以停赛处罚_世界热讯

《崩坏星穹铁道》怎么完成生存的智慧任务

春季健康食谱,营养的下饭菜,简单又好吃

中国东北粮食主产区抢抓农时忙播种 当前速递

星火成炬 | 向阳青春-世界播报

“五一”假期日均125.3万人次出入境

环球热门:24家投行前瞻非农:增速料进一步放缓,黄金有望继续上行

环球今日讯!声动中国丨这届青年是懂

爱玛矿山路店恶意涨钱坑人

全球简讯:2023云南红河州事业单位招聘考试期间市场监管工作公告

青春中国丨在最需要的地方绽放-天天速看

全球快看点丨环保行业报告:四月可转债报告

天天看点:SpaceX超重星舰首飞“失败”,问题出在哪儿?

2023年“五一”假期全国营业性客运量合计26971.6万人次-速讯

天天速看:融创中国涨超8%领涨内房股 二季度全国房地产市场有望企稳

利好金价!第一共和银行也倒了,市场等待下一个倒下的多米诺骨牌!

无界之美 | 松下冰箱携厨居一体明星新品亮相AWE2023

英镑反弹逼近1.39关口后暂受阻 反弹行情再度来到十字路口

金界传奇:短线黄金2037先顺势多!|焦点速递

恒生科技指数转跌 焦点滚动

黄金酝酿下一波爆发!黄金日内交易分析:若突破关键水平 金价有望飙升近50美元

阿布扎比港口集团斥资2.6亿美元收购8艘船|当前资讯

4月百城二手住宅价格跌幅扩大:挂牌量持续走高,房价走势承压

即时看!好听微信名字简单气质_好听微信名字简介介绍

恒泰裕集团(08081.HK)盈警后跌超10%,截至发稿,跌9.57%,报0.085港元,成交额716.66万港元 全球热闻

康奈中亚战略发展启航 舒适科技新品迭代升级

都乐携进博明星展品亮相“四叶草55生活节”,高品质果蔬,一站式嗨购

方华富:黄金测试高点,强势多头回归! 天天视讯

吉林省高级人民法院刑二庭到白城法院对涉民营企业刑事案件处理情况开展调研

墨西哥这一家航司即将重启运营_实时

生孩子担心没人带?上海市教委主任回应如何为最柔软群体提供优质照护 环球快播

中国绿地博大绿泽股东博大国际拟减持公司股权

我是农民工给雇主装门不幸腰椎压缩性骨折二分之一,动完手木医院费2万

有机豆浆粉,成长好营养|龙王为中国儿童膳食营养健康持续助力

行业唯一!法大大上榜《2023胡润全球未来独角兽》

四川新闻联播丨峡谷高山攻“难点” 只为蜀道破重山-独家焦点

世界焦点!重庆第三代试管生双胞胎费用是多少?

【环球新要闻】手提电脑怎样打开蓝牙_手提电脑蓝牙在哪里

特斯拉突然宣布涨价,价格战要结束了?

天天快资讯丨港媒曝魏大勋求婚被秦岚拒绝 两人正处于冷战状态

万达信息董秘回复:公司暂未有商业密码产品销售许可证|今日要闻

“万味齐遇”在广交 八方食品受追捧

环球播报:【劳动者之歌】向险而生 实战硝烟锻造特勤尖兵

方萍萍:2023年05月04日原油交易策略_天天最资讯

即时看!香港故宫文化博物馆成为“新地标” “五一”迎来参观热潮

焦点信息:外汇界新一:怎么办?黄金历史新高啦

广交会里的“大食物观” 食品企业坚持科技创新 天天时快讯

生活腐化堕落 广东省农村信用社联合社原党委书记王硕平被“双开” 全球即时看

甘肃省甘谷县市场监管局开展特种设备安全检查-全球讯息

知己网注销_知己网-头条焦点

国家移民管理局:“五一”假期日均125.3万人次出入境 环球时讯

每日热议!成交额44.16万亿元!4月我国期货市场运行平稳

永寿县探索“生态+”模式发展县域经济-全球新要闻

公安部:“五一”假期刑事警情同比下降5.6%,1417场大型活动安全顺利|世界滚动

“五一”演出市场精彩不断 观演消费持续增长

mt6260da(mt620怎么样)

【世界独家】5.4—秋末悔城:美联储加息之路已到头?鸽派信号再创历史!

环球新资讯:印尼,加大力度转移人口

看,这是黑土地上的春耕“科技范儿”

全球观热点:山西沉浸式体验引领新消费

“服务越来越好,村民看病更方便了”(帮扶县驻村手记)

王导:黄金2052反手空获利,反弹继续空_播资讯

我很忙歌词表达什么意思_我很忙歌词?

当前速读:2023年“五一”假期国内旅游出游2.74亿人次 同比增长70.83%

“为全球市场带来机遇和动力” 焦点热讯

全球快资讯丨山海杨阳:美联储利率决议落地,黄金避险情绪高预期!

环球今日讯!方矩管_关于方矩管介绍