【Spring】使用注解的方式获取Bean对象(对象装配)

目录

一、了解对象装配

1、属性注入

1.1、属性注入的优缺点分析

2、setter注入

 2.1、setter注入的优缺点分析

3、构造方法注入

 3.1、构造方法注入的优缺点

二、@Resource注解

三、综合练习


上一个博客中,我们了解了使用注解快速的将对象存储到Spring中,当然存储有简单方法,读取对象也可以使用注解的简单方法来实现,下面我们来了解一下,简单的获取对象的方法。

一、了解对象装配

获取Bean对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入。

对象装配(对象注入)的实现方法有下面的三种方式

  1. 属性注入:会根据属性的类型,在容器种查找并获取对象,然后赋值给类的成员变量。
  2. 构造方法注入:通过构造方法将对象注入到对象中。
  3. Setter注入:通过setXXX 方法将对象注入到类中。

1、属性注入

属性注入只需要在需要注入对象的属性上加上@Autowired或者@Resource注解即可,这两个注解存在什么样的区别,我们在后面分析,这里我们以@Autowired注解来举例说明。

1️⃣容器中同类型的对象只有一个:直接将获取到的对象注入到当前属性上。

这里我们想让UserService类种获取到UserRepository类的对象,我们就需要在UserService类种设置一个类型是UserRepository的属性,给这个属性添加上@Autowired。表示的意思为从容器种获取这个属性类型的对象注入给这个属性。

首先给UserRepository类上添加类注解,将Bean存放在容器中。

package com.java.demo.dao;import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public int add(){System.out.println("hello UserRepository");return 1;}
}

属性注入:将UserRepository在容器中的对象注入到UserService类中。

package com.java.demo.service;import com.java.demo.dao.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {//1.属性注入(DI:依赖注入)@Autowiredprivate UserRepository userRepository;public int add(){System.out.println("do UserService add method");return userRepository.add();}}

上面两步完成之后,我们就可以在测试类中,通过获取上下文对象(容器对象)来获取到userService对象(也就是依赖查找的方式获取到userService对象),然后调用userService对象的add方法,就可以观察到执行了UserRepository类的add方法,此时说明属性注入获取对象成功,我们并没有手动的new 这个userRepository对象。这里设置这个类只是用来检测属性注入(依赖注入)获取对象是否成功。

package com.java.demo.test;import com.java.demo.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserService userService = context.getBean("userService",UserService.class);userService.add();}
}

容器中同类型的对象只有一个的情况,属性注入的时候根据类型在Spring中查找,找到对象之后,直接复制给这个类型的属性,但是如果容器中同类型的对象存在多个的情况,就需要给属性指定注入那个对象。如果不指定这程序就会报错。

2️⃣容器中同类型的对象存在多个:如果获取到多个同类型对象,会根据属性的名字来进行匹配。

我们通过下面的例子来了解容器中存在多个同类型对象,在获取对象是没有指定给属性指定注入那个对象,程序出现的错误。

创建一个普通实体类User,然后再Users类中使用@Bean的方式向Spring中添加多个对象,然后再UserService类中使用属性注入的方式获取User类的对象注入到属性中。

package com.java.demo.model;public class User {private int id;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
}package com.java.demo.model;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;@Component
public class Users {@Bean("user1")public User user1(){User user = new User();user.setId(1);user.setName("张三");return user;}@Bean("user2")public User user2(){User user = new User();user.setId(2);user.setName("李四");return user;}
}package com.java.demo.service;import com.java.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService2 {@Autowiredprivate User user;public void sayHi(){System.out.println(user.toString());}
}package com.java.demo.test;import com.java.demo.service.UserService2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserService2 userService2 = context.getBean("userService2",UserService2.class);userService2.sayHi();}
}

 上述的代码中由于获取到多个同类型的对象,不能直接根据类型将对象直接注入到当前类的属性中,需要根据属性的名称,将获取到的对象注入到属性中,但是属性的名称和对象名不相同的情况下,属性注入就会失败。想要解决这个问题,就需要设置对象名与属性名匹配,下面是三种解决这个问题的方法。

✨解决方案

1️⃣将属性的名字和Bean的名字对应上即可。

