草庐IT

Spring IOC官方文档学习笔记(十四)之ApplicationContext的其他功能

shame11 2023-03-28 原文

1.使用MessageSource

(1) 有时,我们的项目可能会面临国际化需求,例如:对不同国家的人,我们需返回不同语言的消息,而java本身已经给我们提供了ResourceBundle类实现国际化的需求,如下

//在resources目录下,新建两个配置文件,分别为message_en_us.properties和message_zh_cn.properties,内容如下

//message_en_us.properties文件中配置如下
country=us

//message_zh_cn.properties文件中配置如下,注意对中文使用unicode编码
country=\u4e2d\u56fd

//现在,我们希望我们的项目在不同的国家返回不同的country信息,那么就可以使用ResourceBundle类了,如下
public static void main(String[] args) {
    //使用ResourceBundle加载的文件都必须放置在resources根目录下,因此我们的message_en_us.properties和message_zh_cn.properties文件都位于resources根目录,而且这些文件都必须按照${name}_${language}_${region}的方式来命名,因为这种命名方式正好能对应ResourceBundle.getBundle()方法中的参数,例如ResourceBundle.getBundle("message", new Locale("zh", "cn")),其中,message对应${name},zh对应${language},cn对应${region},即ResourceBundle.getBundle("message", new Locale("zh", "cn"))这个方法会读取我们的message_zh_cn.properties配置文件,这样我们就可以根据不同的参数来读取不同的文件,达到国际化的目的

    //未指定它的Locale,因此java获取它当前所在的地区,为cn
    ResourceBundle DefaultBundle = ResourceBundle.getBundle("message");
    System.out.println(DefaultBundle.getString("country"));

    //指定地区为cn
    ResourceBundle cnBundle = ResourceBundle.getBundle("message", new Locale("zh","cn"));
    System.out.println(cnBundle.getString("country"));

    //指定地区为us
    ResourceBundle uSbundle = ResourceBundle.getBundle("message", new Locale("en","us"));
    System.out.println(uSbundle.getString("country"));
}

//打印结果如下,通过ResourceBundle实现了国际化
中国
中国
us

(2) Spring提供了MessageSource来帮助我们实现国际化功能,具体的使用方法同jdk中的ResourceBundle,如下

//在resources目录下,新建两个配置文件,分别为message_en.properties和message_zh.properties,内容如下

//message_en.properties文件中配置如下
country=us

//message_zh.properties文件中配置如下
country=中国

<!-- 配置xml -->
<beans ....>
        <!-- 使用ReloadableResourceBundleMessageSource类,向容器中注入MessageSource用于国际化功能,注意:这个bean的名称必须为messageSource -->
        <bean id="messageSource"
              class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <!-- 指定消息源为message,那么spring会去寻找名称中包含message的配置文件 -->
                    <value>message</value>
                </list>
            </property>
            <!-- 避免中文乱码 -->
            <property name="defaultEncoding">
                <value>UTF-8</value>
            </property>
        </bean>
</beans>

//ApplicationContext继承了MessageSource接口
MessageSource messageSource = new ClassPathXmlApplicationContext("beans.xml");
//指定不同的语言,来获取不同消息
String zh = messageSource.getMessage("country", null, Locale.CHINESE);
System.out.println(zh);

String en = messageSource.getMessage("country", null, Locale.ENGLISH);
System.out.println(en);

//启动容器,输出如下
中国
us

(3) Spring的MessageSource还提供了占位符功能,来进行消息内容的填充,如下例所示

//向message_zh.properties中添加配置项如下,{0}表示第一个占位符,还有{1},{2}等等,以此类推
argument=we need {0}

//main函数
MessageSource messageSource = new ClassPathXmlApplicationContext("beans.xml");
                                                     //Object[]指定向占位符填充的内容
String argument = messageSource.getMessage("argument", new Object[]{"蛋糕"}, Locale.CHINESE);
System.out.println(argument);

//启动后,打印如下
we need 蛋糕

2.标准和自定义事件

