Spring系列文章3:基于注解方式依赖注入

和XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测 到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作,本质上所有操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行

一、负责声明bean的注解

1、5个声明bean的注解

Spring中使用以下5个注解声明一个bean

@Controller、@Service、@Repository、@Component、@Configuration

查看@Controller、@Service、@Repository三个注解源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {String value() default "";
}

可以看到只是在@Component注解的基础上起了三个新的名字,对于Spring使用IOC容器管理这些组件来说没有区别。Controller、Service、Repository三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。

虽然本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。 

  • @Controller:将类标识为控制层组件即用于对 Controller 实现类进行注解
  • @Service:将类标 识为业务层组件即对service层进行注解
  • @Repository:将类标识为持久层组件即用于对 DAO 实现类进行注解
  • @Component:将类标识为普通组件,当前对象不是持久层、业务层、控制层的时候最好使用component
  • @Configuration:如果一个Bean不在我们自己的package管理之内,例如ZoneId,如何创建它?使用@Configuration注解定义一个类,并在方法上标记一个@Bean注解

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识便于在其他地方引用。

使用注解后每个组件仍然应该有一个唯一标识

默认情况类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。

可通过标识组件的注解的value属性设置自定义的bean的id @Service("userService")


2、声明注解如何使用 

加⼊aop的依

我们可以看到当加⼊spring-context依赖之后会关联加⼊aop的依赖

第⼆步在配置⽂件中context命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"></beans>

在配置⽂件中定要描的包 

 扫描指定包下所有被注解标注的组件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--方式1:只指定具体的包--><context:component-scan base-package="com.springcode.example.controller"></context:component-scan><!--方式2:也可以多个包,逗号隔开--><context:component-scan base-package="com.springcode.example.controller,com.springcode.example.entity"></context:component-scan><!--方式3:指定共同的父包--><context:component-scan base-package="com.springcode.example"></context:component-scan>
</beans>

 排除指定组件

<context:component-scan base-package="com.demo">
<!-- context:exclude-filter标签:指定排除规则 --><!--type:设置排除或包含的依据type="annotation",根据注解排除,expression中设置要排除的注解的全类名type="assignable",根据类型排除,expression中设置要排除的类型的全类名--><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/><!--<context:exclude-filter type="assignable"expression="com.demo.controller.UserController"/>-->
</context:component-scan>

 仅扫描指定组件

<context:component-scan base-package="com.demo" use-default-filters="false">
<!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
<!--
type:设置排除或包含的依据
type="annotation",根据注解排除,expression中设置要排除的注解的全类名
type="assignable",根据类型排除,expression中设置要排除的类型的全类名
-->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable"
expression="com.demo.controller.UserController"/>-->
</context:component-scan>

Bean使⽤注解 

@Controller
public class UserController {
}

 编写试程序

public class SpringTest {@Testpublic void testFirst() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");UserController user   = applicationContext.getBean("userController", UserController.class);System.out.println(user);}
}

3、方法注解

 可以通过上面注解声明一个类作为一个bean,也可以声明一个方法作为一个bean,如下

@Configuration //也可以用@Component
public class MyBean {//使用bean注解声明一个方法返回值作为一个bean,bean的名字是方法名@Beanpublic User userInfo(){return new User();}
}

测试

public class SpringTest {@Testpublic void testFirst() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");User user   = applicationContext.getBean("userInfo", User.class);System.out.println(user);}
}

4、 @Configuration和@Component区别

如果一个Bean不在我们自己的package管理之内,例如ZoneId,如何创建它?使用@Configuration注解定义一个类,并在方法上标记一个@Bean注解:

@Configuration
public class AppConfig {// 创建一个Bean:@BeanZoneId createZoneId() {return ZoneId.of("Z");}
}

查看@Configuration源码本质是使用@Component进行元注解,因此 <context:component-scan/> 或者 @ComponentScan 都能处理@Configuration 注解的类。