2️⃣ 也可以通过@Autowired注解和@Qualifier注解搭配使用,设置@Qualifier的参数为获取到的对象中的任意一个对象名即可,@Qualifier表示的意思就是根据参数筛选对象。

3️⃣属性上直接添加@Resource注解,这个注解的参数中存在name属性,我们可以将name的值设置获取到的对象中的某一个对象的名字。

 

1.1、属性注入的优缺点分析

1️⃣优点

 属性注入的优点就是使用简单,直选哟添加一个@Autowired注解,就可以在不new对象的情况下,直接获取注入的对象了。

2️⃣缺点

🍂功能性问题:无法注入一个不可变的对象(被final修饰的对象),因为被final修饰的变量只能有两种初始化的方式,一种是直接复制,一种是通过构造方法赋值。就不能通过属性注入的方式获取到对象了。

 🍂通用性问题:属性注入只能在IoC容器的前提下使用,脱离了IoC容器就不能使用了。

🍂设计原则问题:更容易违背单一设计原则,通俗来说就是属性注入的方式简单,滥用的概率就会很大。举个例子比如一个页面中不仅有用户的信息,也有用户请求的资源信息,那么后端写代码的时候在数据持久层的用户类中,本来就是针对用户注入相关的依赖,但是由于为了让程序的效率更高,有可能会在这个类中注入一些其他的信息相关的依赖。


2、setter注入

使用setter注入,在setXXX方法上添加一个@Autowired或者@Resource注解即可,当然setter注入也存在和属性注入一样的容器中相同类型的对象个数问题。这个问题的解决方法也和上述的属性注入时说的方法一样。

package com.java.demo.dao;import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public int add(){System.out.println("hello UserRepository");return 1;}
}package com.java.demo.service;import com.java.demo.dao.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService3 {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public void sayHi(){System.out.println("hello UserService3 .");userRepository.add();}
}package com.java.demo.test;import com.java.demo.service.UserService3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserService3 userService3 = context.getBean("userService3",UserService3.class);userService3.sayHi();}
}

 2.1、setter注入的优缺点分析

1️⃣优点

setter更符合单一设计原则,因为那个setter方法,只是针对一个属性进程赋值。

2️⃣缺点

🍂无法注入一个final修饰的变量,因为final修饰的变量初始化的时候只有两种方式要么直接初始化,要么通过构造方法初始化。

🍂注入的对象可被修改。因为setXXX是一个方法,所以他就可能别调用,这个时候就会导致注入的Bean对象被修改了。


3、构造方法注入

使用构造方法注入,标准的写法在构造方法上添加一个@Autowired注解或者在构造方法上不加注解也可以实现注入效果。当然构造注入也存在和属性注入一样的容器中相同类型的对象个数问题。这个问题的解决方法也和上述的属性注入时说的方法一样。这里构造方法不支持使用@Resource注解。

1️⃣标准的写法构造方法上添加@Autowired注解

package com.java.demo.dao;import org.springframework.stereotype.Repository;@Repository
public class UserRepository {public int add(){System.out.println("hello UserRepository");return 1;}
}package com.java.demo.service;import com.java.demo.dao.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService4 {private UserRepository userRepository;@Autowiredpublic UserService4(UserRepository userRepository) {this.userRepository = userRepository;}public void sayHi(){System.out.println("hello userService4 .");userRepository.add();}
}package com.java.demo.test;import com.java.demo.service.UserService2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserService4 userService4 = context.getBean("userService4",UserService4.class);userService4.sayHi();}
}

 2️⃣不太标准的写法,构造方法上不加注解,这种写法是当前类中只有一个构造方法的时候可以使用的如果有多个构造方法的时候,构造方法上的@Autowired注解是不可以省略的。

 3.1、构造方法注入的优缺点

1️⃣优点

🍂可以注入不可变对象(被final修改的变量)

 🍂注入的对象不会被修改,因为构造方法会随着JVM的启动而被加载,只会被加载一次。就像上面的例子userRepositoryd对象注入到UserService4这个类中,这个类实例化被存入容器的时候,构造方法只会执行一次。

🍂构造方法注入,可以保证注入对象完全初始化,因为构造方法是在对象创建之前执行的。