(1) Spring中的事件是通过ApplicationEvent类和ApplicationListener接口提供的,如果一个bean实现了ApplicationListener接口,那么每当一个ApplicationEvent发布到Spring中时,都会通知该bean

(2) Spring中内置事件

事件 说明
ContextRefreshedEvent 容器初始化或刷新时(refresh)时发布该事件
ContextStartedEvent 通过调用ConfigurationApplicationContext接口中的start()方法启动容器时发布该事件
ContextStoppedEvent 通过调用ConfigurationApplicationContext接口中的stop()方法停止容器时发布该事件
ContextClosedEvent 通过调用ConfigurationApplicationContext接口中的close()方法或jvm关闭钩子关闭容器时发布该事件
RequestHandledEvent 适用于使用了DispatcherServlet的web环境中,在请求完成后发布该事件,用于告知所有的bean已经为http请求提供了服务
ServletRequestHandledEvent RequestHandledEvent的子类,其中添加了Servlet特定信息

(3) 示例如下

//现在假设有一个用户注册事件,每当一个用户注册后,进行相应的其他操作(如发送邮件等等)
//自定义事件,需继承ApplicationEvent
public class RegisterEvent extends ApplicationEvent {
    private String username;

    public RegisterEvent(Object source,String username) {
        super(source);
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

//使用ApplicationEventPublisher中的publishEvent()方法来向容器中发布一个事件
@Service
public class RegisterService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    //publishEvent()方法会阻塞,直到所有的监听器都完成了对事件的处理
    public void finishRegister(String username) {
        this.publisher.publishEvent(new RegisterEvent(this, username));
    }
}

//实现ApplicationListener接口,实现某种类型事件的监听者
@Component
public class RegisterLister implements ApplicationListener<RegisterEvent> {

    //每当有一个RegisterEvent事件发布后,都会触发该回调
    @Override
    public void onApplicationEvent(RegisterEvent registerEvent) {
        System.out.println("用户:" + registerEvent.getUsername() + "完成注册...");
        //do other things,such as send emails
    }
}


////除了上面实现ApplicationListener接口外,我们还可以使用@EventListener注解来定义监听器,如下
//@Component
//public class RegisterLister {
//    //每当有一个RegisterEvent事件时,都会回调该方法,跟上面的例子等价,方法签名指定了监听事件的类型
//    @EventListener
//    public void handleRegister(RegisterEvent registerEvent) {
//        System.out.println("用户:" + registerEvent.getUsername() + "完成注册...");
//    }
//}

//main
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext("cn.example.spring");
String username ="zpc";
ctx.getBean(RegisterService.class).finishRegister(username);

//启动后,容器打印如下,其中Spring使用了观察者模式,来实现了一个事件发布与订阅的功能
用户:zpc完成注册...

(4) Spring还支持在处理完一个事件后发布一个新事件,如下

//每处理完RegisterEvent事件后都会发布一个新事件FinishRegisterEvent,如果需要发布多个事件,可以返回一个Collection事件集合
@EventListener
public FinishRegisterEvent handleRegister(RegisterEvent registerEvent) {
     //...
}

(5) 使用@Async注解,可使监听器异步处理该事件,如下

//注意:异步事件所抛出的异常会被AsyncUncaughtExceptionHandler捕获处理,不会传播到调用方,同时异步事件不支持像上面的例子那样通过方法返回值来在处理完一个事件后发布一个新事件
@EventListener
@Async
public void handleRegister(RegisterEvent registerEvent) {
  //在另一个线程中进行处理
}

(6) 使用@Order注解,可以指定不同监听器的执行顺序

@EventListener
@Order(42)
public void handleRegister(RegisterEvent registerEvent) {

}

3.使用Resource来便捷的访问资源

(1) Spring提供了Resource,ResourceLoader相关接口,来从类路径,文件系统中或URL中等来获取资源

有关Spring IOC官方文档学习笔记(十四)之ApplicationContext的其他功能的更多相关文章

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  4. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  5. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  6. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  9. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  10. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

随机推荐