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 {@Beanpublic Service liteService1() {System.out.println("liteService1 实例化");return new Service(){};}@Beanpublic 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() {};}@Beanpublic Service fullService2() {// 直接调用了fullService1()方法,无论该方法在个类被调用多少次,都只会生成一个实例(单例)System.out.println("fullService2 实例化:" + fullService1());return new Service() {};}@Beanpublic 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 {@Beanpublic Service1 service1(Service2 service2) {System.out.println("service1() service2:");Service1 service1 = new Service1();System.out.println("service1:" + service1);return service1;}
}@Configuration
public class Configuration2 {@Beanpublic 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 {@Beanpublic 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/beanshttps://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实例化");}@Beanpublic 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 errormessage/message_en.properties:
info=info str 
content=message contentmessage/message_en.properties:
info=中文内容
content=消息内容@Configuration
public class MessageSourceConfiguration{@Beanpublic 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)@Asyncpublic 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);}@EventListenerpublic 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);}@Overridepublic ResolvableType getResolvableType() {System.out.println("getClass():" + getClass());System.out.println("getSource():" + getSource());return ResolvableType.forClassWithGenerics(getClass(),ResolvableType.forInstance(getSource()));}
}@Configuration
public class GenericConfiguration {@EventListenerpublic void listenerOfString(GenericEvent<String> genericEvent) {System.out.println("string 监听");}@EventListenerpublic 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));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevOps落地笔记-20|软件质量:决定系统成功的关键

上一课时介绍通过提高工程效率来提高价值交付效率&#xff0c;从而提高企业对市场的响应速度。在提高响应速度的同时&#xff0c;也不能降低软件的质量&#xff0c;这就是所谓的“保质保量”。具备高质量软件&#xff0c;高效率的企业走得更快更远。相反&#xff0c;低劣的软件…

消息中间件之RocketMQ源码分析(八)

RocketMQ中的消息过滤 RocketMQ设计了消息过滤&#xff0c;来解决大量无意义流量的传输:即对于客户端不需要的消息&#xff0c; Broker就不会传输给客户端&#xff0c;以免浪费宽带&#xff0c;RocketMQ4.2.0支持Tag过滤、SQL92过滤、Filter Server过滤 Tag过滤 第一步:用户发…

蓝桥杯Web应用开发-CSS3 新特性【练习三:文本阴影】

文本阴影 text-shadow 属性 给文本内容添加阴影的效果。 文本阴影的语法格式如下&#xff1a; text-shadow: x-offset y-offset blur color;• x-offset 是沿 x 轴方向的偏移距离&#xff0c;允许负值&#xff0c;必须参数。 • y-offset 是沿 y 轴方向的偏移距离&#xff0c…

Swift Combine 管道 从入门到精通三

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二 1. 用弹珠图描述管道 函数响应式编程的管道可能难以理解。 发布者生成和发送数据&#xff0c;操作符对该数据做出响应并有可能更改它&#xff0c;订阅者请求并接收这些数据。 这…

LoveWall v2.0Pro社区型校园表白墙源码

校园表白墙&#xff0c;一个接近于社区类型的表白墙&#xff0c;LoveWall。 源码特色&#xff1b; 点赞&#xff0c; 发评论&#xff0c; 发弹幕&#xff0c; 多校区&#xff0c; 分享页&#xff0c; 涉及违禁物等名词进行检测&#xff01; 安装教程: 环境要求&#xff1b;…

一文读懂|Apollo自动驾驶平台9.0全面解读

2023年12月19日&#xff0c;百度正式推出了Apollo开放平台的全新升级版本--Apollo开放平台9.0&#xff0c;面向所有开发者和生态合作伙伴&#xff0c;以更强的算法能力、更灵活易用的工具框架&#xff0c;以及更易拓展的通用场景能力&#xff0c;继续构筑自动驾驶开发的领先优势…

极限的反问题【高数笔记】

1. 什么是极限反问题&#xff1f; 2. 极限反问题分为几类&#xff1f; 3. 每一类极限反问题的具体做法是什么&#xff1f; 4. 每一类极限反问题具体做法是否有前提条件&#xff1f; 5. 例题&#xff1f;

本地安全策略 | 服务器管理 | 配置项

本地安全策略 Windows 本地安全策略是一组在本地计算机上配置的安全设置&#xff0c;用于管理计算机的安全性和访问控制。这些策略是针对单个计算机的&#xff0c;与域策略不同&#xff0c;本地安全策略不通过域控制器进行集中管理。本地安全策略通过本地组策略编辑器进行配置…

