@Autowired和@Resource注解之间的关系区别,Bean的作用域和生命周期,Spring的执行流程

news/2024/10/23 7:31:42/

目录

一. @Autowired 和 @Resource 注解

二. Bean的作用域 

1. singleton(单例模式)

2. prototype(原型模式)(多例模式)

3. 请求作用域:request

4. 会话作用域:session

三. Spring 的执行流程

四. Bean 的生命周期 

 1. 实例化

 2. 设置属性 

3. Bean 初始化  

   3.1 执行各种各种 Aware 通知; 

   3.2 执行初始化前置方法;

   3.3 执行初始化方法;

   3.4 执行初始化后置方法; 

4. 使用 Bean 对象

5. 销毁 Bean 对象 

6. 代码演示 


一. @Autowired 和 @Resource 注解

在前面的文章中,我们介绍了通过 @Autowired 注解的方式来获取到Spring容器中的Bean对象,实际上,还有另一个注解:@Resource,它的功能也是差不多的,也可以从 Spring容器中获取到Bean对象,但也是存在一定的差别的。 

  1. @Resource 来自于 jdk,而 @Autowired 来自于 Spring;
  2. 使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如
    name 设置,根据名称获取 Bean;
  3. @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊;
  4. @Autowired在获取Bean对象的时候,先根据类型查找,之后再根据名称查找;而@Resource先根据名称查找,之后再根据类型查找;

问题分析:当在 Spring 中存放多个同一类型 Bean 对象的时候,使用 @Autowired 去获取 Bean对象会出错。

@Component
public class UserBeans {@Beanpublic User user1(){User user = new User();user.setAge(20);user.setId(1);user.setName("张三");return user;}@Beanpublic User user2(){User user = new User();user.setAge(22);user.setId(11);user.setName("李四");return user;}
}
@Controller
public class UserController2 {@Autowiredprivate User user;public void sayHi(){System.out.println("do UserController2");System.out.println(user.getName());}
}

public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserController2 userController2 = context.getBean("userController2",UserController2.class);userController2.sayHi();}
}