Spring @Configuration 和 @Component 区别,一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

@Configuration
public class MyBeanConfig {@Beanpublic Country country(){return new Country();}@Beanpublic UserInfo userInfo(){return new UserInfo(country());}}

相信大多数人第一次看到上面 userInfo() 中调用 country() 时,会认为这里的 Country 和上面 @Bean 方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:

@Autowired
private Country country;

实际上不需要这么做(后面会给出需要这样做的场景),直接调用 country() 方法返回的是同一个实例。
但是@Component 注解并没有通过 cglib 来代理@Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country。

@Component
public class MyBeanConfig {@Beanpublic Country country(){return new Country();}@Beanpublic UserInfo userInfo(){return new UserInfo(country());}}

二、负责注入的注解

上面注解声明后,如何给Bean的属性赋值?Bean属性赋需要⽤到

@Value@Autowired@Qualifier@Resource

1、@value

对于简单属性注入可以使用@value赋值

@Component
public class User {@Value(value = "tom")private String name;@Value("18")private int age;public User(){System.out.println("无参构造被调用");}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}

测试

public class SpringTest {@Testpublic void testFirst() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");User user   = applicationContext.getBean("user", User.class);System.out.println(user);//User{name='tom', age=18}}
}

 通过代码可以发现我们并有给属性提供setter但仍然可以完成属性赋值。 如果提供settersetter@Value可以完成⼊吗

@Component
public class User {private String name;private int age;@Value(value = "tom")public void setName(String name) {this.name = name;}@Value("18")public void setAge(int age) {this.age = age;}public User(){System.out.println("无参构造被调用");}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}

通过测试可以得知@Value解可以直接使⽤属性上,也可以使⽤setter是可以的。都可以完成属性的赋值。

为了简化代码以后我们提供setter直接属性使⽤@Value解完成属性赋值。

是否能够通过完成 

@Component
public class User {private String name;private int age;public User(@Value("tome") String name,@Value("18") int age){this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}

通过测试得知@Value解可以出现属性setter以及构的形参可⻅Spring给我们提供了多样化的 

2、@Autowired与@Qualifier

使用@Autowired完成引用类型装配,单独使⽤@Autowired注解,默认根据类型装配。【默认是byType】

源码如下

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETE
R, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}

 源码理解1、该注解可以标注在哪⾥?

  • 构造⽅法上
  • ⽅法上
  • 形参上
  • 属性上
  • 注解上

源码理解2、该注解有⼀个required属性,默认值是true,表示在注⼊的时候要求被注⼊的Bean必须是 存在的,如果不存在则报错。如果required属性设置为false,表示注⼊的Bean存在或者不存在都没 关系,存在的话就注⼊,不存在的话,也不报错

1、在属性上使⽤@Autowired注解

在成员变量上直接标记@Autowired注解即可完成自动装配,如在controller中装配service层bean

@Controller
public class UserController {@Autowiredprivate UserService userService;public void saveUser(){userService.saveUser();}
}

2、可以标记在set方法上

@Service
public class UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

 

3、@Autowired注解还可以标记在构造器

@Controller
public class UserController {private UserService userService;@Autowiredpublic UserController(UserService userService){this.userService = userService;}public void saveUser(){userService.saveUser();}
}

4、标注在构造⽅法的形参上 

@Service
public class UserService {private UserDao userDao;public UserService(@Autowired UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

并且当有参数的构造⽅法只有⼀个时,@Autowired注解可以省略

@Service
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

 如果有多个构造⽅法,@Autowired肯定是不能省略的。如下测试会报错

@Service
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public UserService(){}public void save(){userDao.insert();}
}

@Autowired注解默认是byType进⾏注⼊的,也就是说根据类型注⼊的,如果以上程序中,UserDao接⼝ 还有另外⼀个实现类,会出现问题吗?

可以测试是不能装配的,提示UserDao这个Bean的数量⼤于1. 怎么解决这个问题呢?当然要byName,根据名称进⾏装配了。 @Autowired注解和@Qualifier注解联合起来才可以根据名称进⾏装配,在@Qualifier注解中指定Bean名 称。

@Service
public class UserService {private UserDao userDao;@Autowired@Qualifier("userDaoTwo") // 这个是bean的名字。public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

