IOC容器
Spring的IOC容器用来创建、存储、销毁对象并维护对象之间的关系,IOC容器也被称为【Bean容器】。
IOC的概念
IOC全称【Inversion of Control】,意为控制反转,有的地方称为IOC容器,它是一项对象生成、获取和管理的技术,Java作为一门面向对象的语言,对象的管理至关重要。在传统的java编程中,对象一般通过new构建,二spring通过描述构建对象。
项目中会有各式各样的对象,这些对象也不是独立存在互不相关的,spring提供了依赖注入【Dependency Injection,DI】来管理各个对象之间的关系。
常见的关系有:依赖、关联、聚合、组合、继承、实现。
IOC容器介绍
spring通过工厂创建对象,涉及到的接口和类非常多,其中我们经常打交道的有:
- BeanFactory:定义一系列获取bean的方法,根据不同参数,条件获取Bean,是整个IOC容器体系的根接口。
- ApplicationContext:在BeanFactory基础上扩展了事件发布、资源加载、环境参数、国际化消息等功能。
- ClassPathXmlApplicationContext:从类路径上读取spring声明文件来创建Bean。
- FileSystemXmlApplicationContext:从文件中的spring声明文件来创建Bean。
- AnnotationConfigApplicationContext:通过注解来创建Bean,需要指定要识别的包名,会识别通过诸如@Configuration、@Component、@Service、@Bean等描述的类。
ClassPathXmlApplicationContext实践
通过最原始的xml文件管理Bean,需要使用ClassPathXmlApplicationContext容器。
数据源对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package top.lime.entity;
public class User {
private Long id;
private String name;
private Integer age;
public User() {
}
public User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
|
xml配置
在resources目录下创建springContext.xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring描述Bean -->
<!-- id属性是唯一的,不能重复 -->
<bean id="user1" class="top.lime.entity.User">
<!-- 指定具体的属性数据 -->
<!-- property是通过set方法注入的,需要实体类实现set方法 -->
<property name="id" value="1"/>
<property name="name" value="zhangsan"/>
<property name="age" value="18"/>
</bean>
<bean id="user2" class="top.lime.entity.User">
<!-- 指定具体的属性数据 -->
<!-- constructor-arg是通过构造方法注入的,需要实体类实现了构造方法 -->
<constructor-arg name="id" value="2"/>
<constructor-arg name="name" value="lisi"/>
<constructor-arg name="age" value="24"/>
</bean>
</beans>
|
获取Bean对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@SpringBootApplication
public class App {
public static void main(String[] args) {
//SpringApplication.run(App.class, args);
//读取springContext.xml文件获取容器,并通过容器获取Bean
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springContext.xml");
//获取Bean,getBean()里面可以填Bean的id,也可填Bean的名字,对应xml里面Bean标签的id和name属性
//默认返回的是Object类型,可以强转回原来的类型
User user1 = (User) applicationContext.getBean("user1");
//不想强转可以通过类型来获取Bean,如果有多个同类型的bean还要额外指定名字
User user2 = applicationContext.getBean("user2",User.class);
//打印结果
System.out.println(user1);
System.out.println(user2);
}
}
|
FileSystemXmlApplicationContext实践
使用FileSystemXmlApplicationContext的话参数中填写文件路径,其余相同
获取Bean对象
1
2
3
4
5
6
7
8
9
|
@SpringBootApplication
public class App {
public static void main(String[] args) {
// 从文件系统获取,传入springContext.xml文件的路径,获取Bean
FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext("D:\\JavaWeb\\porject\\springContext.xml");
User user2 = applicationContext.getBean("user2", User.class);
System.out.println(user2);
}
}
|
AnnotationConfigApplicationContext实践
此处介绍两种创建Bean方式,一种是在AnnotationConfigApplicationContext的构造方法中传入包名,另一种是在构造方法中传入配置类。
通过传入包名
在需要创建的Bean上通过@Component或者@Configuration等注解声明bean,相当于bean标签,通过@Value注解注入值。
数据源对象
这里通过引入的lombok依赖,在类上加注解,实现在编译时生成get/set和构造方法的代码。
1
2
3
4
5
6
7
8
9
10
11
12
|
@Data //生成get/set方法
@AllArgsConstructor //生成全参构造函数
@NoArgsConstructor //生成空参构造函数
@Component //通过注解创建对象,spring启动时扫描到有该注解的类,会当作一个Bean加载到IOC容器中
public class Book {
@Value("13") //通过@Value注入参数
private Long id;
@Value("西游记")
private String bookName;
@Value("zhangsan")
private String author;
}
|
获取Bean对象
1
2
3
4
5
6
7
8
9
10
11
12
|
public class App {
public static void main(String[] args) {
//通过注解获取Bean
//通过包名扫描 比如:top.lime 就会扫描该包下所有的类和子包下的所有类
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("top.lime");
//获取Bean name默认的是首字母小写的类名
Book book1 = applicationContext.getBean("book");
Book book2 = applicationContext.getBean(Book.class);
System.out.println(book1);
System.out.println(book2);
}
}
|
通过注解扫描包
在主类上添加@ComponentScan包扫描注解,注意:该注解会扫描述的类所在包和其子包的注解
1
2
3
4
5
6
7
8
9
|
@ComponentScan(value = "top.lime") //扫描top.lime包下的所有与@Component相关的注解
public class App {
public static void main(String[] args) {
//通过包扫描注解获取Bean
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
Book book = applicationContext.getBean("book", Book.class);
System.out.println(book);
}
}
|
通过@Bean注解创建Bean
通过Bean注解创建对象的话,首先需要将User类中的@Value注解取消掉,否则Bean注入的值是Value注解注入的。
数据源对象
1
2
3
4
5
6
|
@Data
public class Car {
private Long id;
private String carName;
private String brand;
}
|
创建配置类
在配置类中生成Bean并注入IOC容器
@Configuration:该注解使用在类上,标记该类为一个配置类,用@Component、@Service都可以,只是@Configuration注解更利于理解,更规范。目的就是项目启动时可以识别该类,并且加载该类到IOC容器中。因为该类中有Bean的创建,所以需要加载它!
@Bean:该注解可以使用在方法和注解上,一般都使用在方法上,表示被描述的方法会返回一个Bean,方法内室创建这个Bean的过程,BeanName默认是方法名,可以通过Bean注解的value属性或name属性自定义BeanName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//表示CarConfig是一个配置类,他是在@Component的基础上衍生出来的
@Configuration
public class CarConfig {
//创建Bean,Bean的名字默认是方法名
@Bean //如果需要指定名字则 @Bean(name="car1")
public Car myCar() {
Car car = new Car();
car.setId(10L);
car.setCarName("bird");
car.setBrand("bbb");
//返回对象,交给IOC管理
return car;
}
}
|
获取Bean对象
1
2
3
4
5
6
7
8
|
public class App {
public static void main(String[] args) {
//通过@Bean注解创建Bean,并获取
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(CarConfig.class);
Car myCar = applicationContext.getBean("myCar", Car.class);
System.out.println(myCar);
}
}
|
装配Bean最佳实践
装配Bean使用全注解 + 配置类的方式装配自定义Bean,是目前通用最优的方式了,因为SpringBoot用的就是这种方式。如果要修改SpringBoot的默认配置,也是通过配置文件或者JavaConfig的方式修改。比如通过数据源的案例
数据源对象
1
2
3
4
5
6
7
|
@Data
public class MySQLDataSource {
private String driverName;
private String url;
private String username;
private String password;
}
|
创建配置类
配置类一般使用**@Configuration**注解描述,配置类通常用来配置系统信息或者诸如:数据库、消息队列、缓存等的第三方组件。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Configuration
public class MySQLDataSourceConfig {
@Bean
public MySQLDataSource dataSource() {
System.out.println("初始化数据库连接......");
MySQLDataSource dataSource = new MySQLDataSource();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
|
获取Bean对象
主类上使用**@ComponentScan**注解来配置要扫描的包,所有的类都应该放到此包下
注意:@SpringBootApplication注解的启动类在启动时会扫描自己所在的包及其子包
1
2
3
4
5
6
7
8
9
10
|
@SpringBootApplication
//@ComponentScan(value = "top.lime") //扫描top.lime包下的所有与@Component相关的注解
public class App {
public static void main(String[] args) {
//最优方案,使用包扫描,springboot启动时会扫描启动类所在包及其子包,如果不在启动类所在包则可用
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(App.class);
MySQLDataSource dataSource = applicationContext.getBean(MySQLDataSource.class);
}
}
|
小结
- 装配Bean时一般在一个主类上通过@ComponentScan设置包扫描
- 如果是第三方的配置通过配置类装载Bean,使用@Configuration注解描述类
配置文件
对于SpringBoot项目来说,很多配置项都是做了默认配置的,比如tomcat端口号,日志的输出规则,我们如果想要修改默认配置,要么自定义配置类覆盖原配置,要么在配置文件中修改参数,覆盖原配置。
SpringBoot中的配置文件名字按照约定来说叫application.properties或者application.yml两种,放到resources目录下,这样项目启动时会自动读取,无需做其他配置,贴出两种配置文件,只是写法不同,推荐使用yml格式文件,是缩进类型的不需要写重复的公用前缀。
properties类型文件
1
2
3
4
|
# 设置端口号
server.port=8081
#设置编码
server.servlet.encoding.charset=UTF-8
|
yml类型文件
1
2
3
4
5
6
|
# yml文件是层级结构,比较简介,清晰
server:
port: 8082
servlet:
encoding:
charset: UTF-8
|
读取配置文件
读取配置文件是开发中非常重要的操作,因为配置文件中通常会写重要的配置项,配置项在JavaConfig实现的配置类中往往会使用到,并不会在代码写死配置。
通过@Value注解获取文件值
配置类中使用@Value注解获取配置文件内容,语法格式为
@Value("${配置文件中的key}")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.stt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
// 读取信息
@Value("${spring.application.name}")
private String name;
@Value("${spring.application.version}")
private String version;
@Value("${spring.application.author}")
private String author;
public void getInfo() {
System.out.println("name: " + name + ",version: " + version + ",author: " +author);
}
}
|
通过@ConfigurationProperties注解获取值
配置文件中新增person信息为例
1
2
3
4
5
6
7
|
person:
name: lime
age: 18
id_card: 815811
hobby: # 列表、数组、集合
- fly
- game
|
配置类内容如下,必须有set方法才可赋值成功:
- 通过@Configuration注解表名是配置类,被Spring加载
- 通过@ConfigurationProperties(prefix = “person”),指明前缀
- 属性名就写配置文件的后缀,如果有下划线,使用驼峰命名就可以了
- 类中不能有其他方法
1
2
3
4
5
6
7
8
9
10
|
@Data // lombok 生成get/set方法
@Configuration
@ConfigurationProperties(prefix = "person")
public class PersonConfig {
private String name;
private Integer age;
private String idCard;
private List<String> hobby;
}
|
读取自定义文件
我们上边读取的文件是springboot约定好的文件【application.yml/properties】,如果定义一个application-db.properties文件。这个文件不是SpringBoot约定好的,此时就需要指定文件读取
1
2
3
4
|
db.classname= mysql
db.url= localhost
db.username= root
db.password= 123456
|
读取properties文件
配置类中可以通过**@PropertySource**注解指定要读取的配置文件,可以指定多个
1
2
3
4
5
6
7
8
9
10
|
@Configuration
@PropertySource(value = {"application-db.properties"})
@ConfigurationProperties(prefix = "db")
@Data
public class DBConfig {
private String classname;
private String url;
private String username;
private String password;
}
|
读取yml文件
如果读取的是yml文件,则首先需要自定义一个资源工厂类,因为SpringBoot默认只实现了读取properties文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory{
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
Resource resourceResource = resource.getResource();//先得到资源文件
if(!resourceResource.exists()){
return new PropertiesPropertySource(null, new Properties());
} else if(
//判断得到的资源文件是否是yml文件
resourceResource.getFilename().endsWith(".yml") ||
resourceResource.getFilename().endsWith(".yaml")){
System.out.println("resourceResource = " + resourceResource.getFilename());
//开始加载yaml文件
List<PropertySource<?>> sources = new YamlPropertySourceLoader()
.load(resourceResource.getFilename(),resourceResource);
return sources.get(0);
}
return super.createPropertySource(name, resource);
}
}
|
接下来在@PropertySources注解上通过factory属性指定这个自定义的工厂
1
2
3
4
5
6
7
8
9
10
|
@Configuration
@PropertySources(value = {@PropertySource(value = {"application-db.yml"},factory = YamlPropertySourceFactory.class)})
@ConfigurationProperties(prefix = "db")
@Data
public class DBConfig {
private String classname;
private String url;
private String username;
private String password;
}
|
注意:@ImportResource注解用来加载xml文件,用SpringBoot就不需要xml文件了,这个注解基本不用
通过Environment获取文件数据
还可以通过Spring提供的Environment对象获取文件数据,这个对象就霸道了。每一个文件中的值都可以获取到,用法相对较少,因为读取配置文件数据一般用在第三方组件的配置类中,配置类都是相互独立的,不会说读取所有数据,一般也是仅仅获取改配置类所需要的配置数据就可以
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@RestController
public class UserController {
@Autowired
private Environment environment;
public void controllerMethod() {
System.out.println("SttController=======》controllerMethod()");
}
@GetMapping("value")
public String getValue() {
String username = environment.getProperty("db.username");
String applicationName = environment.getProperty("spring.application.name");
System.out.println(username);
System.out.println(applicationName);
return username + " : " + applicationName;
}
}
|
profile
一般软件有开发环境、测试环境、生产环境,不同的环境有些配置可能不同,比如端口、回调地址、数据库地址、redis地址等,SpringBoot支持加载不同的配置文件,称之为profile。通常就是创建不同的配置文件,命名格式为application-${profile}.yml
默认只会加载application.yml文件不会加载其他文件。从日志也可以看出,下方红色框中的日志就表明没有激活任何profile,使用default
如果希望使用分环境加载,常见的方式有以下三种方式:
- application.yml文件中通过spring.profiles.active配置
- IDEA中配置参数,打包时不生效
- 启动jar命令添加–spring.profiles.active配置
使用spring.profiles.active
指定dev环境
1
2
3
|
spring:
profiles:
active: dev
|
IDEA配置
首先点击编辑配置
选中项目,在有效配置文件选项中填入对应文件profile,点击应用和确定完成
如果同时存在则使用IDEA配置
通过命令注入参数
在启动jar时,通过java -jarxxx.jar –spring.profiles.active=prod,命令追加参数,会使用注入参数的配置文件,这个参数是注入到虚拟机里边的哦!会覆盖配置文件中的spring.profiles.active的配置
依赖注入
Bean之间相互依赖可以通过依赖注入【Dependency Injection, DI】功能将一个Bean注入到另一个Bean中使用。例如Controller层调用Service层,Service层调用Dao层。
简单的三层架构案例
下方列出核心代码,通过**@Autowired**注解注入对象。
Dao层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 接口
public interface IUserDao {
int insertUser();
}
// 实现类
@Repository
public class UserDaoImpl implements IUserDao {
@Override
public int insertUser() {
System.out.println("用户数据层添加用户");
return 0;
}
}
|
Service层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 接口
public interface IUserService {
String createUser();
}
// 实现类
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public String createUser() {
System.out.println("用户服务层创建用户");
int result = userDao.insertUser();
return result > 0 ? "添加成功" : "添加失败";
}
}
|
controller层
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private IUserService userService;
@RequestMapping("create")
public String createUser() {
System.out.println("创建用户接口");
return userService.createUser();
}
}
|
注入值的方式
Spring中注入值一般有三种方式:
- @Autowired:Spring提供的注解,默认根据type(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类),当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个,这个时候,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。另外,如果type无法辨别注入对象时,也可以配合@Qualifier或@Primary注解来分辨注入类。
- @Resource:JavaEE提供的注解,默认根据name注入,如果不成功则按照type注入,name属性解析为bean的名称,type属性解析为bean的类型,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName。
- 构造方法:根据构造方法注入数据,原理与@Autowired相同,属性可以加final修饰,避免在业务中修改Bean的引用,增加安全性。
resource注解
1
2
|
@Resource
private IUserService userService;
|
构造方法
1
2
3
4
5
|
private IUserService userService;
public UserController(IUserService userService) {
this.userService = userService;
}
|
setter方法
1
2
3
4
5
6
7
8
9
10
11
|
@RestController
@RequestMapping("user")
public class UserController {
private IUserService userService;
// 使用set方法注入
public void setUserService(IUserService userService) {
this.userService = userService;
}
}
|
歧义性对象现象
当使用@Autowired注入时,编译期间IDEA就会提示,同时启动项目时,JVM报错,也是说存在两个Bean,构造方法的方式也会出现同样的错误。
注意:一般三层架构简引用不会存在一个接口实现多个实现类,但是当我们引入第三方组件,比如SpringSecurity、Druid等时一个组件可能会创建多个Bean,不同的功能会使用不同的Bean,就会出现这种问题。
解决方案
使用**@Autowired**注解有两种方案:
- @Primary:在一个实现类上添加该注解,标明权重较高,当发现多个同类型Bean时优先使用被@Primary修饰的Bean。如果多个Bean都加了@Primary。那么IOC容器就又无法区分了
- @Qualifier:使用在注入Bean的属性声明上,配置@Autowired实现根据类型和名字注入数据。
@Primary使用在实现类上
1
2
3
4
|
@Service
@Primary
public class UserServiceImpl implements IUserService {
}
|
@Qualifier使用在数据注入上,先根据Type注入,再根据Name注入
1
2
3
4
5
6
7
8
9
|
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
@Qualifier(value = "userServiceImpl")
private IUserService userService;
}
|
对于构造方法和set方法也是这样解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@RestController
@RequestMapping("user")
public class UserController {
private IUserService userService;
public UserController(@Autowired @Qualifier(value = "userServiceImpl") IUserService userService) {
this.userService = userService;
}
@RequestMapping("create")
public String createUser() {
System.out.println("创建用户接口");
return userService.createUser();
}
}
|
setter方法
1
2
3
4
5
|
@Autowired
@Qualifier("userServiceImpl")
public void setUserService(IUserService userService) {
this.userService = userService;
}
|
当使用@Resource注解注入数据时,会先根据name注入,再根据type注入,name值默认和属性名一致。如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@RestController
@RequestMapping("user")
public class UserController {
// name属性默认名为 userService。
@Resource
private IUserService userService;
@RequestMapping("create")
public String createUser() {
System.out.println("创建用户接口");
return userService.createUser();
}
}
|
属性名为userService那么就会默认去找beanName为userService的Bean,其实容器中没有,会再根据type注入,会找到两个对象,无法确定注入哪一个,就会出现以下错误:注入资源失败,没有合格的top.lime.service.IUserService类型bean
解决方案就是根据name属性指明名字,或将属性名修改为正确的beanName
1
2
3
4
5
6
7
|
@Resource(name = "userServiceImpl")
private IUserService userService;
或
@Resource
private IUserService userServiceImpl;
|
IDEA不建议使用Autowired
当使用Autowired注解时,IDEA中给出警告,不建议使用字段注入.
但是使用@Resource注解时则没有警告提示。
使用字段注入有一定风险存在:
- 可能导致空指针异常:如果对象创建不使用Spring容器,而是直接使用无参构造方法new一个对象,此时使用注入的对象可能导致空指针异常。
- 不能使用final修饰字段:缺乏final修饰会使得类的依赖可变,进而可能引发一些不可预料的异常。通常情况下,可以使用构造方法注入来声明强制依赖的Bean,使用Setter方法注入来声明可选依赖的Bean。
- 可能更容易违反单一职责原则:这是一个关键原因。使用字段注入可能会轻易地在类中引入各种依赖,导致类的职责过多,但开发者往往难以察觉。相比之下,使用构造方法注入,当构造方法的参数过多时,会提示开发者重构这个类。
- 不利于写单元测试:在单元测试中,使用Field注入,必须使用反射的方式来Mock依赖对象。
推荐方案:
- 当类有强依赖于其他Bean时,优先使用构造方法注入。
- 对于可选依赖,可以使用Setter方法注入,并在代码中处理可能出现的引用对象不存在的情况。
小结
依赖注入有两个注解、构造方法、setter方法三种方式,推荐使用构造方法方式注入bean,可以避免不必要的空指针异常,和Bean引用被修改的问题。
Bean作用域
在Spring容器中,Bean的作用域默认是单例的,可以通过@Scope注解设置,有6种作用域:
- prototype:一个bean定义可以有多个bean实例。
- singleton:(默认的)在每个Spring IoC容器中,一个bean定义对应只会有唯一的一个bean实例。
- request:一个bean定义对应于单个HTTP 请求的生命周期。也就是说,每个HTTP 请求都有一个bean实例,且该实例仅在这个HTTP 请求的生命周期里有效。该作用域仅适用于WebApplicationContext环境。
- session:一个bean 定义对应于单个HTTP Session 的生命周期,也就是说,每个HTTP Session 都有一个bean实例,且该实例仅在这个HTTP Session 的生命周期里有效。该作用域仅适用于WebApplicationContext环境。
- application:一个bean 定义对应于单个ServletContext 的生命周期。该作用域仅适用于WebApplicationContext环境。
- websocket【这个作用范围没有找到在哪声明】:一个bean 定义对应于单个websocket 的生命周期。该作用域仅适用于WebApplicationContext环境。
1
2
3
4
5
6
7
8
9
10
|
@Repository
// 声明作用范围
@Scope(value = "singleton")
public class UserDaoImpl implements IUserDao {
@Override
public int insertUser() {
System.out.println("用户数据层添加用户");
return 0;
}
}
|
条件装配
条件装配就是在满足相应条件的时候再装载对应的Bean,比如连接数据库时当数据源存在的时候再加载连接数据库的Bean,条件装配可以使用@Conditional和其派生注解实现。
下方以数据库连接为例:
数据源
1
2
3
4
5
6
7
8
9
|
@Data
public class DataSource {
private String driverClassName;
private String url;
private String username;
private String password;
}
|
数据库连接
数据库连接时需要用到数据数据源的参数数据
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Data
public class DataCollectionService {
private DataSource dataSource;
public void collect() {
String url = dataSource.getUrl();
String driverClassName = dataSource.getDriverClassName();
System.out.println("连接数据库======》");
System.out.println("url====》" + url);
System.out.println("driverClassName====》" + driverClassName);
}
}
|
配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Configuration
public class ApplicationConfig {
// 数据源配置
@Bean
public DataSource dataSource() {
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
// 连接配置
@Bean
// 当DataSource这个bena存在时加载
@ConditionalOnBean(DataSource.class)
public DataCollectionService dateCollectionService() {
DataCollectionService dateCollectionService = new DataCollectionService();
System.out.println("dataSource()====>" + dataSource());
dateCollectionService.setDataSource(dataSource());
return dateCollectionService;
}
}
|
测试类
1
2
3
4
5
6
7
8
9
|
@SpringBootApplication
public class ConditionalApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ConditionalApplication.class);
DataCollectionService dateCollectionService = (DataCollectionService) context.getBean("dateCollectionService");
dateCollectionService.collect();
}
}
|
——来自B站【石添的编程哲学】