Spring IoC容器(四)容器、环境配置及附加功能

 本文内容包括容器的@Bean 及 @Configuration 注解的使用、容器环境的配置文件及容器的附加功能(包括国际化消息、事件发布与监听)。

1 容器配置

在注解模式下,@Configuration 是容器核心的注解之一,可以在其注解的类中通过@Bean作用于方法上来配置Bean。xml 与 注解模式可以混合在一起使用。

1.1 @Bean

作用类似于xml 配置文件中的<bean/>标签。可以在注解内标识bean名,也可定义一些列别名。

作用对象

@Component作用于类,@Bean作用于方法。

实现原理

@Component 通常通过类路径扫描来自动探测,自动装配到Spring容器中,而@Bean则是在标有该注解的方法中创建这个Bean。

自定义性

@Bean 的自定义性更强。例如引入第三方库中的类需要装配到Spring时,只能使用@Bean来实现。

表 @Bean 与 @Component的对比

通常是在@Configuration注解的类中来使用@Bean,这种被称为“Full模式”,相反,如果没在@Configuration注解的类中使用@Bean,则被称为“Lite模式”。

1.1.1 Lite 模式

lite 模式定义如下:

1)在非@Configuration标记(例如@Component或者没有任何Spring注解,但在该类中的某个方法上使用了@Bean)的类里定义的@Bean方法。

2)@Configuration(proxyBeanMethods=false)标记的类内定义的@Bean方法。

该模式相对于Full模式有以下限制:

  1. 不会被CGLIB代理,而是使用了原始的类类型。
  2. 该类的@Bean方法相互之间不能直接调用(如果直接调用会生成新的实例)。
//@Configuration(proxyBeanMethods = false)
public class LiteConfiguration {
    @Bean
    public Service liteService1() {
        System.out.println("liteService1 实例化");
        return new Service(){};
    }

    @Bean
    public Service liteService2() {
        System.out.println("liteService2 实例化" + liteService1());
        return new Service(){};
    }
}

public class LiteTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(FullConfiguration.class,LiteConfiguration.class);
        Object fullService1 = applicationContext.getBean("full1");
        Object liteService1 = applicationContext.getBean("liteService1");
        System.out.println(fullService1);
        System.out.println(liteService1);
    }
}

图 Lite模式下,bean没有被代理

通常情况下,推荐使用Full模式。Lite模式可能更时候需要更快响应速度或资源受限时。

1.2 @Configuration

作用类似于xml配置文件中的<beans/>标签。可以在类里配置bean,及可以在类上配置bean的扫描路径。在同一个@Configuration注解的类中,@Bean标注的方法可以相互调用,且无论调用多少次,都只会生成一个单例bean(该bean的作用域要为单例)。

@Configuration
public class FullConfiguration {
    // 如果定义了别名,则默认名失效
    @Bean({"full1","full"})
    public Service fullService1() {
        System.out.println("fullService1 实例化");
        return new Service() {};
    }

    @Bean
    public Service fullService2() {
        // 直接调用了fullService1()方法,无论该方法在个类被调用多少次,都只会生成一个实例(单例)
        System.out.println("fullService2 实例化:" + fullService1());
        return new Service() {};
    }

    @Bean
    public Service fullService3() {
        // 这里直接调用了fullService1()方法
        System.out.println("fullService3 实例化:" + fullService1());
        return new Service() {};
    }
}

1.2.1 @Lookup 方法注入依赖 Look Method Injection

通过@Inject注入时,依赖只会被注入一次,即在该bean被初始化时。如果想实现每次都能获取一个新的bean,可以使用@Lookup注解。如果想动态获取一个被容器管理的Bean实例时很有用。

该注解核心思想时:使用抽象方法声明需要注入的Bean,Spring在运行时动态生成其子类并重新实现该抽象方法(cglib代理),从而实现原型Bean的注入。使用规则如下:

  1. 可以在@Lookup注解中指定bean的名字。
  2. 该方法要符合cglib 代理方法的规则(不能是private、static、final)。
  3. 如果想每次调用都生成新的bean,则对应的bean作用域不能为singleton。(适合生命周期较短的Bean)
@Component
@Scope(value = "prototype")
public class LookupService1 implements Service {
}

@Component
public class LookupService2 implements Service {
}

@Component
public class LookupManage {
    @Lookup("lookupService1")
    public Service lookupService() {
        return null;
    }
}