 @Autowired工作流程

首先根据所需要的组件类型到IOC容器中查找,唯一直接返回,存在多个

        没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为bean的id进行 匹配 能够找到:执行装配 找不到:装配失败

        使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配 能够找到:执行装配 找不到:装配失败 

@Autowired中有属性required,默认值为true,因此在自动装配无法找到相应的bean时,会装 配失败 可以将属性required的值设置为true,则表示能装就装,装不上就不装,此时自动装配的属性为 默认值

3、@Resource

@Resource注解也可以完成⾮简单类型注⼊。那它和@Autowired注解有什么区别?

@Resource注解是JDK扩展包中的,也就是说属于JDK的⼀部分。所以该注解是标准注解,更加具 有通⽤性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)

  • @Autowired注解是Spring框架⾃⼰的。
  • @Resource注解默认根据名称装配byName,未指定name时,使⽤属性名作为name。通过name 找不到的话会⾃动启动通过类型byType装配。
  • @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解⼀起 ⽤。
  • @Resource注解⽤在属性上、setter⽅法上。
  • @Autowired注解⽤在属性上、setter⽅法上、构造⽅法上、构造⽅法参数上。
  • @Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引⼊以下依赖:【如果是JDK8的话不需 要额外引⼊依赖。⾼于JDK11或低于JDK8需要引⼊以下依赖。】
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>

注意:如果你⽤Spring6,要知道Spring6不再⽀持JavaEE,它⽀持的是JakartaEE9。(Oracle 把JavaEE贡献给Apache了,Apache把JavaEE的名字改成JakartaEE了,⼤家之前所接触的所有的 javax.* 包名统⼀修改为 jakarta.*包名了。)

如果你是spring5-版本请使⽤这个依赖

<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
</dependency>

 @Resource注解的源码如下:

使用

@Service
public class UserService {@Resourceprivate UserDao userDao;public void save(){userDao.insert();}
}

 @Resource注解也可以使⽤在setter⽅法上

@Service
public class UserService {private UserDao userDao;@Resourcepublic void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

注意这个setter⽅法的⽅法名,setUserDao去掉set之后,将⾸字⺟变⼩写userDao,userDao就是name

当然,也可以指定name:

@Service
public class UserService {private UserDao userDao;@Resource(name = "userDaoForMySQL")public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}

 ⼀句话总结@Resource注解:默认byName注⼊,没有指定name时把属性名当做name,根据name找不 到时,才会byType注⼊。byType注⼊时,某种类型的Bean只能有⼀个。

三、全注解开发

所谓的全注解开发就是不再使⽤spring配置⽂件了。写⼀个配置类来代替配置⽂件。

@Configuration
@ComponentScan({"com.spring6demo.spring6.dao", "com.spring6demo.service"})
public class Spring6Configuration {
}

编写测试程序:不再new ClassPathXmlApplicationContext()对象了。

@Test
public void testNoXml(){ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);UserService userService = applicationContext.getBean("userService", Use
rService.class);userService.save();
}

四、注解和xml的对比

实际开发中以注解为主, xml为辅

五、IOC(依赖注入)使用总结

IOC解决的是业务逻辑对象之间的耦合关系,也就是service和dao之家的解耦合

spring容器适合管理对象

  • service层、dao层、controller对象、工具类对象

不适合管理管理对象