Linux---线程

线程概念 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列” 一切进程至少都有一个执行线程 线程在进程内部运行&#xff0c;本质是在进程地址空间内运行 在Linux系统中&#xff0c;在CPU眼中…

数据结构第十二天(队列)

目录 前言 概述 源码&#xff1a; 主函数&#xff1a; 运行结果&#xff1a; 前言 今天和大家共享一句箴言&#xff1a;我本可以忍受黑暗&#xff0c;如果我不曾见过太阳。 概述 队列&#xff08;Queue&#xff09;是一种常见的数据结构&#xff0c;遵循先进先出&#…

25、数据结构/二叉树相关练习20240207

一、二叉树相关练习 请编程实现二叉树的操作 1.二叉树的创建 2.二叉树的先序遍历 3.二叉树的中序遍历 4.二叉树的后序遍历 5.二叉树各个节点度的个数 6.二叉树的深度 代码&#xff1a; #include<stdlib.h> #include<string.h> #include<stdio.h> ty…

UDP是什么,UDP协议及优缺点

UDP&#xff0c;全称 User Datagram Protocol&#xff0c;中文名称为用户数据报协议&#xff0c;主要用来支持那些需要在计算机之间传输数据的网络连接。 UDP 协议从问世至今已经被使用了很多年&#xff0c;虽然目前 UDP 协议的应用不如 TCP 协议广泛&#xff0c;但 UDP 依然是…

提速MySQL:数据库性能加速策略全解析

提速MySQL&#xff1a;数据库性能加速策略全解析 引言理解MySQL性能指标监控和评估性能指标索引优化技巧索引优化实战案例 查询优化实战查询优化案例分析 存储引擎优化InnoDB vs MyISAM选择和优化存储引擎存储引擎优化实例 配置调整与系统优化配置调整系统优化优化实例 实战案例…

Linux学习笔记(centOS)—— 文件系统

目录 一、Linux中的文件 打开方式 二、目录结构​ 三、相关命令 切换目录命令 列出当前目录下的文件和目录命令 一、Linux中的文件 “万物皆文件。” 图1.1 所有文件 打开方式 图形化界面左上角的位置→计算机&#xff0c;打开以后就可以看到Linux全部的文件了&#xf…

C语言:操作符详解

创作不易&#xff0c;给个三连吧&#xff01;&#xff01; 一、算术操作符 C语言中为了方便计算&#xff0c;提供了算数操作符&#xff0c;分别是:,-,*,/,% 由于这些操作符都是有两个操作数&#xff08;位于操作符两边&#xff09;&#xff0c;所以这种操作符也叫做双目操作…

【操作系统】MacOS虚拟内存统计指标

目录 命令及其结果 参数解读 有趣的实验 在 macOS 系统中&#xff0c;虚拟内存统计指标提供了对系统内存使用情况和虚拟内存操作的重要洞察。通过分析这些指标&#xff0c;我们可以更好地了解系统的性能状况和内存管理情况。 命令及其结果 >>> vm_stat Mach Virtu…

查看NodeJs版本和查看NPM版本

Windows10 Dos命令下 查看NodeJs版本和查看NPM版本 NodeJs的命令是&#xff1a;node -v Npm的命令是&#xff1a;npm -v 下图&#xff1a; 记录下&#xff01;~

Springboot 整合 Elasticsearch(三):使用RestHighLevelClient操作ES ①

&#x1f4c1; 前情提要&#xff1a; Springboot 整合 Elasticsearch&#xff08;一&#xff09;&#xff1a;Linux下安装 Elasticsearch 8.x Springboot 整合 Elasticsearch&#xff08;二&#xff09;&#xff1a;使用HTTP请求来操作ES 目录 一、Springboot 整合 Elasticsea…

DC-7靶机渗透详细流程

信息收集&#xff1a; 1.存活扫描&#xff1a; 由于靶机和kali都是nat的网卡&#xff0c;都在一个网段&#xff0c;我们用arp-scan会快一点&#xff1a; arp-scan arp-scan -I eth0 -l └─# arp-scan -I eth0 -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:dd:ee:6…

【蓝桥杯冲冲冲】[NOIP2017 提高组] 宝藏

蓝桥杯备赛 | 洛谷做题打卡day29 文章目录 蓝桥杯备赛 | 洛谷做题打卡day29[NOIP2017 提高组] 宝藏题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示题解代码我的一些话[NOIP2017 提高组] 宝藏 题目背景 NOIP2017 D2T2 题目描…