目录
一、Bean注入
1.存储 Bean 对象
1.1 前置工作:配置扫描路径
注意:想要将对象成功的存储到 Spring 中,我们需要配置一下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。
//ApplicationContext就是Spring容器的顶级接口
//AnnotationConfigApplicationContext是其中的一个实现类,它的作用是:
//(1)扫描指定的包路径下,使用Spring框架注解的类
//(2)注册这些类到容器中=>框架帮我们new对象,及注入对象的依赖关系(属性赋值)
ApplicationContext context = new AnnotationConfigApplicationContext("org.example");
1.2 添加注解存储 Bean 对象
想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration
2. 方法注解:@Bean(此时,同一个类型,就可以注册多个Bean对象)
2.获取 Bean 对象(对象装配/注入)
2.1 属性注入(重点)
属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中。
调用关系:一般是,controller写一个方法(处理http请求) => 调用service的业务方法(业务逻辑处理)=> repository的方法(数据库的CRUD操作)
@Controller
@Data//lombok注解:会自动生成getter/setter/hashcode/equais/toString
public class UserController {
//需要让Spring容器,帮助我们将容器中的bean对象注入到这个属性
@Autowired
private UserService userService;
}
@Service
@Data
public class UserService {
//属性注入
@Autowired
private UserRepository userRepository;
}
@Repository
public class UserRepository {
}
2.2 构造注入
@Service
@Data
public class UserService {
private UserRepository userRepository;
//构造注入,和属性注入,作用是一样的
//本质是:要将UserService注册到容器中(实例化),是通过构造方法,传入容器中的userRepository对象来构造的
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
注意事项:如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。
一个类型,多个bean的注入方式:
// 同一个类型,多个bean的注入方式
//第一种方式:变量名=bean的id
@Autowired
private Bean对象2 testBean2_1;
//第二种方式:
@Resource(name = "testBean2_1")
private Bean对象2 bean对象2;
//第三种方式:
@Autowired
@Qualifier("testBean2_1")//qualifier可以指定bean的id/名称
private Bean对象2 bean对象2;
@Autowired 和 @Resource 的区别
- 出身不同:@Autowired 来自于 Spring,而 @Resource 来自于 JDK 的注解;
- 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如name 设置,根据名称获取 Bean。
2.3 Setter 注入
Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set 方法的时候需要加上 @Autowired 注
解。
private UserRepository userRepository;
//setter注入
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
2.4 三种注入优缺点分析
- 属性注入的优点是简洁,使用方便;缺点是只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)。
- 构造方法注入是 Spring 推荐的注入方式,它的缺点是如果有多个注入会显得比较臃肿,但出现这种情况你应该考虑一下当前类是否符合程序的单一职责的设计模式了,它的优点是通用性,在使用之前一定能把保证注入的类不为空。
- Setter 方式是 Spring 前期版本推荐的注入方式,但通用性不如构造方法,所有 Spring 现版本已经推荐使用构造方法注入的方式来进行类注入了。
二、作用域
1. 作用域定义
限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。
而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,那么当其他人修改了这个值之后,那么另一个人读取到的就是被修改的值。
2. Bean 的 6 种作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:
- singleton:单例作用域
- prototype:原型作用域(多例作用域)
- request:请求作用域
- session:回话作用域
- application:全局作用域
- websocket:HTTP WebSocket 作用域
2.1 singleton
- 描述:该作用域下的Bean在IoC容器中只存在一个实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同一个对象。
- 场景:通常无状态的Bean使用该作用域。无状态表示Bean对象的属性状态不需要更新
- 备注:Spring默认选择该作用域
//以下四个注解,都是类注解,用于注册Bean对象
//注册的方式,默认的Bean ID(名称),是类名首字母小写
@Controller
//@Service
//@Repository
//@Component
public class Bean对象1 {
public void sayHello(){
System.out.println("Hello");
}
}
2.2 prototype
- 描述:每次对该作用域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是新的对象实例。
- 场景:通常有状态的Bean使用该作用域
@Controller
@Scope("prototype")
public class Bean对象1 {
public void sayHello(){
System.out.println("Hello");
}
}
2.3 request
- 描述:每次http请求会创建新的Bean实例,类似于prototype
- 场景:一次http的请求和响应的共享Bean
- 备注:限定SpringMVC中使用
2.4 session
- 描述:在一个http session中,定义一个Bean实例
- 场景:用户回话的共享Bean, 比如:记录一个用户的登陆信息
- 备注:限定SpringMVC中使用
三、Bean的生命周期
所谓的生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期。
Bean 的生命周期分为以下 5 大部分:
1.实例化 Bean(为 Bean 分配内存空间)
类似我们new对象
2.设置属性(Bean 注入和装配)
依赖注入:属性 装配
3.Bean 初始化
表示我们要初始化很多内容,才能使用
- 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;(bean实现这些通知接口后,在这个时机,就会依次执行对应的接口方法)(依次执行Aware通知接口的方法)
- 执行 BeanPostProcessor 初始化前置方法;(BeanPostProcessor接口中的一个方法(XXXbeforeXXX))
- 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;(初始化方法)
- 执行自己指定的 init-method 方法(如果有指定的话);(初始化方法)
- 执行 BeanPostProcessor 初始化后置方法。(BeanPostProcessor接口中的一个方法(XXXafterXXX))
4.使用 Bean
5.销毁 Bean
销毁容器的各种方法,如 @PreDestroy、DisposableBean 接口方法、destroy-method。
前三个步骤完成,Bean才算注册成功。
- 创建好对象;
- 依赖注入(属性初始化);
- 其他初始化内容;
- 把Bean对象放入容器中。
package org.lifecycle.config;
import org.lifecycle.model.MyBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LifecycleConfig implements BeanPostProcessor {
@Bean(initMethod = "初始化方法2", destroyMethod = "销毁方法2")//initMethod的值,是对象中方法的名称
public MyBean bean(){
return new MyBean();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor生命周期:初始化前置方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor生命周期:初始化后置方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
package org.lifecycle.model;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//@Service
public class MyBean implements BeanNameAware,
BeanFactoryAware,
ApplicationContextAware,
InitializingBean,
DisposableBean {
public MyBean(){
System.out.println("Bean对象实例化");
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware生命周期方法");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware生命周期方法");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("ApplicationContextAware生命周期方法");
}
@PostConstruct
public void 初始化方法1(){
System.out.println("Bean的初始化方法:@PostConstruct");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean的初始化方法:InitializingBean");
}
public void 初始化方法2(){
System.out.println("Bean的初始化方法:@Bean(initMethod=本方法名)");
}
@PreDestroy
public void 销毁方法1(){
System.out.println("Bean的销毁方法:@PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("Bean的销毁方法:DisposableBean");
}
public void 销毁方法2(){
System.out.println("Bean的销毁方法:@Bean(destroyMethod=本方法名)");
}
}
package org.lifecycle;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Spring容器启动类_观察Bean生命周期 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("org.lifecycle");
//销毁容器,就会执行bean的销毁方法
context.close();
}
}
实例化和初始化的区别
实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可人工干预和修改;而初始化是给开发者提供的,可以在实例化之后,类加载完成之前进行自定义“事件”处理