  • 实体类对象
  • servle、listener、filter等web中的对象,他们是tomcat创建和管理的

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

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

相关文章

<九> objectARX开发:读写Excel、json与txt格式文件

一、功能描述 在实际应用中,有时候我们需要通过文本格式或者表格等格式来传递数据,例如*.txt、Excel表格或者*.json文件。此时我们就需要想办法来读写这些数据。本节我们来说一下如何读写读写Excel、json与txt格式文件。 二、不同文件格式读写 2.1 读写Excel excel文件的读…

SpringBoot项目防止接口重复提交(简单拦截器实现方案)

基于SpringBoot框架来开发业务后台项目时&#xff0c;接口重复提交是一个常见的问题。为了避免这个问题&#xff0c;我们可以通过自定义拦截器实现一个后台拦截接口重复提交的功能&#xff0c;本文将介绍如何使用基于SpringBoot实现这个功能。 首先&#xff0c;我们需要引入一…

macOS - 安装使用 SQLite

文章目录 关于 SQLite安装 使用 关于 SQLite 官网&#xff1a;https://sqlite.org/index.html 安装 https://formulae.brew.sh/formula/sqlite brew install sqlite包被安装在了&#xff1a;/usr/local/Cellar/sqlite/3.43.0_1 查看已安装版本信息 $ brew info sqlite >…

【Hive-小文件合并】Hive外部分区表利用Insert overwrite的暴力方式进行小文件合并

这里我们直接用实例来讲解&#xff0c;Hive外部分区表有单分区多分区的不同情况&#xff0c;这里我们针对不同情况进行不同的方式处理。 利用overwrite合并单独日期的小文件 1、单分区 # 开启此表达式&#xff1a;(sample_date)?. set hive.support.quoted.identifiersnon…

CSS 中的 display 和 visibility

CSS 中的 display 和 visibility 都可以设置一个元素在浏览器中的显示或隐藏效果。 display: 隐藏某个元素时&#xff0c;不会占用任何空间。换句话讲&#xff0c;不会影响布局。visibility: 隐藏某个元素时&#xff0c;仍需占用与未隐藏之前一样的空间。换句话讲&#xff0c;…

在VR全景中嵌入3D模型有哪些优势?

现阶段&#xff0c;很多商企都会引入VR全景展示来宣传推广自己的产品、服务以及环境&#xff0c;但是环境展示凸显的沉浸式体验只是 VR全景一部分的价值所在&#xff0c;商企使用VR全景还有一个优势就是互动性&#xff0c;通过丰富多样的互动性&#xff0c;让用户同VR场景中的物…

Ab3d.DXEngine 6.0 Crack 2023

Ab3d.DXEngine 不是另一个游戏引擎&#xff08;如Unity&#xff09;&#xff0c;它强迫您使用其游戏编辑器、其架构&#xff0c;并且需要许多技巧和窍门才能在标准 .Net 应用程序中使用。Ab3d.DXEngine 是一个新的渲染引擎&#xff0c;它是从头开始构建的&#xff0c;旨在用于标…

汽车信息安全导图

尊敬的读者们,欢迎来到我的信息安全专栏。在这个专栏中,我将结合我在信息安全领域的开发经验,为大家深入浅出地讲解信息安全的重要性和相关知识点。 在数字化时代,信息成为了我们生活中不可或缺的一部分。我们的个人信息、交易数据、社交网络、公司机密等都以电子形式存储…

力扣(LeetCode)算法_C++——两个列表的最小索引总和

假设 Andy 和 Doris 想在晚餐时选择一家餐厅&#xff0c;并且他们都有一个表示最喜爱餐厅的列表&#xff0c;每个餐厅的名字用字符串表示。 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个&#xff0c;则输出所有答案并且不考虑顺序。 你可以假设答案…

vue和h5如何设置网页端和窗口大小同步缩放

在HTML文件中加入以下代码 <body style"transform-origin: top left; -moz-transform-origin: top left; font-family: Microsoft YaHei; width: 100%; height: 100%; margin: 0px; overflow: hidden; background-color: rgb(0,42,77);" οnresize"resize();…

Docker技术入门 | Part01:Docker简介

文章目录 1 虚拟化技术2 Docker概述2.1 Docker能解决的问题2.2 Docker介绍2.3 为什么使用Docker2.4 Docker特点2.5 Docker应用场景 3 Docker与虚拟机对比3.1 Docker和虚拟机组成结构3.2 Docker和虚拟机的不同点 4 Docker基本概念4.1 Docker引擎4.2 Docker基本架构4.3 Docker容器…

使用llvm 编译最新的linux 内核(LoongArch)

1. 准备交叉工具链 llvm 使用了最新的llvm-17, 编译方法见:编译LoongArch的llvm交叉工具链 gcc 从linux 官方下载&#xff1a;http://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/13.2.0/x86_64-gcc-13.2.0-nolibc-loongarch64-linux.tar.xz 发布llvm和g…

css中flex和flex-grow的区别

设置了1个class为parent且宽度为700px的div父级元素&#xff1b; 它有3个子元素&#xff0c;分别宽高为100px&#xff1b; 其中item2的元素flex值为1&#xff0c;item3的元素flex值为2 <!DOCTYPE html> <html lang"en"> <head><style>.pare…

glibc2.35-通过tls_dtor_list劫持exit执行流程

前言 glibc2.35删除了malloc_hook、free_hook以及realloc_hook&#xff0c;通过劫持这三个hook函数执行system已经不可行了。 传统堆漏洞利用是利用任意地址写改上上述几个hook从而执行system&#xff0c;在移除之后则需要找到同样只需要修改某个地址值并且能够造成程序流劫持…

【解决】多卡服务器GPU不能多用户同时使用的问题

一台多卡服务器&#xff0c;为提高利用效率&#xff0c;通常有多个用户使用。 假设有一台服务器A&#xff0c;分别有0&#xff0c;1&#xff0c;2&#xff0c;3四张卡&#xff0c;我们有两个用户&#xff1a;甲和乙。 当甲启动卡0时&#xff0c;乙想用卡1&#xff0c;2&#…

基于网络表示学习的 新闻推荐算法研究与系统实现

摘要 第1章绪论 新闻推荐通常是利用用户的阅读行为和习惯、阅读选择和爱好等信息,为 用户推荐新闻内容。新闻推荐能够减少用户在数量庞大数据信息中获取信息的 时间消耗,从而能够缓解“信息过载[7]”的难题。以文本为内容的新闻,和商品、 电影、短视频等推荐系统相比,新闻推…

Navicat使用HTTP通道服务器进行连接mysql数据库(超简单三分钟完成),centos安装nginx和php,docker安装nginx+php合并版

序言 因为数据库服务器在外网是不能直接连接访问的&#xff0c;但是可以访问网站&#xff0c;网站后台就能访问数据库&#xff0c;所以在此之前&#xff0c;访问数据库的数据是一件非常麻烦的事情&#xff0c;在平时和运维的交流中发现&#xff0c;他们会使用ssh通道进行连接访…

整车电子电器架构和自动驾驶架构的区别

整车电子电器架构和自动驾驶架构的区别&#xff1a; 范围不同&#xff1a; 整车电子电器架构是面向整车电子电器部件的通信、网络和诊断&#xff0c;包含车身、动力、底盘、娱乐和电器等系统&#xff1b;自动驾驶架构是面向智能网联的驾驶辅助功能或高度自动驾驶功能系统的功…

c语言练习41:深入理解字符串函数strlen strcpy strcat

深入理解字符串函数strlen strcpy strcat 模拟实现&#xff1a;”strlen strcpy strcat strlen strcat: #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<assert.h> strlen 1.通过指针移动模拟 //int my_strlen(char* str) { // size_t c…

记录--CSS 滚动驱动动画 scroll()

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 CSS 滚动驱动动画 scroll() animation-timeline 通过 scroll() 指定可滚动元素与滚动轴来为容器动画提供一个匿名的 scroll progress timeline. 通过元素在顶部和底部(或左边和右边)的滚动推进 scroll…