@Configuration
@ComponentScan(basePackages = "lookup")
public class LookupConfiguration {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(LookupConfiguration.class);
        LookupService1 lookupService1 = context.getBean(LookupService1.class);
        LookupService2 lookupService2 = context.getBean(LookupService2.class);
        System.out.println("原型 lookupService1:" + lookupService1);
        System.out.println("原型 lookupService2:" + lookupService2);
        System.out.println("--------lookup---------");
        LookupManage lookupManage = context.getBean(LookupManage.class);
        System.out.println(lookupManage.lookupService());
        System.out.println(lookupManage.lookupService());
    }
}

1.2.2 @Import 依赖导入

可以在类上使用@Import注解来导入其他@Configuration注解的类,这些被导入的类与该类就组合成了一个整体(类之间的Bean可以相互依赖)。

@Configuration
public class Configuration1 {
    @Bean
    public Service1 service1(Service2 service2) {
        System.out.println("service1() service2:");
        Service1 service1 = new Service1();
        System.out.println("service1:" + service1);
        return service1;
    }
}

@Configuration
public class Configuration2 {
    @Bean
    public Service2 service2(Service3 service3) {
        System.out.println("service2() service3:" + service3);
        Service2 service2 = new Service2();
        System.out.println("service2:" + service2);
        return service2;
    }
}

@Configuration
@Import({Configuration1.class,Configuration2.class})
public class Configuration3 {
    @Bean
    public Service3 service3() {
        Service3 service3 = new Service3();
        System.out.println("service3:" + service3);
        return service3;
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Configuration3.class);
        Service3 service3 = context.getBean(Service3.class);
        Service2 service2 = context.getBean(Service2.class);
        Service1 service1 = context.getBean(Service1.class);
        System.out.println("service1:" + service1);
        System.out.println("service2:" + service2);
        System.out.println("service3:" + service3);
    }
}

1.3 AnnotationConfigApplicationContext

用于创建和初始化基于Java配置类的容器。可以通过构造函数或register方法(参数为class类型,可以是有@Configuration、@Component注解的或者没有任何注解的类)来指定需要注册的bean。容器会将这些类注册为bean。

还可以通过scan方法来指定需要扫描的类路径。

使用了register或scan方法后,要使用其refresh方法来刷新容器。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProfilesConfiguration.class);
context.refresh();
Service bean = context.getBean(Service.class);

1.4 xml 与 注解混合使用

在代码中可以将注解与xml格式的bean配置文件混合在一起使用。可分为两种情况:1) 以XML为中心;2)以注解为中心。

1.4.1 以XML为中心

在代码中,以XML配置为主。需要使用注解形式配置的bean时,有两种方法:1)在xml中 使用 <component-scan/>标签来指定需要扫描的类路径;2)在xml中将@configuration注解的类用<bean/>标签来配置,还必须加上<context:annotation-config/>标签。

<!--xml_center.xml-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--    要让xml支持注解,必须加上这个-->
    <context:annotation-config/>
    <context:component-scan base-package="container.xml"/>
    <bean class="container.xml.XMLConfiguration"/>
    <bean class="container.xml.XMLService">
        <constructor-arg name="service1" ref="service1"/>
        <property name="service2" ref="service2"/>
    </bean>
</beans>

@Configuration
public class XMLConfiguration {

    public XMLConfiguration() {
        System.out.println("XMLConfiguration实例化");
    }
    @Bean
    public Service1 service1() {
        System.out.println("service1 实例化");
        return new Service1();
    }

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("xml_center.xml");
        XMLService xmlService = context.getBean(XMLService.class);
        System.out.println("xmlService:" + xmlService);
        Object service = context.getBean("service1");
        System.out.println("service:" + service);
        System.out.println("service2:" + xmlService.getService2());
    }
}

1.4.2 以注解为中心

在代码中,以注解为主,可以在有@Configuration注解的类上,加上@ImportResource注解,来指定xml配置文件的路径。

@Configuration
@ImportResource("classpath:annotate_center.xml")
public class AnnotateConfiguration {
    @Bean("service1")
    public Service service1(XMLService xmlService) {
        System.out.println("service1 xmlService:" + xmlService);
        return new Service() {};
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AnnotateConfiguration.class);
        XMLService xmlService = context.getBean(XMLService.class);
        Object service1 = context.getBean("service1");
        System.out.println(service1);
        System.out.println("xmlService:" + xmlService);
    }
}

2 环境配置

Environment接口是对容器环境的一个抽象,它包括两个访问:概要文件和属性。

