SpringBoot 基础(Spring)
Bean
注解标记和扫描 (IoC)
配置类概念
@SpringBootConfiguration
或者@Configuration
注解标注的类就是配置类- 配置类本身也会加入 IoC 容器*
@Configuration
public class configuration1 {}@SpringBootConfiguration
public class configuration1 {}
标记 Bean
Spring 提供多种注解用于定义 Bean:
@Component
:通用注解,标记类为Spring Bean
,可用于各层,如Service、Dao
等,直接标注在类上即可。@Repository
:专用于Dao
层类,@Service
:用于Service
层类,@Controller
:用于Controller
层
.注意: 源码显示,
@Controller、@Service、@Repository
基于@Component
起名,对Spring IOC
管理无实质区别,仅为方便开发者区分组件作用。虽本质相同,但为保证代码可读性与结构严谨性,不可随意标注。
@Component
public class CommonComponent {}@Controller
public class XxxController {}@Repository
public class XxxDao {}@Service
public class XxxService {}
扫描 Bean
SpringBoot 主程序的
@SpringBootApplication
会自动扫描 Bean
SpringBootAplication
整合了以下注解
@SpringBootConfiguration
—— 表示这个类本身是个配置类@EnableAutoConfiguration
—— 自动加载其他的配置类@ComponentScan
默认是扫描当前类所在的包和子包
- 自定义一个
@ComponentScan(basePackages = "包1", "包2")
- 或者
@SpringBootApplication(scanBasePackages = "包1", "包2")
//@SpringBootConfiguration //表示这个类本身是个配置类
//@EnableAutoConfiguration //自动加载其他的配置类
//@ComponentScan //默认是扫描当前类所在的包和子包
@SpringBootApplication
public class Boot302DemoApplication {public static void main(String[] args) {var ioc = SpringApplication.run(Boot302DemoApplication.class, args);}}
第三方组件加入 IoC
容器
第一种方法:@Bean
第三方
jar
我们不可能直接在源码加@Component
所以需要用@Bean
标签
@Bean
:方法返回值作为Bean
类型:在使用@Bean注解
的方法中,方法的返回值类型决定了Bean
的类型。- 例如,如果方法返回一个
DataSource
类型的对象,那么这个Bean
在Spring
容器中的类型就是DataSource
。这个类型可以是 具体的类,也可以是 接口 或者 抽象类.
注意:@Bean
只能在配置类里面写。当然不仅是第三方 jar 普通的也行
@Configuration
public class AppConfig {//@Beanpublic FastsalException fastsalException() {return new FastsqlException();}
}
第二种方式:@Import
快速把类放入 IoC 容器 (第三方或者自己的类都可以)。常用于整合配置类等操作。
配置类放进 IoC 容器相比与配置类配置的所有组件都会放进 IoC 容器
也只能在配置类中使用
@Configuration
@Import(FastsalException.class) //快速把第三方地 FastsalException类放入 IoC 容器
public class AppConfig {}
Bean
的 BeanName
问题
@Component
的 BeanName
- 默认情况:和首字母小写的类名相同。 例如:
SoldierController
类对应的bean 的 id
就是soldierController
。- 自己指定:
@Controller( value = "BeanName")
@Bean
的 BeanName
- 默认情况:和方法名相同
- 自己指定:
@Bean("BeanName")
Bean
的初始化和销毁方法
我们可以在组件类中定义方法,然后当 IoC 容器 实例化和销毁组件 对象的时候进行自动调用!这两个方法我们称为为生命周期方法!
.
类似于Servlet的init/destroy
方法, 我们可以在周期方法完成初始化和释放资源等工作。
第一种:直接声明
//只要有 Bean 初始化和销毁了就执行这里的两个方法
public class BeanOne {//周期方法要求: 方法命名随意,但是要求方法必须是 public void 无形参列表@PostConstruct //注解制指定初始化方法public void init() {// 初始化逻辑}@PreDestroy //注解指定销毁方法public void cleanup() {// 释放资源逻辑}}
第二种:通过 @Bean
指定
public class BeanOne {public void init() {// initialization logic}
}public class BeanTwo {public void cleanup() {// destruction logic}
}@Configuration
public class AppConfig {@Bean(initMethod = "init")public BeanOne beanOne() {return new BeanOne();}@Bean(destroyMethod = "cleanup")public BeanTwo beanTwo() {return new BeanTwo();}
}
Bean
的单例和多例问题
取值 | 含义 | 创建对象的时机 | 默认值 |
---|---|---|---|
singleton | 在 IOC 容器中,这个 bean 的对象始终为单实例 | IOC 容器初始化时 | 是 |
prototype | 这个 bean 在 IOC 容器中有多个实例 | 获取 bean 时 | 否 |
第一种方式:全名声明
//ConfigurableBeanFactory.SCOPE_PROTOTYPE 就是多例
//单例模式就是只要实例化一个 bean 就执行一次
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 单例
@Component
public class JavaBean {}
第二种方式:简洁声明
@Scope("prototype") // 单例
@Component
public class JavaBean {}
两者区别
@Scope("prototype")
:
- 对于初学者或简单项目,使用字符串的方式可能更容易理解和书写,但是在大型项目中,如果作用域名称拼写错误(例如写成 “prototyp”),编译器不会给出错误提示,可能导致运行时问题。
,@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- 使用
ConfigurableBeanFactory.SCOPE_PROTOTYPE
常量可以避免拼写错误,因为 IDE 会在编译时检查该常量是否存在,同时也更符合 Java 编程的最佳实践,即使用常量而不是硬编码字符串。
Bean
依赖注入:引用类型自动装配(DI)
前提条件:参与自动装配的组件,无论是要被装配的还是提供装配的,都必须在
IoC
容器里,并且IoC
容器的实现方式(XML 配置或者注解方式)对此没有影响。
@Autowired
:按照类型装配
@Autowired 注解使用方法:可以直接在成员变量上标记 @Autowired 注解 来自动注入对象,在实际项目中我们通常采用这种方式。甚至第三方类放入容器后,也能使用此注解来实现自动装配。
原理:
- 在 ioc 容器中查找符合类型的组件对象
- 设置给当前属性(di)
@Autowired 标记的位置:成员变量,get方法, 构造器
- Bean
@Repository
public class UserDao {}@Service
public class UserService {private UserDao userDao;public UserDao getUserDao() {return userDao;}
}
@Autowired
注入
@Service
public class UserService {//这里相当于注入了 UserDao 类型的对象@Autowiredprivate UserDao userDao;public UserDao getUserDao() {return userDao;}
}
@Autowired
装配流程
·
@Qualifier
:指定 BeanName
装配
根据
@Qualifier
注解中指定的名称作为BeanName
进行匹配。需要配合@Autowired
@Repository
public class UserDao {private UserDao userDao;//匹配 BeanName 为 userDao 进行装配//value 可以省略@Autowired@Qualifier(value = "userDao")public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
@Resource
:多功能装配
- 如果没有指定
name
, 先根据 属性名 查找 IoC 中组件- 如果没有指定
name
, 并且属性名没有对应的组件, 会根据 属性类型 查找- 如果指定name名称查找。
Resource(name='test')
==@Autowired + @Qualifier(value='test')
Repository
public class UserDao {@Resource(name = "userDao")private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
@Bean
声明组件之间的装配
第一种方式:直接调用方法
如果其他组件也是
@Bean
方法 也可以 直接的调用方法这个本质上从IoC
容器获取组件
- POJO
public class Money {}public class People {private Money money;public void setMoney(Money money) {this.money = money;}
}
- 配置类
@SpringBootConfiguration
public class configuration1 {@Beanpublic Money money() {return new Money();}@Beanpublic People people() {People people = new People();//直接调用方法本质上从 ioc 容器获取组件people.setMoney(money());return people;}}
第二种方式:形参列表自动注入
形参列表声明自动注入
- 要求必须有对应的类型的组件, 如果没有抛异常
- 如果有多个同类型的 Bean 。可以使用 形参名称 = 对应的 beanName 的方式
- POJO
public class Money {}public class People {private Money money;public void setMoney(Money money) {this.money = money;}
}
- 配置类
@SpringBootConfiguration
public class configuration1 {@Bean //默认方法名就是BeanNamepublic Money money() {return new Money();}@Beanpublic People people(Money money) {People people = new People();people.setMoney(money);return people;}}
Bean
依赖注入:基本类型装配(DI)
第一种方式:@Value
直接绑定
@Value
是 Spring 框架中的一个注解,其主要作用是将外部配置文件中的具体值注入到由 Spring 管理的组件的字段中。这里所说的 Spring 管理的组件,通常是指带有@Component
注解的类。- 具体使用时,在字段上添加
@Value("${某个属性名}")
注解。当 Spring 应用启动时,它会自动从配置文件里查找与注解中指定属性名相对应的值,并将该值填充到使用@Value
注解标注的字段中。
.使用
- 情况1:
${key}
取外部配置 key 对应的值!- 情况2:
${key : defaultValue}
没有 key, 可以给与默认值
properties 配置文件
username=mangfu
gender=男
age=20
- 开始注入
@Component
//导入 properties配置文件
@Data
public class MyComponent {//注入@Value("${username}")private String name;@Value("${age}")private String age;@Value("${gender}")private String gender;}
第二种方式:@configurationProperteis
根据前缀绑定
@ConfigurationProperties
前提是要用
@Component
或者@Bean
把类加入 IoC 容器。并且有get set
方法
application.properties
pig.id=1
pig.name=佩奇
pig.age=5
@Component
方式
@Data
//必须加入 IoC 容器 @ConfigurationProperties(prefix = "pig") 才生效
@Component
//根据前缀为 pig 的自动匹配和 properties 一样的后缀然后赋值
@ConfigurationProperties(prefix = "pig")
public class Sheep {private Long id;private String name;private Integer age;}
@Bean
方式
@Configuration
public class AppConfig2 {@ConfigurationProperties(prefix = "pig")@Beanpublic Pig pig() {return new Pig();}
}
@EnableConfigurationProperties
Spring Boot 默认只扫描主程序所在包,导入第三方包时,即便组件标有
@Component
、@ConfigurationProperties
,也因扫描不到而无法完成属性绑定和注册到容器。
.使用
@EnableConfigurationProperties(Sheep.class)
可快速对指定类(如Sheep
类)进行属性绑定并注册到容器。
,
使用该注解需注意:
- 先使用
@ConfigurationProperties(prefix = " ")
绑定指定类。- 在配置类中使用。
Application.properties
sheep.id=1
sheep.name=苏西
sheep.age=5
POJO
//可以不用 @Component 加入 IoC 容器。等下 @EnableConfigurationProperties 会把它加入 IoC并赋值
@ConfigurationProperties(prefix = "sheep")
public class Sheep {private Long id;private String name;private Integer age;}
配置类
//这里就把 Sheep 加入 IoC 容器并且对应赋值了
@EnableConfigurationProperties(Sheep.class)
public class AppConfig2 {}
@ConditionalOnXXX
条件判断注解
如果注解指定的条件成立,则触发指定行为
@ConditionalOnClass
:如果类路径中存在这个类,则触发指定行为
@ConditionalOnMissingClass
:如果类路径中不存在这个类,则触发指定行为
@ConditionalOnBean
:如果容器中存在这个Bean(组件),则触发指定行为
@ConditionalOnMissingBean
:如果容器中不存在这个Bean(组件),则触发指定行为
@ConditionalOnBean(value=组件类型,name=组件名字)
:判断容器中是否有这个类型的组件,并且名字是指定的值.
如果是类级别就。类里所有操作都执行或不执行
举例
@SpringBootConfiguration
public class AppConfig {//类路径中存在这个类就创建这个 Bean @ConditionalOnClass(name="com.atguigu.boot.controller.HelloController.class")@Beanpublic Money money() {return new Money();}//类路径中不存在这个类就创建这个 Bean @ConditionalOnMissingClass(value ="com.atguigu.boot.controller.HelloController.class")@Beanpublic People person() {return new People();}
}
SpringBoot
获取 Bean
之前我们用的是
AnnotationConfigApplicationContext
。现在 主程序SpringApplication.run
返回的是ConfigurableApplicationContextConfigurableApplicationContext
接口类型
.
AnnotationConfigApplicationContext
是ConfigurableApplicationContextConfigurableApplicationContext
的具体实现类。所以相当于直接创建好了ApplicationContext
了我们直接获取IoC
中的容器就行
第一种方式:根据 BeanName
获取
直接根据
beanName
获取即可 返回值类型是Object
需要强转 【不推荐】
Object happyConponent = applicationContext.getBean("happyComponent");
第二种方式:根据 BeanName
同时定制 Class
HappyComponent happyComponent1 = applicationContext.getBean("happyComponent", HappyComponent.class);
第三种方式:直接根据类型获取
直接根据类型获取
条件1
:同一个类型, 在Ioc
容器中只能有一个Bean
,如果IoC
容器中存在多个同类型的Bean
会出现:NoUniqueBeanFinitionException
条件2
:IoC
的配置一定是实现类, 但是可以通过接口类型获取触发多态
.原理:根据类型来获取
Bean
时,在满足Bean
唯一性的前提下,其实只是看:【对象 instanceof 指定的类型】的返回结果,只要返回的是true
就可以认定为和类型匹配,能够获取到。
//A 是接口 HappyComponent 是实现类
A bean = applicationContext.getBean(A.class);
bean.doWork();