spring boot基于Java的容器配置讲解
作者:dust1 发布时间:2023-11-09 05:24:54
spring容器是负责实例化、配置、组装组件的容器。
容器的配置有很多,常用的是xml、Java注解和Java代码。
在spring中Ioc容器相关部分是context和beans中。其中context-support保存着许多线程的容器实现。比如AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext。两者只有接收的目标不同,前者接收Java类后者接收Xml文件。但作为spring容器的不同实现殊途同归。
下面我通过spring文档中的介绍来自己过一遍容器配置,来加深印象。这里我使用的是springboot。所以有些基于web.xml的配置不会涉及到。
@Bean和@Configuration
@Configuration注解的类,表示这个类是一个配置类,类似于<beans></beans>或者.xml文件。
@Bean注解用来说明使用springIoc容器管理一个新对象的实例化、配置和初始化。类似于<bean></bean>,默认情况下,bean名称就是方法名称.
例子:
@Configuration
public class Conf {
@Bean
public HelloService helloService() {
return new HelloServiceImpl();
}
}
这种配置方式就类似于xml配置中的
<beans>
<bean id="helloService" class="com.dust.service.impl.HelloServiceImpl" />
</beans>
等价于注解配置中的
@Service
public class HelloServiceIMpl implements HelloService {
@Override
public String hello() {
return "hello world";
}
}
使用AnnotationConfigApplicationContext实例化Spring容器
这是在spring3.0加入的功能,除了接收@Configuration注解的类作为输入类之外还可以接受使用JSR-330元数据注解的简单类和@Component类。
当@Configuration注解的类作为输入时,@Configuration类本身会被注册为一个bean,在这个类中所有用@Bean注解的方法都会被定义为一个bean。
具体有哪些类型的bean可以方法遍历打印容器中的bean。
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Conf.class);
HelloService helloService = context.getBean(HelloService.class);
String hello = helloService.hello();
System.out.println(hello);
}
该实例的步骤为:
1. 创建AnnotationConfigApplicationContext容器对象,同时将@Configuration注解的Conf.class作为参数传入。
2. 容器回根据传入的Conf类来构建bean。其中就有helloService
3. 通过bean的对象类型获取到容器中保管的对象。
4. 执行对象方法
但是AnnotationConfigApplicationContext并不仅使用@Configuration类。任何@Component或JSR-330注解的类都可以作为输入提供给构造函数。例如:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(HelloServiceImpl.class, A.class, B.class);
HelloService helloService = context.getBean(HelloService.class);
String hello = helloService.hello();
System.out.println(hello);
}
上面假设MyServiceImpl、A和B都用了Spring的依赖注入的注解,例如@Autowired。
使用register(Class<?>…)的方式构建容器
也可以使用无参构造函数实例化AnnotationConfigApplicationContext,然后使用register()方法配置。当使用编程方式构建AnnotationConfigApplicationContext时,这种方法特别有用。
例子:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
HelloService helloService = context.getBean(HelloService.class);
String hello = helloService.hello();
System.out.println(hello);
}
其中的refresh方法是一个初始化工作。否则注册的类并不会被生成bean。
使用scan(String …)组件扫描
组件扫描,只需要设置好对应包路径,spring容器回自动扫描包下面所有能够被容器初始化的Java类。
使用注解:
@Configuration
@ComponentScan("com.example.springdemo.beans")
public class Conf {
@Bean
public HelloService helloService() {
//用这种方法创建的service相当于用@Service注解标注
return new HelloServiceImpl();
}
}
在该路径下还有一个配置文件:
@Configuration
public class Conf2 {
@Bean
public ByeService byeService() {
//用这种方法创建的service相当于用@Service注解标注
return new ByeServiceImpl();
}
}
然后是初始化容器:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
ByeService byeService = context.getBean(ByeService.class);
String hello = byeService.bye();
System.out.println(hello);
}
可以看到,虽然传入的是Conf类,但是由于包扫描机制,该容器同时创建了Conf2类中的bean。
这就类似xml配置中的:
<beans>
<context:component-scan base-package="com.example.springdemo.beans"/>
</beans>
还可以直接调用容器的扫描方法
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// context.register(Conf.class);
context.scan("com.example.springdemo.beans");
context.refresh();
ByeService byeService = context.getBean(ByeService.class);
String hello = byeService.bye();
System.out.println(hello);
}
springboot中的包扫描
springboot通过main方法启动,其中的注解为@SpringBootApplication。通过查看该注解的代码可以发现一下代码段:
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
由此可以知道@SpringBootApplication注解包括了包扫描注解,同时扫描的是该类的目录以及子目录的所有可以被spring容器初始化的类
AnnotationConfigWebApplicationContext对于web应用的支持
AnnotationConfigApplicationContext在WebApplicationContext中的变体为 AnnotationConfigWebApplicationContext。当配置Spring ContextLoaderListener servlet * 、Spring MVC DispatcherServlet的时候,可以用此实现。
Bean依赖
@Bean注解方法可以具有描述构建该bean所需依赖关系的任意数量的参数。依赖的必须也是Ioc容器中注册的bean。
将上面的代码修改后如下:
@Configuration
public class Conf {
@Bean
public HelloService helloService(ByeService byeService) {
return new HelloServiceImpl(byeService);
}
@Bean
public ByeService byeService() {
return new ByeServiceImpl();
}
}
public class HelloServiceImpl implements HelloService {
private ByeService byeService;
public HelloServiceImpl(ByeService byeService) {
this.byeService = byeService;
}
@Override
public String hello() {
return "hello world" + byeService.bye();
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
HelloService helloService = context.getBean(HelloService.class);
String hello = helloService.hello();
System.out.println(hello);
ByeService byeService = context.getBean(ByeService.class);
String bye = byeService.bye();
System.out.println(bye);
}
输出结果:
hello worldGoodbye!
Goodbye!
这种解决原理和基于构造函数的依赖注入几乎相同。
生命周期回调
@Bean注解支持任意的初始化和销毁回调方法,这与Spring XML 中bean元素上的init方法和destroy-method属性非常相似:
@Bean(initMethod = "init")
public HelloService helloService(ByeService byeService) {
return new HelloServiceImpl(byeService);
}
@Bean(destroyMethod = "destroy")
public ByeService byeService() {
return new ByeServiceImpl();
}
public interface ByeService {
String bye();
void destroy();
}
public interface HelloService {
String hello();
void init();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
context.close();
}
输出如下:
init helloService!!
destroy byeService!
默认情况下,Ioc容器关闭后所有bean都会被销毁,但是如果要引入一个生命周期在应用程序之外进行管理的组件,例如:DataSource。那么只需要将@Bean(destroyMethod =””)添加到你的bean定义中即可禁用默认(推测)模式。
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
当然,初始化的时候也可以先执行对应方法,而不用交给Ioc容器
@Bean
public HelloService helloService(ByeService byeService) {
HelloService helloService = new HelloServiceImpl(byeService);
helloService.init();
return helloService;
}
@Scope和scope 代理
Scope描述的是Spring容器如何新建Bean实例的。
Singleton:一个Spring容器中只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例。
Prototype:每次调用新建一个Bean实例。
Request:Web项目中,给每一个 http request 新建一个Bean实例。
Session:Web项目中,给每一个 http session 新建一个Bean实例。
GlobalSession:这个只在portal应用中有用,给每一个 global http session 新建一个Bean实例。
@Bean
//每次调用就创建一个新的bean
@Scope("prototype")
public UserInfo userInfo() {
return new UserInfo();
}
@Bean
public UserService userService() {
UserService userService = new UserServiceImpl();
userService.init(userInfo());
return userService;
}
测试代码:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
UserService userService = context.getBean(UserService.class);
UserService userService2 = context.getBean(UserService.class);
UserInfo userInfo = context.getBean(UserInfo.class);
UserInfo userInfo2 = context.getBean(UserInfo.class);
System.out.println(userService == userService2);
System.out.println(userInfo == userInfo2);
}
输出:
true
false
自定义Bean命名
通常,bean的名称是bean的方法名,但是可以通过name属性重命名。有时一个单一的bean需要给出多个名称,称为bean别名。为了实现这个目标,@Bean注解的name属性接受一个String数组。
@Bean(name = {"user", "userService", "User"})
public UserService userService() {
UserService userService = new UserServiceImpl();
userService.init(userInfo());
return userService;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
Object user = context.getBean("user");
Object userService = context.getBean("userService");
Object User = context.getBean("User");
System.out.println(user == userService);
System.out.println(user == User);
System.out.println(userService == User);
}
输出:
true
true
true
Bean描述
有时候需要提供一个详细的bean描述文本是非常有用的。当对bean暴露(可能通过JMX)进行监控使,特别有用。可以使用@Description注解对Bean添加描述:
@Bean(name = {"user", "userService", "User"})
@Description("这是用户服务对象")
public UserService userService() {
UserService userService = new UserServiceImpl();
userService.init(userInfo());
return userService;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
String description = context.getBeanDefinition("user").getDescription();
System.out.println(description);
}
输出:
这是用户服务对象
基于Java组合配置
使用@Import注解
和Spring XML文件中使用元素来帮助模块化配置类似,@Import注解允许从另一个配置类加载@Bean定义:
@Configuration
@Import(UserConf.class)
public class Conf {
@Bean(initMethod = "init")
public HelloService helloService(ByeService byeService) {
//用这种方法创建的service相当于用@Service注解标注
return new HelloServiceImpl(byeService);
}
@Bean(destroyMethod = "destroy")
public ByeService byeService() {
return new ByeServiceImpl();
}
}
@Configuration
public class UserConf {
@Bean
//每次调用就创建一个新的bean
@Scope("prototype")
public UserInfo userInfo() {
return new UserInfo();
}
@Bean(name = {"user", "userService", "User"})
@Description("这是用户服务对象")
public UserService userService() {
UserService userService = new UserServiceImpl();
userService.init(userInfo());
return userService;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Conf.class);
context.refresh();
String description = context.getBeanDefinition("user").getDescription();
System.out.println(description);
}
这种方法简化了容器实例化,因为只需要处理一个类,而不是需要开发人员在构建期间记住大量的@Configuration注解类。
Java and XML 混合配置
Java配置并不能100%替代xml配置,因此Ioc容器支持两者混合配置。不过这里有个区别就是以xml为中心还是以Java配置为中心。
以XML为中心
@Configuration
public class DataSourceConf {
@Autowired
private DataSource dataSource;
@Bean
public DataSourceService dataSource() {
return new DataSourceerviceImpl(dataSource);
}
}
jdbc.url=jdbc:mysql://39.108.119.174:3306/dust
jdbc.username=root
jdbc.password=123456
<beans>
<context:annotation-config/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean class="com.example.springdemo.beans.DataSourceConf"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
DataSourceService dataSourceService = context.getBean(DataSourceService.class);
System.out.println(dataSourceService.toString());
}
以Java类为中心
<beans>
<context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
@Configuration
@ImportResource("classpath:spring/datasource.xml")
public class DataSourceConf {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSourceService dataSource() {
return new DataSourceerviceImpl(url, username, password);
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.example.springdemo.beans");
context.refresh();
DataSourceService dataSourceService = context.getBean(DataSourceService.class);
System.out.println(dataSourceService.toString());
// ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/datasource.xml");
// DataSourceService dataSourceService = context.getBean(DataSourceService.class);
// System.out.println(dataSourceService.toString());
}
来源:https://juejin.im/post/5ca1c1ecf265da30b34090ae
猜你喜欢
- 一、SpringCache介绍Spring Cache 是一个优秀的缓存组件。自Spring 3.1起,提供了类似于@Transaction
- Java 数据库连接池详解数据库连接池的原理是:连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库
- 1、前言最近在用Kotlin+Spring Boot写一个后端项目,实体类习惯性地用了Kotlin中的data class,但是Spring
- @Order控制配置类/AOP/方法/字段的加载顺序1.AOP加载顺序 @Component &nbs
- mkdir函数用于创建目录。格式如下:#include<sys/types.h>#include<sys/stat.h&g
- 1、说明isInterrupted()可以判断当前线程是否被中断,仅仅是对interrupt()标识的一个判断,并不会影响标识发生任何改变(
- 一、写在前面数据结构中的队列应该是比较熟悉的了,就是先进先出,因为有序故得名队列,就如同排队嘛,在对尾插入新的节点,在对首删除节点.jdk集
- 前言ps命令的作用是显示进程信息的。|符号,是个管道符号,表示左右两边两个命令同时执行。grep命令是查找(Global Regular E
- 单行文本的输入存在严重的缺陷,也不适合实际的运用,本节通过一个无功能的记事本来介绍可以进行多行输入的JTextArea:JTextArea(
- @Lazy用于指定该Bean是否取消预初始化。主要用于修饰Spring Bean类,用于指定该Bean的预初始化行为,使用该Annotati
- 在之前项目中有用到关于获取手机联系人的部分,闲置就想和大家分享一下,话不多说,上代码:java部分:package com.example.
- 一、方法这里我们用两种方法来实现跑马灯效果,虽然实质上是一种实质就是:1、TextView调出跑马灯效果2、TextView获取焦点&nbs
- Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,
- Java泛型映射不同的值类型详解前言:一般来说,开发人员偶尔会遇到这样的情形: 在一个特定容器中映射任意类型的值。然而Java 集合API只
- Java 15 在 2020 年 9 月发布,虽然不是长久支持版本,但是也带来了 14 个新功能,这些新功能中有不少是十分实用的。Java
- 更新: 工作中突然想起来,关于Yaml的使用,并不属于Spring的范畴,是org.yaml.snakeyaml处理的。所以yaml的使用应
- 本文为大家分享了JAVA语言课程设计:连连看小游戏,供大家参考,具体内容如下1.设计内容界面中有5*10的界面,图中共有6种不同的图片,每两
- Spring Cache抽象-使用SpEL表达式概述在Spring Cache注解属性中(比如key,condition和unless),S
- 本文实例为大家分享了Unity实现场景漫游相机的具体代码,供大家参考,具体内容如下前言拿到场景后总喜欢在场景里面玩一段时间,那这个脚本就是你
- 我们都知道mybatis在进行参数判断的时候,直接可以用<if test=""></if> 就可