2.1 @Profile 概要文件

@Profile是一种将容器的bean按照不同配置进行分组的机制。用于指示一个或多个组件仅在某些特定的配置下被创建和初始化。

在该注解中可以使用逻辑运算符(与 & 或 | 非 !)。

@Configuration
public class ProfilesConfiguration {

    @Bean
    @Profile("dev & other")
    public Service devService() {
        System.out.println("实例化 devService");
        return new Service() {};
    }

    @Bean
    @Profile("prod")
    public Service prodService() {
        System.out.println("实例化 prodService");
        return new Service() {};
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("other","dev");
        context.register(ProfilesConfiguration.class);
        context.refresh();
        Service bean = context.getBean(Service.class);
        System.out.println(bean);
    }
}

2.2 property 环境属性

Environment 提供了在多种环境下搜索属性的接口。不同环境属性名相同时优先级如下:1)ServletConfig (例如DispatcherServlet上下文)。2)ServletContext参数(例如web.xml)。3)JNDI环境变量。4)JVM系统变量。5)JVM系统环境。

还可以使用@PropertySource 注解来引入自定义属性。

@Configuration
@PropertySource("/pro.properties")
public class PropertyConfiguration {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(PropertyConfiguration.class);
        String name = context.getEnvironment().getProperty("my.name");
        String address = context.getEnvironment().getProperty("my.address");
        System.out.println(name + ":" + address);
    }
}

pro.properties文件:
my.name=Jon
my.address=China

3 附加功能

ApplicationContext类还实现了MessageSource(消息)、ApplicationEventPublisher(事件发布)等接口。让容器拥有了像事件发布与监听、消息国际化等附加功能。

3.1 MessageSource 消息国际化

要实现这个功能,必须注册一个ResourceBundleMessageSource类型的bean。创建该bean时,可以指定消息的资源包(properties格式的文件)及文件编码等信息。

如果要实现国际化消息,则需要创建对应地域的资源包。

message/exception.properties:
error=The argument {0} is big error

message/message_en.properties:
info=info str 
content=message content

message/message_en.properties:
info=中文内容
content=消息内容

@Configuration
public class MessageSourceConfiguration{
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setBasenames("message/message","message/exception");
        return messageSource;
    }

    public static void main(String[] args) {
        MessageSource messageSource = new AnnotationConfigApplicationContext(MessageSourceConfiguration.class);
        String info = messageSource.getMessage("info", null, Locale.ENGLISH);
        System.out.println("info:" + info);
        String error = messageSource.getMessage("error", new Object[]{"userName"}, Locale.ENGLISH);
        System.out.println("error:" + error);

        System.out.println(messageSource.getMessage("info", null, Locale.CHINESE));;
    }
}

3.2 事件发布与监听

可以自定义事件,需要继承ApplicationEvent类。使用@EventListener注解作用于方法上,参数为对应的事件类型来实现对该事件的监听,可以通过@EventListener注解的筛选表达式来对监听的事件进行筛选。返回值可以是void,也可以是事件类型,当为事件类型时,表示继续发布返回的事件。

监听事件时默认是同步的,即会阻塞其他监听器。可以使用@Async来使该监听方法成为异步监听,使用异步监听有以下限制;1)异常不会传递到调用者。2)建议(同步或异步都可以通过返回一个事件来继续发布事件)不要用返回值来再次发布事件,而是在方法内部手动发布事件。这是为了让事件代码更清晰及更好控制。

public class CustomEvent1 extends ApplicationEvent {
    public CustomEvent1(Object source) {
        super(source);
    }
}

public class CustomEvent2 extends ApplicationEvent {
    public CustomEvent2(Object source) {
        super(source);
    }
}

@Configuration
public class EventConfiguration {

    @EventListener(condition = "event.source == '测试1'")
    @Order(1)
    @Async
    public CustomEvent2 listener1A(CustomEvent1 customEvent1) {
        System.out.println("事件监听1A:" + customEvent1);
        return new CustomEvent2("测试2");
    }

    @EventListener(condition = "event.source != '测试1'")
    @Order(0)
    public void listener1B(CustomEvent1 customEvent1) {
        System.out.println("事件监听1B:" + customEvent1);
    }


    @EventListener
    public void listener2(CustomEvent2 customEvent2) {
        System.out.println("事件监听2:" + customEvent2);
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(EventConfiguration.class);
        context.publishEvent(new CustomEvent1("测试1"));
        context.publishEvent(new CustomEvent1("测试2"));
    }
}

