IOC
- 定义:
IOC: inverse of controll 控制反转
将对象的创建权交给Spring去管理
- 如何将一个bean交给Spring去管理
通过@Configuration和@Bean注解来管理
@Configuration和@Bean注解
- @Configuration
用于定义在类的上方,表明该类为配置类,在Spring框架启动时加载
- Bean
定义在方法的上方,表示该方法会返回一个Bean对象,每个Bean对象都会有自己的beanid
- 若在配置类中通过@Bean注解的方式来定义bean对象,此时@Bean方法的方法名即为beanid
- 应用:
public class UserRepository{
.....
}
public class UserService{....}
@Configuration
public class MyConfig{
@Bean
public UserRepository userRepository(){
return new UserRepository();
}
@Bean
public UserService userService(UserRepository userRepository){
}
}
注意点:@Bean作用的方法可以是无参的,可以是有参的,若有参,参数一定是Spring中的Bean对象,此时,会自动注入
- 如何获取某个bean对象
通过ApplicationContext(上下文对象)来获取某个bean对象
ApplicationContext对象
上下文对象,(Spring容器),Spring会管理一系列的Bean对象,若想获取某个Bean对象,则需要从ApplicationContext对象中获取.
使用场景:
1. Junit单元测试环境下可以使用该对象
2. Web应用程序环境
3. 独立的应用程序
注意点:ApplicationContext对象不论在哪种环境下使用,所有的bean实例均已实例化好
- 从ApplicationContext对象中获取Bean对象
//根据beanid获取Bean对象
UserRepository userRepository =
(UserRepository) applicationContext.getBean("userRepository");
//根据beanid获取Bean对象,但是不需要强转
UserRepository userRepository =
applicationContext.getBean("userRepository",UserRepository.class);
//根据类型获取Bean对象,适用于类型的实例只有一个时
UserRepository userRepository =
applicationContext.getBean(UserRepository.class);
Bean的作用域
Spring中所有的Bean默认的作用域是单例的
通过@Scope("val")的方式来设置bean的作用域
eg:
@Configuration
public class MyConfig{
@Bean
@Scope("prototype")
public T t(){
return new T();
}
}
Bean的作用域:
Singleton -- 单例
prototype -- 原型
-每次获取Bean对象,都会重新实例化一个新的Bean对象返回
request --仅限WEB环境
-每次请求均会重新创建实例
session --仅限web环境,session的作用域是一次会话(从浏览器打开到浏览器关闭)
-每创建一个session对象,会重新实例化
session的作用域是一次会话的原因:
在服务器端创建session对象后,每个session对象都有唯一的sessionId,而sessionId会保存在cookie对象中,下发给浏览器,客户端向访问session对象中的数据,必须携带sessionId去服务器端找session对象;而cookie对象默认的age值为-1,即表示cookie是保存在浏览器内存中的,那么浏览器关闭,对应的cookie对象会销毁,即sessionId会销毁,再次打开浏览器,就没有办法访问到之前的session对象,而浏览器从打开到关闭是一次会话.所以session对象的作用域为一次会话.
但是在服务器端session对象并不会立即销毁,而是从闲置开始的30min后自动销毁.
cookie对象的方法: setMaxAge(int) --设置cookie对象的保存时长
参数:
负数:都等同于-1,即cookie是保存在浏览器的内存的
正数:表示cookie对象保存到磁盘多少分钟
0:删除该cookie对象
配置好的Bean对象其实都可以看成是Spring的组件,所以Spring中配置Bean还可以使用组件隐式配置
Spring中提供了一系列组件类注解,在定义类的时候,在类上方添加上该注解后,即表示,这个类会成为Spring的组件,自动配置为Bean对象,无需后续再配置类中通过@Bean的方式来配置Bean对象
- Bean配置的2种方式
显示配置 -- @Bean
隐式配置 -- 组件类注解
- 组件类注解
Spring框架提供的组件类注解
@Component -- 通用组件
@Controller -- 控制器组件
@Service -- 业务层组件
@Repository -- 持久层组件
- Bean显示和隐式配置的使用场景
若是自己写的类,则使用组件类注解
若是将不是自己写的类定义成bean,必须在配置类中使用@Bean
eg:
连接数据库需要一个对象 DataSource
- 组件的名称
@Component
public class UserService{
}
以上bean的beanid为:
若类名首字母大写,第二位是小写的,则beanid为类名首字母小写后的名称 -- userService
若首字母大写,第二个也是大写的,此时beanid为类名
eg:class USerService --beanid为USerService
- 组件扫描
Spring框架要想把添加了组件注解的类装配成Bean对象,必须扫描这些注解,此时就需要组件扫描注解
注意:在SpringBoot项目中,启动类的注解中已经包含了@ComponentScan,已经定义了扫描的包范围,所以在SpringBoot项目中我们无需再次使用该注解,但是若使用的是Spring项目,则必须添加该注解
@ComponentScan如何指定扫描的包范围
@ComponentScan("cn.tedu.springtest")
@ComponentScan({"cn.tedu.controller","cn.tedu.service"})
-- 数组的形式指定扫描的包
依赖注入
依赖注入DI(Denpendcy Injection),在某个bean中,若要使用其中的bean对象,通过注解直接注入该对象
-
注入的注解
- @Autowired - 根据数据类型注入,是Spring提供的注解
1. 字段注入
@Autowired
UserRepository userRepository;
2. 构造方法注入
class T{
@Autowired
T(UserRepository userRepository){
}
}
注意:
1. 若类中只有一个构造方法,构造方法的参数列表中为Spring管理的bean对象,此时构造方法上方可以省略@Autowired注解,同样可以达到注入对象的效果
2. 构造方法注入中,参数列表中可以注入多个bean对象
3. setter注入 --- set方法注入
在Spring容器启动时,会自动调用set方法进行注入,set方法的参数列表中可以有一个参数,可以有多个参数,使用@Autowired注解可以注入以上对象.
- 自动布线和消除歧义
自动布线:使用@Autowired注解时,我们叫做自动布线,即自动装配
歧义的产生:当一个数据类型对应的bean实例超过一个时,此时会产生歧义
消除歧义:前提:一个数据类型的bean实例超过一个
1. 使用@Qualifier注解,指明注入的beanid,此时根据beanid进行注入
- @Autowired是根据数据类型进行注入的
-若不存在对应数据类型的Bean对象,则注入失败,编译不通过.
-若该类型的bean对象有多个,此时会根据beanid进行注入
2. 若没有@Qualifier注解,此时会自动根据类型后的名称来进行beanid匹配,若存在对应的beanid,则注入成功;若不存在对应的beanid,注入失败
- @Resource – 根据name/beanid进行注入的
@Resource注解不是Spring框架提供的,而是java提供的,是根据bean的名称/beanid注入对象的.
注入方式:首先默认根据beanid进行注入,若beanid不存在,则自动回退到根据数据类型进行注入
-若该类型不存在,则抛出运行时异常
-若该类型存在,且只有一个bean实例,注入成功
-若该类型存在,但是有多个bean实例,此时注入失败,除非使用@Qualifier指定beanid
@Resource注解可以指定name/beanid,若指定了name,此时只能根据name来注入,若不存在对应的name,则直接注入失败,不会回退到类型注入
@Resource注解的注入方式:只适用于setter注入和字段注入
~~~
- @Qualifier注解作用于构造方法和setter方法时的使用
~~~java
该注解必须写在字段之上或之前,不可以写在方法上
@Autowired
public void setUserService(@Qualifier("foo1") Foo foo){
- 注入方式
1. 构造方法注入
2. Setter注入
3. 字段注入(属性注入)
工厂模式 - 设计模式
Spring就是一个工厂,是Bean的工厂
Spring中的配置类是工厂,在配置类中,添加了@Bean注解的方法就是工厂方法
Spring的工厂接口FactoryBean
需要了解FactoryBean接口中的方法getObject()的调用
在Spring早期是通过xml配置的方式来进行开发的,若要对Bean进行复杂初始化,没有直接的方法,需要实现FactoryBean接口,重写其中的抽象方法getObject().在该方法内存完成复杂初始化,在Spring启动时会自动调用该方法,无需程序员调用.
还有重写getObjectType方法,返回bean的类型
该接口中还有一个方法isSingleton,返回当前实例是否为单例,从Spring5版本开始默认为单例
Bean的生命周期 – 面试热点
1. 初始化
1.加载bean
2.进入后处理bean--将所有的bean交给BeanFactoryPostProcessor(BFPP)
- BFPP经常用于自定义作用域和引用配置文件中的属性值
3.查找/创建依赖
4.实例化bean-->构造方法注入-->字段注入-->setter注入
5.Bpp(BeanPostProcessor) - beforeInit
6.调用初始化器
-Spring提供了一个生命周期中的注解@PostConstruct(setter注入之后执行初始化)
7.Bpp(BeanPostProcessor) - afterinit
2. 使用
不论是通过context获取bean对象还是通过注入的方式来获取bean对象,若在初始化过程中使用了Bpp,则得到bean对象为代理对象(对原对象进行了加工,之后执行的都是加工后的代码,新对象就叫做代理对象)
产生代理对象需要使用代理机制,Spring中Bean对象的创建会使用动态代理机制:
-JDK动态代理:使用JDK提供的api方法来生成代理对象的,要求目标对象一定实现了接口
-CGLIB动态代理:使用CGLib提供的api方法来生成代理对象的,要求目标对象没有实现接口
动态:在运行期执行的操作,叫做动态,而在运行期执行操作是通过反射来完成的
3. 销毁
在context对象关闭之前会销毁所有的bean对象,但是会先调用添加了@PreDestory注解的方法,然后才销毁bean实例
生命周期相关注解@PostConstruct和@PreDestory
以上两个注解均是用于添加在方法上方的
@PostContruct:表示方法在构造方法注入-->字段注入-->setter注入之后,执行该方法
@PreDestory:表示方法在bean销毁之前会被调用
注意点:
1.以上2个注解使用的前提:方法必须没有参数,且方法的返回值必须为void
2.以上2个注解是java提供的,而不是spring提供的
3.仅当应用程序正常关闭时会调用PreDestroy方法,如果进程意外终止或强制终止则不会调用。
也可以使用一下的方案替换以上2个注解:
@Bean(initMethod="populateCache", destroyMethod="flushCache")
public AccountRepository accountRepository() {
// ...
}
使用场景:
如果是自己的类,使用@PostConstruct/@PreDestroy
如果不是自己写的类,也无法添加注解,只能使用@Bean的属性