🍂构造方法注入的通用性最好的,因为一个类中使用了构造方法注入,当你要使用这个类的对象的时候你就不得不给构造方法中的参数(当前类的属性)注入一个对象。

2️⃣缺点

🍂构造方法注入的写法比属性注入复杂

🍂构造方法注入的写法无法解决循环依赖的问题


二、@Resource注解

@Resource注解和@Autowired注解的用法是相同的,都可以注入对象。

 ✨@Resource和@Autowired的区别

1️⃣出生不同:@Autowired来自于Spring,而@Resource来自于JDK的注解;

2️⃣支持参数不同:@Autowired的参数只有一个,而@Ressource支持更多的参数设置。

 3️⃣使用上的区别@Autowired可用于Setter注入、构造方法注入和属性注入,但是@Resource只能用于Setter注入和属性注入,不支持使用构造方法注入

4️⃣idea兼容性支持不同:使用@Autowired在idea专业版下可能出现误报,@Resource不存在误报的问题。


三、综合练习

在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀ 个 User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使用伪代码即可。

这里需要注意的是,通过main方法获取Controller类,在main方法中不能使用依赖注入的方式获取对象,因为main方法为静态方法,静态方法的执行是在Spring框架之前的,所以我们需要使用依赖查找的方式获取容器对象,然后获取对象。

类的设置路径

 

package com.java.demo.model;public class User {private int id;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}}
package com.java.demo.dao;import com.java.demo.model.User;
import org.springframework.stereotype.Repository;@Repository
public class UserRepository {//伪代码public User getUser(){User user = new User();user.setId(1);user.setName("王五");return user;}}
package com.java.demo.service;import com.java.demo.dao.UserRepository;
import com.java.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUser(){return userRepository.getUser();}
}
package com.java.demo.controller;import com.java.demo.model.User;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate UserService userService;public User getUser(){return userService.getUser();}
}
package com.java.demo;import com.java.demo.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UserController userController = context.getBean("userController",UserController.class);System.out.println(userController.getUser());}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/25454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android:自己写一个简单记事本

一、前言:我的app是点击加号跳转到另一个界面 那么我遇到的问题的是点击加号是一个从一个Fragment跳转到另一个Fragment跳转失败。 二、解决方案: //相应控件的监听里面实现跳转FragmentManager fragmentManagergetFragmentManager();fragmentManager.b…

网络可靠性之链路聚合

网络的可靠性 网络的可靠性指当设备或者链路出现单点或者多点故障时保证网络服务不间断的能力网络的可靠性是可以从单板、设备、链路多个层面实现。 链路聚合 以太网链路聚合: 通过将多个物理接口捆绑成为一个逻辑接口,可以再不进行硬件升级的条件下&a…

css滚动条样式指南

css滚动条样式指南 滚动条是网页设计中经常被忽视的元素。虽然它看起来像是一个小细节,但它在网站导航中起着至关重要的作用。默认的滚动条可能看起来不合适,有损整体美观。本文将介绍如何使用 CSS 自定义滚动条。 在 Chrome、Edge 和 Safari 中设置滚…

【Rust 基础篇】Rust类函数宏:代码生成的魔法

导言 Rust是一门现代的、安全的系统级编程语言,它提供了丰富的元编程特性,其中类函数宏(Function-Like Macros)是其中之一。类函数宏允许开发者创建类似函数调用的宏,并在编译期间对代码进行生成和转换。在本篇博客中…

基于Azure OpenAI Service 的知识库搭建实验⼿册

1.概要 介绍如何使⽤Azure OpenAI Service 的嵌⼊技术,创建知识库;以及创建必要的资源组和资源,包括 Form Recognizer 资源和 Azure 翻译器资源。在创建问答机器⼈服务时,需要使⽤已部署模型的 Azure OpenAI 资源、已存在的…

SAP-MM-发票校验的重复校验功能

路径:SPRO-物料管理-后勤发票校验-收入发票-设置重复发票检查 按公司代码设置重复检查,可以按三个方式进行检查,公司代码、参照、发票日期,如果此处未维护就是按供应商(XK02)的六项进行检查 但是如果两处都…

MySQL 中的 distinct 和 group by 哪个效率更高?