3.2.1 泛型与事件的监听

在监听事件时,可以通过事件类型来对事件进行筛选。但是因为泛型的擦除(在运行过程中无法获取实例的泛型类型),无法之间利用泛型来对事件进行筛选。 可以让自定义事件类实现ResolvableTypeProvider接口,来对事件类的实例的类型进行处理,从而来实现通过泛型筛选事件。

public class GenericEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
    public GenericEvent(T source) {
        super(source);
    }

    @Override
    public ResolvableType getResolvableType() {
        System.out.println("getClass():" + getClass());
        System.out.println("getSource():" + getSource());
        return ResolvableType.forClassWithGenerics(getClass(),ResolvableType.forInstance(getSource()));
    }
}

@Configuration
public class GenericConfiguration {
    @EventListener
    public void listenerOfString(GenericEvent<String> genericEvent) {
        System.out.println("string 监听");
    }

    @EventListener
    public void listenerOfBoolean(GenericEvent<Boolean> genericEvent) {
        System.out.println("Boolean 监听");
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(GenericConfiguration.class);
        context.publishEvent(new GenericEvent<String>("string"));
        System.out.println("------------------------");
        context.publishEvent(new GenericEvent<Boolean>(false));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/377903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sping Cloud Hystrix 参数配置、简单使用、DashBoard

Sping Cloud Hystrix 文章目录 Sping Cloud Hystrix一、Hystrix 服务降级二、Hystrix使用示例三、OpenFeign Hystrix四、Hystrix参数HystrixCommand.Setter核心参数Command PropertiesFallback降级配置Circuit Breaker 熔断器配置Metrix 健康统计配置Request Context 相关参数C…

在flink-connector-jdbc中增加对国产数据库达梦(V8)的支持

在flink-connector-jdbc中增加对国产数据库达梦(V8)的支持 ​ 本文将展示如何在flink-connector-jdbc中增加对国产数据库达梦(V8)的支持。演示基于Java语言&#xff0c;使用Maven。 1. 关于flink-connector-jdbc ​ flink-connector-jdbc是Apache Flink框架提供的一个用于与…

2 月 7 日算法练习- 数据结构-树状数组上二分

问题引入 给出三种操作&#xff0c; 0在容器中插入一个数。 1在容器中删除一个数。 2求出容器中大于a的第k大元素。 树状数组的特点就是对点更新&#xff0c;成段求和&#xff0c;而且常数非常小。原始的树状数组只有两种操作&#xff0c;在某点插入一个数和求1到i的所有数的…

07-OpenFeign-HTTP压缩优化

gzip是一种数据格式&#xff0c;采用用deflate算法压缩数据&#xff1b;gzip是一种流行的数据压缩算法&#xff0c;应用十分广泛&#xff0c;尤其是在Linux平台。 当GZIP压缩到一个纯文本数据时&#xff0c;效果是非常明显的&#xff0c;大约可以减少70&#xff05;以上的数据…

基于hadoop+spark的大规模日志的一种处理方案

概述: CDN服务平台上有为客户提供访问日志下载的功能,主要是为了满足在给CDN客户提供服务的过程中,要对所有的记录访问日志,按照客户定制的格式化需求以小时为粒度(或者其他任意时间粒度)进行排序、压缩、打包,供客户进行下载,以便进行后续的核对和分析的诉求。而且CDN…

java Servlet 云平台教学系统myeclipse定制开发SQLServer数据库网页模式java编程jdbc

一、源码特点 JSP 云平台教学系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助 系统采用serlvet dao bean&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B/S模式开发。开发 环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据…

【Python基础】案例分析:电影分析

电影分析 项目背景&#xff1a; 数据集介绍&#xff1a;movie_lens数据集是一个电影信息&#xff0c;电影评分的数据集&#xff0c;可以用来做推荐系统的数据集需求&#xff1a;对电影发展&#xff0c;类型&#xff0c;评分等做统计分析。目标&#xff1a;巩固pandas相关知识…

Redis——缓存设计与优化

讲解Redis的缓存设计与优化&#xff0c;以及在生产环境中遇到的Redis常见问题&#xff0c;例如缓存雪崩和缓存穿透&#xff0c;还讲解了相关问题的解决方案。 1、Redis缓存的优点和缺点 1.1、缓存优点&#xff1a; 高速读写&#xff1a;Redis可以帮助解决由于数据库压力造成…

Blender教程(基础)-顶点的移动、滑移-16

一、顶点的移动与缩放 ShiftA新建柱体、切换到编辑模式 点模式下&#xff0c;选择一个顶点、选择移动&#xff08;GZ&#xff09;&#xff0c;发现顶点严Z轴移动&#xff0c;如下图所示 GY 按数字键盘7切换视图&#xff0c;选择这个面的所有顶点 按S把面缩放大 Ctrl…

第九个知识点:内部对象

Date对象: <script>var date new Date();date.getFullYear();//年date.getMonth();//月date.getDate();//日date.getDay();//星期几date.getHours();//时date.getMinutes();//分date.getSeconds();//秒date.getTime();//获取时间戳&#xff0c;时间戳时全球统一&#x…

一键放置柱子护角,你get了吗?

今天写个番外篇&#xff0c;给柱子添加护角。 记得几年前刚开始做BIM的时候&#xff0c;有次做车库导视方案模型&#xff0c;记得好像是鼎伦设计的车库一体化方案&#xff0c;当时柱子护角就给了两种方案&#xff0c;而且基本上每颗柱子上都要放护角&#xff0c;然后甲方竟然要…

容斥原理基础

文章目录 容斥原理的引入从集合的角度考虑推广例子不被2、3、5整除的数错排问题求不定方程的解Devu和鲜花 容斥原理的引入 从一个小学奥数问题引入&#xff1a; 一个班级有50人 喜欢语文的人有20人 喜欢数学的人有30人 同时喜欢语文数学的人有10人。 问题&#xff1a; 两门都不…

10个简单有效的编辑PDF文件工具分享

10个编辑PDF文件工具作为作家、编辑或专业人士&#xff0c;您可能经常发现自己在处理 PDF 文件。无论您是审阅文档、创建报告还是与他人共享工作&#xff0c;拥有一个可靠的 PDF 编辑器供您使用都非常重要。 10个简单适用的编辑PDF文件工具 在本文中&#xff0c;我们将介绍当今…

详细分析python中的 async 和 await(附Demo)

目录 前言1. 基本知识2. Demo2.1 Demo1&#xff08;同步&#xff09;2.2 Demo2&#xff08;错误&#xff09;2.3 Demo3&#xff08;不正确的异步&#xff09;2.4 Demo4&#xff08;正确异步&#xff09; 3. 完整版4. 拓展4.1 asyncio.create_task(coroutine)4.2 asyncio.gather…

FXTM富拓监管变更!2024开年连续3家交易商注销牌照

交易商的监管信息是经常发生变更的&#xff0c;即使第一次投资时查询平台监管牌照&#xff0c;投资者仍需持续关注其监管动态。千万不要以为第一步审核好后就万事大吉了&#xff01; 2024年开年&#xff0c;就有3家交易商的重要信息发生变更&#xff0c;注销其金融监管牌照&…

Java 将TXT文本文件转换为PDF文件

与TXT文本文件&#xff0c;PDF文件更加专业也更适合传输&#xff0c;常用于正式报告、简历、合同等场合。项目中如果有使用Java将TXT文本文件转为PDF文件的需求&#xff0c;可以查看本文中介绍的免费实现方法。 免费Java PDF库 本文介绍的方法需要用到Free Spire.PDF for Java…

编程实例分享,宠物诊所电子处方怎么开,兽医电子处方模板电子版操作教程

编程实例分享&#xff0c;宠物诊所电子处方怎么开&#xff0c;兽医电子处方模板电子版操作教程 一、前言 以下操作教程以 佳易王兽医电子处方软件V16.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、在系统 设置里可以设置打印参数&#x…

《动手学深度学习(PyTorch版)》笔记7.2

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

单片机学习笔记---LED点阵屏的工作原理

目录 LED点阵屏分类 LED点阵屏显示原理 74HC595的介绍 一片74HC595的工作原理 多片级联工作原理 总结 LED点阵屏由若干个独立的LED组成&#xff0c;LED以矩阵的形式排列&#xff0c;以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合&#xff0c;如汽…

go语言进阶篇——面向对象(一)

什么是面向对象 在我们设计代码时&#xff0c;比如写一个算法题或者写一个问题结局办法时&#xff0c;我们常常会使用面向过程的方式来书写代码&#xff0c;面向过程主要指的是以解决问题为中心&#xff0c;按照一步步具体的步骤来编写代码或者调用函数&#xff0c;他在问题规…