Spring框架主要解决了创建对象、管理对象的问题
在开发实践中,Spring框架的核心价值在于:开发者可以通过Spring框架提供的机制,将创建对象、管理对象的任务交给Spring来完成,以致于开发者不必再关心这些过程,当需要某个对象时,只需要通过Spring获取对象即可。 Spring框架也经常被称为 Spring容器
- Spring框架2大核心功能:
- IOC: 控制反转
- AOP: 面向切面编程
将对象的创建权交由Spring去管理,我们需要使用某个对象时,直接从Spring框架获取即可使用。
之前创建对象的步骤:
class A{}
class Test{
main:
A a = new A(); //由我们自己创建的
}
Spring框架中对象的创建:
对象不需要由我们创建,Spring框架已经帮我们创建好了所有的对象,
使用时直接从Spring框架获取对象使用即可。
class B{}
class Test{
main:
//不能这样做
//new B();
//正确的做法:
从Spring容器中获取B对象
}
模块目标:
- 定义Spring Bean
- 在Application Context中访问Bean
- Bean的配置
- 组件扫描和BeanId的命名规则
- 依赖注入Bean
- Bean的作用域
- 组合注解和元注解
定义Spring Bean
- 定义类,并将类交由Spring去管理,如何实现?
1. 显示配置Bean
使用2个注解配合使用,具体做法是:
1. 在项目定义配置类 -- 通过注解@Configuration类定义配置类
@Configuration作用于类上方,则表面这个类是一个配置类
2. 在配置类中定义Bean -- 告诉Spring容器需要去创建哪些Bean对象
判断Spring容器是否已经定义Bean对象的方式:从Spring容器获取该Bean对象
案例步骤:
1. 创建SpringBoot项目
2. 创建包:config用于放配置类 dao包用于保存一些定义的类
3. 创建类:在config包下创建配置类,类名自定义 在dao包下创建UserDao类
4. 配置Bean
在配置类上方添加注解@Configuration
在配置类中定义一个方法用于返回UserDao实例,在该方法上方添加注解@Bean
5. 测试Spring是否已经配置好了Bean对象
获取ApplicationContext对象,然后调用getBean方法获取UserDao实例,如果获取成功,表明Spring配置UserDaoBean成功
注意点:通过@Bean显示配置Bean对象,方法名即为Bean对象的id/name,通常我们叫做BeanId,BeanId是用于检索Bean对象的
通过@Bean装配Bean时,若需要注入其他Bean对象,可以使用参数注入Bean对象
@Beanpublic PersonService personService(PersonDao personDao){return new PersonServiceImpl(personDao);}
2. 隐式配置Bean
1. 通过在类上方添加组件类注解来表明某个类是组件,交由Spring去管理
组件:Spring中定义的类,通过在类上放添加注解来表明是组件
2.
组件类注解:
@Component 通用组件注解
@Controller / @RestController 控制器组件
@Service 业务层组件
@Repository 持久层/数据层组件
@Configuration 表示配置类组件
隐式配置的BeanId
1. 若类名是首字母大写,第二个字母小写,则beanId为类名首字母小写:
eg:class DogDaoImpl 对应的BeanId为dogDaoImpl
2. 否则,BeanId和类名保持一致。
eg:class DOgDaoImpl 对应的BeanId为DOgDaoImpl
隐式配置的BeanId也可以通过在注解@Component("BeanId") 指定BeanId
关于显示配置和隐式配置的选择:
在定义自己写的类时,建议使用隐式配置
若想将不是自己写的类装配成Bean时,建议使用显示配置
Spring中所有Bean的创建时机
Spring框架中,所有的Bean对象都是在Spring程序启动时实例化好的。
因为以上操作,造成的现象:启动Spring程序,启动时间会长一些
问题:为什么Spring不启动懒加载机制(即什么时候用某个Bean,再创建某个Bean对象)
如果在程序启动时实例化所有的Bean,这种方式程序启动时间会长,但是后续使用Bean时无需再创建Bean,所以使用过程中销量高。
如果采用懒加载方式,即程序启动时不实例化Bean,而是什么时候用,什么时候再实例化,这种方式会降低程序的使用效率。
从Spring容器中获取Bean对象
- ApplicationContext -- 应用程序上下文对象
该对象代表Spring容器,会存在于整个项目生命周期中。
所以可以通过该对象获取Spring容器中的所有Bean对象
- 如何获取ApplicationContext对象
ApplicationContext context = SpringApplication.run(...);
- 如何通过ApplicationContext对象获取某个Bean对象 -- 三种方式
1. 根据数据类型来获取某个Bean对象
UserDao dao = context.getBean(UserDao.class);
注意点:这种获取方式要求:该类型的Bean对象只有一个
2. 根据BeanId来获取Bean对象
StudentDao studentDao = (StudentDao) context.getBean("studentDao");
注意点:通过BeanId获取到的Bean对象类型为obj
etc,需要强转
3. 根据BeanId来获取Bean对象,可以指定数据类型,无需强转
UserDao userDao = context.getBean("userDao",UserDao.class);
Bean的作用域
- 作用域是Bean的属性,可以单独对某个Bean的作用域进行修改
- Spring容器配置Bean对象时,默认所有的Bean对象都是单例的。
- Spring容器中Bean的常见作用域:
singleton -- 单例 Spring容器默认的
prototype -- 原型 每次获取Bean对象,都会重新实例化
request -- 每个request对象代表一次请求,每发起一次请求,都会重新实例化用到的Bean对象
session -- 每次用户会话创建一个新的session对象,都会重新实例化用到的Bean对象
- 如何修改作用域:
- 通过注解@Scope来定义,用法为:
@Bean
@Scope("prototype")
public UserDao userDao(){
return new UserDao();
}
依赖注入
@Autowired注入
- 该注入是Spring框架提供的注解
- 默认是根据类型来匹配的
@Autowired的三种注入方式
1. 构造方法注入(推荐的做法) 必须存在唯一的匹配类型的依赖
@Autowired public CatServiceImpl(AnimalDao animalDao){
this.animalDao = animalDao;
}
2. set方法注入
@Autowired
public void setBearDao(BearDao bearDao){
this.bearDao = bearDao;
}
3. 字段注入
@Autowired
private CatDao catDao;
- @Autowired可能会产生歧义的问题
案例:
//可以注入的对象有多个
interface userDao{}
class userDaoImpl implements UserDao{}
class userDaoImpl1 implements UserDao{}
class UserServiceImpl 实现UserService{
@Autowired
private UserDao userDao;
}
- @Autowired消除歧义 -- 2种解决办法
- 1. 使用注解@Qualifier(适用于方法注入和属性注入,不适用于构造方法注入)
步骤:
定义一个UserDao接口,两个实现类
在UserServiceImpl中通过字段注入UserDao对象,看是否产生歧义
测试@Qualifier注解是否可以解决歧义
@Qualifier只能作用于set方法注入和字段注入
- 2. 解决歧义的第二种方法
-
@Autowired 若产生歧义,此时默认会自动根据 beanId 去找是否有对应的 Bean ,若有,则直接注入。
-