此时执行是会报错的,因为 @Autowired 注解先通过类型去查找,得到了 user1 和 user2 两个 Bean对象,但是根据 user 名称来去查找的话,是没有找到对应的 Bean 对象的,因此会报错。(查找 Bean 对象的默认名称为 添加 @Autowired 注解的对应属性名称,也就是上述第二段代码的private User user

 解决办法1:修改 @Autowired 注解的对应属性名称

@Controller
public class UserController2 {@Autowiredprivate User user1;public void sayHi(){System.out.println("do UserController2");System.out.println(user1.getName());}
}

此时运行代码就会获取到对应的 Bean对象,也就是存储的 user1 对象。

 

解决办法2:配合使⽤ @Qualifier 注解定义名称

@Qualifier(value = "Bean对象的名称") 

@Controller
public class UserController2 {@Autowired@Qualifier(value="user1")private User user;public void sayHi(){System.out.println("do UserController2");System.out.println(user.getName());}
}

也可以得到预期的结果。 

 

解决办法3:使⽤ @Resource(name="Bean对象名称") 定义

@Controller
public class UserController2 {@Resource(name="user1")private User user;public void sayHi(){System.out.println("do UserController2");System.out.println(user.getName());}
}

 同样是可以得到预期结果的。

 

二. Bean的作用域 

限定程序中变量的可⽤范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。而 Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种行为模式。

1. singleton(单例模式)

singleton 表示的是单例作用域,类似于之前讲过的单例模式。这也是默认情况下的行为模式。在该作用域下的 Bean 在 IoC容器中只存在一个实例,获取到的 Bean 以及对 Bean 进行修改,都是针对同一个 Bean 对象。

代码演示: 

1. 往 Spring 容器中注入一个 User 对象 

@Component
public class UserBeans {@Beanpublic User user(){User user = new User();user.setId(1);user.setName("张三");return user;}
}

2.  通过@Autowired获取到Bean对象,并对其进行修改

@Controller
public class UserController {@Autowiredprivate User user;public void printUser(){System.out.println(user);// 修改 UserUser myUser = user;     // 给引用对象赋值,其实就是共享对象!!!这两个变量指向了同一个地址myUser.setName("李四");System.out.println("myUser -> " + myUser);System.out.println("User -> " + user);  }
}

3. 再新建一个类,通过 @Autowired 获取到Bean对象 


@Controller
public class UserController2 {@Resourceprivate User user;public void printUser2(){System.out.println("user -> " + user);  // Bean 作用域 -> 默认是单例模式 = 此 Bean 在整个框架(Spring 容器)中只有一份}
}

 4. 最后输出观察结果


public class App {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController userController = context.getBean("userController",UserController.class);userController.printUser();UserController2 userController2 = context.getBean("userController2",UserController2.class);userController2.printUser2();}
}

因此也可以得出结论:singleton 单例作用域,他们使用的 Bean 都是 Spring 容器中的同一个对象。这个 Bean 在整个 Spring 中只有一份,是全局共享的,当其他人修改了这个值之后,那么另一个人读到的就是被修改后的值了。

2. prototype(原型模式)(多例模式)

 在原型模式下,每次对该作用域下的 Bean 的请求都会创建新的实例,也就是说每次获取 Bean 对象和修改 Bean 对象都是针对新的 Bean 对象实例而言的。

需要添加注解 @Scope("prototype") 或者@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

代码演示: 

@Component
public class UserBeans {@Bean@Scope("prototype")//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public User user(){User user = new User();user.setId(1);user.setName("张三");return user;}
}

 再次运行得到结果:

可以看出,在第二次通过 @Autowired 获取到 Bean 对象的时候,得到的是一个新的实例对象了。

3. 请求作用域:request

 每次 http 请求会创建新的 Bean 实例。适用于⼀次 http 的请求和响应的共享 Bean 的情况。

 限定SpringMVC中使⽤。

4. 会话作用域:session

在⼀个http session中,定义⼀个 Bean 实例。每次 Session 会话共享一个 Bean 对象。限定SpringMVC中使⽤。

 

三. Spring 的执行流程

 

 

四. Bean 的生命周期 

 1. 实例化

给 Bean 对象分配内存空间。 

此处要注意:初始化不等于实例化!!!

类的初始化是完成程序前的准备工作,在这个阶段,静态的会被执行,同时会开辟一块存储空间用来存放静态的数据,初始化只在类加载的时候执行一次,也可以理解为给对象赋值的过程。

类的实例化,是指创建一个对象的过程,这个过程会在堆中开辟内存,将一些非静态的方法,变量存放在里面,在程序执行的过程中,可以创建多个对象,即多次实例化,每次实例化都会开辟一块新的内存。 

 2. 设置属性 

看需求,有的 Bean 对象需要被注入属性,这个属性也会是已经在 Spring 容器中的 Bean,如果该属性还不存在于 Spring 中,那么会先去将该属性 Bean 存储到 Spring 中。 

3. Bean 初始化  

   3.1 执行各种各种 Aware 通知; 

实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、
ApplicationContextAware 的接⼝⽅法;

   3.2 执行初始化前置方法;

   3.3 执行初始化方法;

有两种方式,第一种是通过注解 @PostConstruct ,依赖注⼊操作之后执行注解修饰的方法;第二种是通过 xml 的方式,执行自己指定的 init-method ⽅法。如果两者都存在,那么先执行注解的初始化方法。

 

   3.4 执行初始化后置方法; 

 

4. 使用 Bean 对象

通过 getBean() 方法来获取容器中的 Bean 对象并使用。 

5. 销毁 Bean 对象 

销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method(XML的方式)。  

6. 代码演示 

public class App {public static void main(String[] args) {
// 此时要使用 ApplicationContext 的子类,因为它本身是没有销毁方法的ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");BeanComponent beanComponent = context.getBean("beanComponent",BeanComponent.class);beanComponent.sayHi();context.close();}
}

public class BeanComponent implements BeanNameAware {@Overridepublic void setBeanName(String s) {System.out.println("执行了通知 BeanName -> " + s);}/*** xml 方式的初始化方法*/public void myInit(){System.out.println("XML 方式初始化");}@PostConstructpublic void doPostConstruct(){System.out.println("注解的初始化方法");}public void sayHi(){System.out.println("执行 sayHi()");}// 销毁的方法,销毁的时候执行的一个方法@PreDestroypublic void doPreDestroy(){System.out.println("do PreDestroy");}
}

 

 


http://www.ppmy.cn/news/95220.html

相关文章

抖音seo源码--开源,支持二开不加密

抖音seo源码,抖音seo矩阵系统源码技术搭建,抖音seo源码技术开发思路梳理搭建 开发思路: 抖音seo源码如何搭建?抖音seo排名优化系统软件部分源码分析,代码打包中。。。 场景:在 python 中,你可…

一文带你了解MySQL之optimizer trace神器的功效

前言: 对于MySQL 5.6以及之前的版本来说,查询优化器就像是一个黑盒子一样,你只能通过EXPLAIN语句查看到最后优化器决定使用的执行计划,却无法知道它为什么做这个决策。这对于一部分喜欢刨根问底的小伙伴来说简直是灾难&#xff1…

二线程序员的出路

最近长沙不太平。去年被动离职一拨人之后,HR一直强调降本增效,人人自危,挤走一拨人,反正会有大量内卷失败的一线程序员进来填坑。当然留就有人走,前同事除了几个出去搞培训创业(后面解散了)的之…

云计算(二):负载均衡概述

云计算(二):负载均衡概述 负载均衡简介负载均衡的原理负载均衡的类型负载均衡的应用 负载均衡简介 负载均衡SLB(Server Load Balancer)是一种广泛应用于计算机网络中的技术,它可以将网络流量按需分发到多个…

[Nacos] Nacos Server处理心跳请求 (八)

文章目录 1.InstanceController#beat()1.1 serviceManager.registerInstance()1.2 serviceManager.getService()1.3 处理本次心跳 1.InstanceController#beat() CanDistroPutMapping("/beat")Secured(parser NamingResourceParser.class, action ActionTypes.WRITE…

Transformer网络原理与实战

Transformer网络原理与实战 1. 什么是Transformer网络2. Transformer网络原理2.1 自注意力机制2.2 多头自注意力机制2.3 Transformer网络的训练 3.Transformer网络实战 1. 什么是Transformer网络 Transformer网络是一种基于自注意力机制的神经网络,由Google于2017年…

算法基础学习笔记——⑧堆\哈希表

✨博主:命运之光 ✨专栏:算法基础学习 目录 ✨堆 🍓堆模板: ✨哈希表 🍓一般哈希模板: 🍓字符串哈希模板: 前言:算法学习笔记记录日常分享,需要的看哈O(…

安捷伦N5182A是德KEYSIGHT N5182B 100KHZ至3G/6G信号发生器

Agilent N5182A、Keysight N5182A MXG 射频矢量信号发生器,100 kHz - 3 GHz 或 6 GHz ​Keysight N5182A (Agilent) MXG 射频矢量信号发生器具有快速频率、幅度和波形切换、带电子衰减器的高功率和高可靠性 – 所有这些都集成在两个机架单元 (2RU) 中。Keysight N5…