在 MySQL 中,DISTINCT 和 GROUP BY 都是用来去除重复的数据,但它们的使用场景和性能影响是不同的。 有索引的情况下 group by 和 distinct 都能使用索引,效率相同。 无索引的情况下 distinct 效率高于 group by。原因是 distinct 和 group …

深入学习 Redis - 事务、实现原理、指令使用及场景

目录 一、Redis 事务 vs MySQL事务 二、Redis 事务的执行原理 2.1、执行原理 2.2、Redis 事务设计这么简单,为什么不涉及成 MySQL 那样强大呢? 三、Redis 事务的使用 3.1、使用场景 3.2、具体演示 开启/执行/放弃事务 watch 监控 watch 实现原理…

chapter14:springboot与安全

Spring Boot与安全视频 Spring Security, shiro等安全框架。主要功能是”认证“和”授权“,或者说是访问控制。 认证(Authentication)是建立在一个声明主体的过程(一个主体一般指用户,设备或一些可以在你的应用程序中…

nginx配置访问springboot服务

一、idea中可通过 clean package打包命令,打好包 ,比如:端口为8080,服务访问地址为/, 前端打包文件为dist,访端口为8000, 则可以这样配置nginx server {listen 8000;location / {root html/dist;…

ASP.NET Core SignalR

ASP.NET Core SignalR是一个开发实时网络应用程序的框架,它使用WebSocket作为传输协议,并提供了一种简单和高效的方式来实现实时双向通信。 SignalR使用了一种称为"Hub"的概念来管理连接和消息的传递。开发者可以编写自己的Hub类,…

Django之JWT库与SimpleJWT库的使用

Django之JWT库与SimpleJWT库的使用 JWTJWT概述头部(header)载荷(payload)签名(signature) Django使用JWT说明jwt库的使用安装依赖库配置settings.py文件配置urls.py文件创建视图配置权限 SimpleJWT库的使用安装SimpleJWT库配置Django项目配置路由创建用户接口测试身份认证自定义…

【雕爷学编程】Arduino动手做(190)---MAX4466声音模块

37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…

(五)Node.js -模块的加载机制

1. 优先从缓存中加载 模块在第一次加载后会被缓存。这意味着多次调用require()不会导致模块的代码被执行多次。 注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。 2. 内置模块的加载…

Jenkins Gerrit Trigger实践

1.创建Gerrit Trigger 2.jenkins master节点生成gerrit用户的密钥 这里的用户名得写登录gerrit后个人信息中的 Username 3.gerrit 配置刚刚jenkins生成密钥的公钥 4.gerrit 用户加入群组 不加这个群组,下一步测试就会报错“User aeshare has no capability conn…

通过VBA宏合并Excel工作表

工作中经常会用到的把几个Excel文件合并到一个,或者是把一个Excel文件里的所有Sheet合并到一个Sheet来进行统计。下面分别提供用vba宏来解决这两个问题的方法。 1、合并Excel文件 打开一个空Excel文件,AltF11,插入一个模块,开始…

Dueling Network

Dueling Network —— Dueling Network Architectures for Deep Reinforcement Learning 论文下载地址 论文介绍 图9. Dueling Network 模型结果示意图 Dueling Network与传统DQN的区别在于神经网络结构的不同,Dueling Netowrk在传统DQN的基础上只进行了微小的改动…

Flowise AI:用于构建LLM流的拖放UI

推荐:使用NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 什么是Flowise AI? Flowise AI是一个开源的UI可视化工具,用于帮助开发LangChain应用程序。在我们详细介绍 Flowise AI 之前,让我们快速定义 LangChain。LangChain是…

MapTR论文笔记

MAPTR: STRUCTURED MODELING AND LEARNING FOR ONLINE VECTORIZED HD MAP CONSTRUCTION 目的 传统高精地图 通过一些离线的基于 SLAM 的方法生成,需要复杂的流程以及高昂的维护费用。基于 bev 分割的建图方法,缺少向量化 实例级的信息,比如…

应急响应-web后门(中间件)的排查思路

0x01 获取当前网络架构 语言,数据库,中间件,系统环境等 0x02 分析思路 1.利用时间节点筛选日志行为 2.利用已知的漏洞在日志进行特征搜索,快速定位到目标ip等信息 3.后门查杀,获取后门信息,进一步定位目…