Spring6--基于注解管理Bean / 手写IOC

news/2024/4/27 17:59:53

1. 基于注入管理Bean概念

Java 5 引入了注解(Annotation)这一特性,它允许程序员在源代码中插入元数据,这些元数据以标签形式存在,可以被编译器、类加载器或运行时环境所识别和处理。注解可以帮助开发者在不修改业务逻辑的前提下,向代码中添加额外的描述性信息,比如标记服务、组件、属性或其他需要特定处理的部分。

注解(Annotation)是Java编程语言中的一种强大特性,它允许开发者在源代码级别上附加一种类似于注释的元数据(meta-data)。这种元数据并不影响程序本身的执行逻辑,但它能携带关于代码元素(如类、方法、变量等)的附加信息,这些信息可以被编译器、开发工具或运行时环境所解读和利用。

注解以@符号开头,后面跟注解的名称,有时还会包含一些参数值。例如,在Java中,@Override就是一个内建的注解,它用来标记一个方法是重写了父类或接口中的方法,编译器会根据这个注解检查是否真的存在这样的重写关系,如果不存在,则会产生编译错误。

除了像@Override这类编译时就发挥作用的注解,还有很多注解在程序运行时才起作用。例如,在Spring框架中,@Component、@Service、@Repository和@Controller等注解用于标记类为Spring容器管理的组件,而@Autowired注解则指示Spring自动进行依赖注入。

Spring Framework 自从 2.5 版本开始增强了对注解功能的支持,其中一个重要应用就是通过注解实现自动装配(Auto-wiring),从而大大简化了基于 XML 的配置方式。

举个例子:

想象一下你正在经营一家咖啡店,每个员工都有不同的职责,如吧台员负责制作咖啡,收银员负责结账等。在传统的管理模式中,你需要详细地编写一份工作手册,说明谁做什么工作,如何与其他角色配合。

现在,我们把这家咖啡店比作一个Java应用程序,每个员工看作是一个类或者对象,他们的职责则是类的方法。在没有注解的情况下,你需要类似XML配置文件这样的“工作手册”,明确指出哪类对象应该扮演哪个角色(如Barista类是吧台员,Cashier类是收银员)以及他们如何协同工作(如吧台员需要一个磨豆机实例来进行工作)。

引入注解后,就像在员工的衣服上贴标签一样,可以直接在代码里标明:“我是吧台员” (@Component + @Role("Barista")) 或者 "我需要磨豆机" (@Autowired private CoffeeGrinder grinder)。这样一来,系统(Spring框架)在运行时就能自动读取这些标签(注解),并依据注解信息完成相应的工作分配和协调,无需再查看那份详细的“工作手册”。

这样,注解使得我们的代码更加简洁清晰,同时大大减少了配置工作量,提高了开发效率和可维护性。


以下是使用注解实现 Spring 自动装配的基本步骤:

  1. 引入依赖
    在构建项目时,确保包含 Spring 相关注解处理器的依赖项,如 spring-contextspring-boot-starter,这将使 Spring 能够识别和处理注解。
  2. 开启组件扫描
    在 Spring 配置类或 XML 配置文件中启用组件扫描(Component Scan),这样 Spring 容器启动时会自动检测指定包及其子包下的类,寻找带有特定注解(如 @Component, @Service, @Repository, @Controller)的类并将它们作为 Bean 进行注册。
  3. 使用注解定义 Bean
    在需要由 Spring 管理的类上使用上述注解,表明它们是 Spring 容器中的 Bean。例如,通过在业务类上标注 @Service 注解,告诉 Spring 这是一个服务层的 Bean。
  4. 依赖注入
    利用注解进行依赖注入,如使用 @Autowired 注解来指示 Spring 自动查找并注入相应类型的 Bean。例如,在一个类的字段、构造器或方法参数上使用 @Autowired,Spring 将负责找到符合条件的 Bean 实例并注入到对应位置。

总结:

通过注解技术,Spring 可以自动发现、实例化和组装对象,减少手动编写配置的工作量,提高了代码的可读性和维护性。


2. 搭建子模块Spring6-ioc-annotation

创建一个子模块--Spring6-ioc-annotation

ta的父工程是之前的Spring6,这里不细说~

因为父工程里已经添加了相关依赖

如:Junit测试单元、log4j2日志、spring-context之类

所以这里子模块Spring6-ioc-annotation的pom文件中不需要添加

在resource资源文件夹下创建spring配置文件、log4j2日志文件

log4j2.xml文件注意这两个本地日志存储位置,根据实际情况来

<?xml version="1.0" encoding="UTF-8"?>
<configuration><loggers><!--level指定日志级别,从低到高的优先级:TRACE < DEBUG < INFO < WARN < ERROR < FATALtrace:追踪,是最低的日志级别,相当于追踪程序的执行debug:调试,一般在开发中,都将其设置为最低的日志级别info:信息,输出重要的信息,使用较多warn:警告,输出警告的信息error:错误,输出错误信息fatal:严重错误--><root level="DEBUG"><appender-ref ref="spring6log"/><appender-ref ref="RollingFile"/><appender-ref ref="log"/></root></loggers><appenders><!--输出日志信息到控制台--><console name="spring6log" target="SYSTEM_OUT"><!--控制日志输出的格式--><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/></console><!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--><File name="log" fileName="F:/Program/Spring6Log/test.log" append="false"><PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/></File><!-- 这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--><RollingFile name="RollingFile" fileName="F:/Program/Spring6Log/app.log"filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"><PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/><SizeBasedTriggeringPolicy size="50MB"/><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --><DefaultRolloverStrategy max="20"/></RollingFile></appenders>
</configuration>

结构如图


3. 开启组件扫描

Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 context:component-scan 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。

所以,我们需要配置bean.xml文件,来开启组件扫描


3.1. 配置bean.xml

bean.xml

新建时的初始样式

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

添加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/beans/contexts.xsd " ></beans>

给个建议

在Spring的bean.xml配置文件中添加新命名空间时,可以遵循以下步骤进行操作:

  1. 命名空间声明:
    在XML文档头部,使用xmlns:prefix="http://www.springframework.org/schema/namespace"格式声明新的命名空间,其中prefix是一个自定义的、用于引用该命名空间的别名,而namespace代表具体的Spring功能模块,例如“context”。
    例如:
xmlns:context="http://www.springframework.org/schema/context"

  1. schemaLocation属性配置:
    xsi:schemaLocation属性中,为新添加的命名空间指定对应的xsd文件位置,格式为http://www.springframework.org/schema/namespace location="xsd文件路径"
    例如:
xsi:schemaLocation="...http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"

综上所述,在bean.xml中添加Spring 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/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置内容 -->
</beans>

通过这种方式,你可以逐步熟悉并掌握在Spring配置文件中引入不同功能模块所需的命名空间,提高理解和实践能力。

个人建议是以后多手动练练,当然你要是觉得懒也可以直接复制下面的,给个建议哈

<?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 "></beans>

3.2. 开启组件扫描

在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 <beans> 中添加 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-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启组件扫描功能参数:base-package 指定需要扫描的包路径返回值:无功能描述:通过指定包路径,自动发现和注册符合特定注解(如@Component、@Service、@Repository等)的类,完成依赖注入。--><context:component-scan base-package="com.sakurapaid.spring6"></context:component-scan></beans>


组件扫描的几种设定情况

在Spring框架中,为了让Spring能够识别并自动管理那些使用注解标记的Bean,我们需在Spring的XML配置文件中通过 <context:component-scan> 标签启用组件扫描功能。具体来说:

  1. 情况一:最基本的扫描方式
<!-- 扫描指定包下的所有组件,自动注册为Spring Bean -->
<context:component-scan base-package="com.sakurapaid.spring6"></context:component-scan>
  • 第一个配置扫描com.sakurapaid.spring6包下所有的组件,没有任何过滤条件,所以会注册包下所有被Spring管理的组件。
  • 在这最基本的形式中,Spring会扫描指定的包"com.Sakurapaid.spring6"及其所有子包,查找标注了@Component、@Service、@Repository、@Controller等Spring注解的类,并将它们作为Bean注册到IoC容器中。

  1. 情况二:指定要排除的组件
<!-- 扫描指定包下除了注解为@Controller的组件外的所有组件 -->
<context:component-scan base-package="com.sakurapaid.spring6"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>
  • 第二个配置同样扫描com.sakurapaid.spring6包,但排除了所有被@Controller注解标记的组件。
  • 在此情况下,除了基础扫描外,我们还指定了一个排除规则,即排除所有标注了@Controller注解的类。这意味着在扫描过程中,Spring会忽略掉所有被@Controller注解的类,不会将其作为Bean注册到IoC容器中。

  1. 情况三:仅扫描指定组件
<!-- 扫描指定包下仅包含注解为@Controller的组件 -->
<context:component-scan base-package="com.sakurapaid" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>
  • 第三个配置扫描com.sakurapaid包,但只包含被@Controller注解标记的组件,且通过设置use-default-filters="false"禁用了默认的过滤器,只保留了显式定义的包含过滤器。
  • 在这一场景中,Spring默认的扫描行为被关闭(use-default-filters="false"),即不再扫描指定包及子包下的所有类。然后通过<context:include-filter>标签设置了自定义的扫描规则,只包含标注了@Controller注解的类。这样,只有满足此条件的类才会被扫描并注册为Spring容器中的Bean。

3.3. 使用注解定义Bean

在Spring框架中,当我们开发一个应用程序时,我们会有很多类,比如处理业务逻辑的类、连接数据库的类以及处理用户请求的类等等。为了让Spring框架能够管理和使用这些类,我们需要将它们注册到Spring的容器(IoC容器)中。就像我们要把各种工具都放在工具箱里以便随时取用一样,Spring也需要一个地方存放和管理这些类,这个地方就是IoC容器

Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean:

注解

说明

@Component

该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。

@Repository

该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

@Service

该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

@Controller

该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

  • @Component这个就像是一个通用标签,贴在任何一个你想让Spring管理的类上,告诉Spring:“嘿,这是一个重要的部分,你要关注它。”无论这个类是处理业务逻辑、数据库交互还是其他功能,只要贴上这个标签,Spring就会把它当作一个Bean(也就是一个可以在容器中被管理和使用的对象)来对待。
  • @Repository@Service @Controller:这三个注解其实是@Component的细分和增强,它们分别针对不同的应用场景:
    • @Repository专门用于标注数据访问层(DAO层)的类,比如数据库操作类。当Spring看到这个注解时,就知道这个类是用来处理数据库相关工作的。
    • @Service用于标注业务逻辑层(Service层)的类,这类类一般封装了复杂的业务处理逻辑。
    • @Controller在Spring MVC环境下使用,标注的是控制器层的类,这类类主要负责接收用户的HTTP请求,执行相应的业务逻辑,并返回响应结果。

尽管它们各自有特定的用途和语境,但从功能上讲,它们和@Component一样,都能使一个类成为Spring IoC容器中的Bean。这样做不仅简化了配置,同时也提高了代码的可读性和结构化程度。


举个代码栗子:

// 数据访问层(DAO层)
import org.springframework.stereotype.Repository;@Repository
public class UserRepository {// 假设这是一个简单的方法,用于从数据库获取用户信息public User getUserById(Long id) {// 实现数据库查询逻辑return new User(id, "username", "password");}
}// 业务逻辑层(Service层)
import org.springframework.stereotype.Service;@Service
public class UserService {private final UserRepository userRepository;// 构造器注入 UserRepositorypublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}// 业务方法,调用DAO层方法完成用户信息获取public User findUserById(Long id) {return userRepository.getUserById(id);}
}// 控制层(Spring MVC中的Controller)
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class UserController {private final UserService userService;// 构造器注入 UserServicepublic UserController(UserService userService) {this.userService = userService;}// 处理HTTP GET请求,根据用户ID获取用户信息@GetMapping("/users/{id}")@ResponseBodypublic User getUser(@PathVariable Long id) {return userService.findUserById(id);}
}

在以上代码中:

  • UserRepository类通过@Repository注解被标识为Spring容器中的一个Bean,Spring知道它是处理数据库相关工作的类。
  • UserService类通过@Service注解被标识为Spring容器中的一个Bean,它封装了业务逻辑,依赖于UserRepository
  • UserController类通过@Controller注解被标识为Spring容器中的一个Bean,它在Spring MVC环境中处理HTTP请求,并通过调用UserService完成业务逻辑,最终返回响应结果。

同时,由于我们在bean.xml或其他Spring配置文件中启用了组件扫描(如之前讨论的<context:component-scan>配置),Spring会自动发现并管理这些带有注解的类,无需手动在XML配置文件中逐一定义Bean。这样既简化了配置,也使得代码结构更为清晰和易于理解。


3.4. 实验一:@Autowired

@Autowired 是 Spring 框架提供的一种自动装配机制,它告诉 Spring 容器,某个 Bean(类实例)需要依赖其他的 Bean,并希望 Spring 能够自动找到并注入这些依赖。

查看源码:

package org.springframework.beans.factory.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {boolean required() default true;
}

第一部分

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)

在源码中的 @Target 注解列出了 @Autowired 可以使用的场景:

  1. 构造函数: 当你在一个类的构造函数上添加 @Autowired 时,Spring 会在创建这个类的实例时,根据构造函数参数的类型自动找寻并注入相应的 Bean。
  2. 方法: 如果你在某个方法上使用 @Autowired,Spring 会在 Bean 初始化后调用此方法,并给其参数注入对应的 Bean。
  3. 方法参数: 在方法的参数上使用 @Autowired,Spring 在调用该方法前会自动根据参数类型注入相应的 Bean。
  4. 类成员变量: 直接在类的成员变量(字段)上使用 @Autowired,Spring 会自动将匹配类型的 Bean 注入到这个字段中。
  5. 自定义注解: @Autowired 也可以应用在注解类型上,以便自定义具有类似自动装配功能的注解。

第二部分

public @interface Autowired {boolean required() default true;
}

@Autowired 注解有一个 required 属性,它的默认值是 true,意味着:

  • required=true:Spring 容器在处理自动装配时,必须找到一个与要注入的字段或方法参数类型匹配的 Bean。如果没有找到匹配的 Bean,Spring 会抛出异常,因为它认为这是必需的依赖。
  • required=false:在这种情况下,Spring 仍然会尝试找到匹配的 Bean 来进行注入,但如果没有找到合适的 Bean,它不会抛出异常,而是允许注入的字段或参数保持未注入的状态。这就意味着这个依赖项不是强制必须的。

所以,简单来讲,@Autowired 是 Spring 帮助我们自动连接不同组件(Bean)的一种方式,而 required 参数则用来决定是否对这种依赖关系做强制要求。


3.4.1. 属性注入

这前提是要有spring配置文件开启组件扫描

<?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-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启组件扫描,让Spring容器自动发现和装配标注了相应注解的Bean。参数:base-package:指定需要扫描的包路径,Spring会在此路径及其子包下查找组件。--><context:component-scan base-package="com.sakurapaid.spring6"/></beans>

创建UserDao接口

package com.sakurapaid.spring6.autowired.dao;public interface UserDao {public void print();
}

创建UserDaoImpl实现

package com.sakurapaid.spring6.autowired.dao;import org.springframework.stereotype.Repository;@Repository // 标示一个数据库访问层的实现类
public class UserDaoImpl implements UserDao {/*** 打印信息,表示Dao层操作已经结束。* 此方法没有参数。* 也没有返回值。*/@Overridepublic void print() {System.out.println("Dao层执行结束...");}
}

创建UserService接口

package com.sakurapaid.spring6.autowired.service;public interface UserService {public void out();
}

创建UserServiceImpl实现类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {@Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

创建UserController类

package com.sakurapaid.spring6.autowired.controller;import com.sakurapaid.spring6.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserController {@Autowired // 自动注入UserService实例,以便在控制器中使用private UserService userService;/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

测试输出,代码结构如图

package com.sakurapaid.spring6.autowired;import com.sakurapaid.spring6.autowired.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** UserTest 类用于通过单元测试方法测试 UserController 类的功能。*/
public class UserTest {/*** test 方法用于测试 UserController 类的实例是否能通过 Spring 上下文正确获取,并调用其方法。* 该方法没有参数。* 该方法没有返回值。*/@Testpublic void test() {// 创建一个 ClassPathXmlApplicationContext 实例,用来加载并使用 "bean.xml" 配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 通过上下文获取 UserController 类的实例UserController userController = context.getBean(UserController.class);// 调用 UserController 实例的 out 方法,进行测试userController.out();}
}


这是一个简单的Spring项目,其中包含三个主要部分:

  1. 服务层(Service Layer):
    UserServiceImpl类中,有一个UserDao类型的私有成员变量userDao,并使用了@Autowired注解。这意味着Spring框架会自动帮我们找到并设置合适的UserDao实例给这个变量。
  2. 控制层(Controller Layer):
    类似地,在UserController类中,有一个UserService类型的私有成员变量userService,同样使用了@Autowired注解。这样,Spring会自动把已创建好的UserService实例(即UserServiceImpl的一个实例)注入到这里。
  3. Spring容器初始化与注入:
    当通过ClassPathXmlApplicationContext加载Spring配置文件"bean.xml"时,Spring容器开始启动并管理这些Bean。它会识别出带有@Service@Controller注解的类,并创建相应的实例。同时,对于带有@Autowired注解的成员变量,Spring会根据类型自动寻找并注入相应的Bean。
  4. 测试过程:
    在测试类UserTest中,我们从Spring容器中获取UserController实例,并调用其out()方法。由于Spring已经完成了依赖注入,UserController里的userService已经有实际的UserService实现,所以可以顺利调用其方法。而在UserServiceout()方法里,又能顺利调用到已注入的UserDao的方法。

总结来说,属性注入就是Spring框架帮助我们将相互依赖的对象关联起来的过程,无需我们在代码中手动创建和组装这些依赖关系,降低了耦合度,提高了程序的灵活性和可维护性。


3.4.2. set注入

把@Autowired注解用在对应的set方法上,而不是在成员属性上

修改UserServiceImpl类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {/* @Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;*/private UserDao userDao; // 用户数据访问对象/*** 通过自动装配设置UserDao对象。* @param userDao 用户数据访问对象,用于进行用户数据的CRUD操作。*/@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

修改UserController类

package com.sakurapaid.spring6.autowired.controller;import com.sakurapaid.spring6.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserController {/*@Autowired // 自动注入UserService实例,以便在控制器中使用private UserService userService;*/// UserService的引用,用于进行用户相关的操作private UserService userService;/*** 通过@Autowired注解自动注入UserService实例。* * @param userService 要注入的UserService对象。*/@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

测试输出

注意:我把上一个代码测试的对象注释掉了,但重新运行测试代码能达到一样的效果,证明set起到了效果

package com.sakurapaid.spring6.autowired;import com.sakurapaid.spring6.autowired.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** UserTest 类用于通过单元测试方法测试 UserController 类的功能。*/
public class UserTest {/*** test 方法用于测试 UserController 类的实例是否能通过 Spring 上下文正确获取,并调用其方法。* 该方法没有参数。* 该方法没有返回值。*/@Testpublic void test() {// 创建一个 ClassPathXmlApplicationContext 实例,用来加载并使用 "bean.xml" 配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 通过上下文获取 UserController 类的实例UserController userController = context.getBean(UserController.class);// 调用 UserController 实例的 out 方法,进行测试userController.out();}
}

@Autowired注解从成员变量移到了对应的setter方法上,这种做法称为setter注入。Spring框架依然利用依赖注入(Dependency Injection, DI)的机制来管理Bean之间的依赖关系。

下面是setter注入的原理过程:

  1. Spring容器初始化
    当创建ClassPathXmlApplicationContext并加载"bean.xml"配置文件时,Spring容器开始初始化并管理所有被标记为@Service@Controller等注解的Bean。
  2. 扫描Bean及其依赖
    Spring容器会识别出UserServiceImplUserController类上的注解,并准备创建这两个Bean的实例。同时,Spring会检查这些类中是否存在带@Autowired注解的setter方法。
  3. 依赖注入
    对于UserServiceImpl,Spring发现setUserDao方法上有@Autowired注解,就会从容器中查找类型匹配的UserDao Bean,找到后调用setUserDao方法,将UserDao实例注入给UserServiceImpl
    同理,对于UserController,Spring会找到并调用setUserService方法,将已创建的UserService实例(也就是UserServiceImpl实例)注入给UserController
  4. 测试阶段
    在测试类UserTest中,我们从Spring容器获取UserController实例。由于Spring已经在容器初始化阶段完成了对UserControllerUserService的依赖注入,所以在调用userController.out()时,userService已经具备了完整的功能,可以正常调用UserServiceImpl中的out()方法,并进一步调用UserDao的方法。

总的来说,无论是成员变量注入还是setter方法注入,Spring都遵循同样的依赖注入原则,即由容器负责管理Bean的生命周期和依赖关系,从而降低模块间的耦合度,提高系统的可测试性和可维护性。在本例中,通过setter方法注入的方式,仍然实现了相同的效果。


3.4.3. 构造方法注入

将@Autowired放在了构造方法上

修改UserServiceImpl类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {/* @Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;*//*private UserDao userDao; // 用户数据访问对象@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}*/private UserDao userDao;@Autowiredpublic UserServiceImpl(UserDao userDao) {this.userDao = userDao;}/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

修改UserController类

package com.sakurapaid.spring6.autowired.controller;import com.sakurapaid.spring6.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserController {/*@Autowired // 自动注入UserService实例,以便在控制器中使用private UserService userService;*/// UserService的引用,用于进行用户相关的操作/*private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}*/private UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

测试输出

测试代码是一样的,这里就不重复了,直接放结果


构造方法注入是Spring框架依赖注入的另一种方式,它通过在类的构造函数上使用@Autowired注解来完成依赖对象的注入。以下是构造方法注入的原理过程:

  1. Spring容器初始化
    当创建ClassPathXmlApplicationContext并加载配置文件时,Spring容器开始扫描并管理所有的Bean。
  2. 识别构造器注入
    Spring容器在创建UserServiceImpl Bean时,会发现其构造函数上有@Autowired注解,这意味着Spring需要通过此构造函数来初始化UserServiceImpl实例,并注入所需的UserDao依赖。
  3. 注入依赖
    Spring会在IoC容器中查找类型匹配的UserDao Bean,找到后将其实例作为参数传入UserServiceImpl的构造函数,从而完成对UserDao依赖的注入。
    同样地,在创建UserController Bean时,Spring发现其构造函数上有@Autowired注解,此时Spring会查找并注入已经初始化好的UserService Bean。
  4. 测试阶段
    测试类UserTest从Spring容器获取UserController实例时,由于Spring已在初始化阶段通过构造方法注入的方式完成了对UserControllerUserService的依赖设置,所以调用userController.out()时,userService能够正确地执行业务逻辑。

总结来说,构造方法注入是在Bean实例化阶段通过构造函数一次性注入所有必需的依赖,相比于成员变量注入和setter方法注入,构造方法注入确保了对象在实例化后即可拥有完整的功能,增强了对象的即时可用性和一致性。在上述代码中,无论是在UserServiceImpl还是UserController类中,都是通过构造方法注入的方式来完成依赖注入的。


3.4.4. 形参上注入

@Autowired 也可以用在形参上

修改UserServiceImpl类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {/* @Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;*//*private UserDao userDao; // 用户数据访问对象@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}*/private UserDao userDao;public UserServiceImpl(@Autowired UserDao userDao) {this.userDao = userDao;}/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

修改UserController类

package com.sakurapaid.spring6.autowired.controller;import com.sakurapaid.spring6.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserController {/*@Autowired // 自动注入UserService实例,以便在控制器中使用private UserService userService;*/// UserService的引用,用于进行用户相关的操作/*private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}*/private UserService userService;public UserController(@Autowired UserService userService) {this.userService = userService;}/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

测试输出


形参注入的优势在于,它可以在创建对象的同时确保依赖对象的有效性,有助于保持对象的完整性,并且可以通过构造函数强制要求必须提供所需依赖,增强了代码的清晰性和安全性。

这种方式下,@Autowired注解直接放在了构造函数的参数上。以下是形参注入的原理过程:

  1. Spring容器初始化
    当Spring容器加载配置并准备创建UserController Bean时,它会检测到类的构造函数中有带有@Autowired注解的参数。
  2. 依赖查找与注入
    Spring会查找IoC容器中类型匹配的UserService Bean。一旦找到匹配的Bean,Spring会将该Bean实例作为参数传递给UserController的构造函数,进而完成对UserService依赖的注入。
  3. 对象实例化
    通过构造函数注入依赖,Spring容器在创建UserController实例的同时完成了所有必要的依赖初始化。因此,新创建的UserController实例就已经具有了完整功能,可以直接调用UserService的方法。
  4. 测试阶段
    在测试类中,从Spring容器获取到的UserController实例,其内部的userService成员变量已经被正确注入,因此可以成功执行out()方法,依次调用UserServiceUserDao的相关方法,并最终在控制台输出预期的结果。

3.4.5. 只有一个构造函数,无注解

当有参数的构造方法只有一个时,@Autowired注解可以省略

修改UserServiceImpl类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {/* @Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;*//*private UserDao userDao; // 用户数据访问对象@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}*//*private UserDao userDao;public UserServiceImpl(@Autowired UserDao userDao) {this.userDao = userDao;}*/private UserDao userDao;public UserServiceImpl( UserDao userDao) {this.userDao = userDao;}/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

修改UserController类

package com.sakurapaid.spring6.autowired.controller;import com.sakurapaid.spring6.autowired.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserController {/*@Autowired // 自动注入UserService实例,以便在控制器中使用private UserService userService;*/// UserService的引用,用于进行用户相关的操作/*private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}*//*private UserService userService;public UserController(@Autowired UserService userService) {this.userService = userService;}*/private UserService userService;public UserController( UserService userService) {this.userService = userService;}/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

测试输出


上面的操作是移除了构造函数参数上的@Autowired注解。尽管如此,Spring依然能够进行依赖注入,这是因为:

  1. Spring容器初始化
    当Spring容器加载配置并初始化Bean时,它会查找所有带有@Controller等注解的类以创建Bean实例。
  2. 自动装配
    即使没有在构造函数参数上明确使用@Autowired注解,Spring也能基于类型匹配进行自动装配。当Spring容器创建UserController Bean时,它会发现UserController构造函数需要一个UserService类型的参数。此时,Spring会在IoC容器中寻找类型匹配的UserService Bean。
  3. 依赖注入
    若Spring找到了唯一的UserService Bean,则会将该Bean实例注入到UserController的构造函数中,从而完成依赖注入。
  4. 对象实例化与测试
    构造函数注入依然有效,创建出来的UserController实例已经包含了完整的UserService依赖。因此,在测试阶段,可以从Spring容器获取到具有完全功能的UserController实例,并能成功调用UserServiceout()方法。

需要注意的是,虽然在本例中移除@Autowired注解不影响依赖注入,但如果存在多个同类型候选Bean时,Spring无法确定具体要注入哪个Bean,这时就需要恢复使用@Autowired注解配合其他策略(如@Qualifier注解)来指定确切的Bean。在只有一个匹配Bean的情况下,默认的类型匹配足以完成注入任务。

说明:有多个构造方法时呢?大家可以测试(再添加一个无参构造函数),测试报错


3.4.6. @Autowired@Qualifier联合注解

添加dao层实现--UserDaoRedisImpl

package com.sakurapaid.spring6.autowired.dao;import org.springframework.stereotype.Repository;@Repository
public class UserDaoRedisImpl implements UserDao {@Overridepublic void print() {System.out.println("Redis Dao层执行结束");}
}

此时测试输出会报错,报错提示很长我截取关键的

因为@Autowired 默认是按照ByType,按照类型进行装配

所以这里需要byName,根据名称进行装配了。

修改UserServiceImpl类

package com.sakurapaid.spring6.autowired.service;import com.sakurapaid.spring6.autowired.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {/* @Autowired // 自动注入UserDao,以便于执行数据库操作private UserDao userDao;*//*private UserDao userDao; // 用户数据访问对象@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}*//*private UserDao userDao;public UserServiceImpl(@Autowired UserDao userDao) {this.userDao = userDao;}*/@Autowired//value值默认小写@Qualifier(value = "userDaoRedisImpl")private UserDao userDao;/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

测试输出


通过@Autowired@Qualifier注解组合使用,我们可以更精确地控制依赖注入的行为,使其不仅依据类型匹配,还能根据Bean的名称进行精准注入,解决了可能存在多个同类型候选Bean时的注入问题。在本例中,Spring会根据名称"userDaoRedisImpl"找到并注入对应的UserDao实现类。

使用了@Autowired结合@Qualifier注解来进行按名称装配(ByName)的依赖注入。原理过程如下:

  1. Spring容器初始化
    当Spring容器加载配置并初始化Bean时,它会扫描带有@Service等注解的类,准备创建UserServiceImpl Bean。
  2. 按名称装配
    UserServiceImpl类中,UserDao成员变量同时使用了@Autowired@Qualifier注解。@Autowired注解告诉Spring需要自动装配一个UserDao类型的依赖,而@Qualifier(value = "userDaoRedisImpl")则指定了具体的Bean名称,要求Spring根据名称去查找并注入对应的UserDao实现类。
  3. 依赖查找与注入
    Spring容器在IoC容器中查找名称为"userDaoRedisImpl"的UserDao Bean。如果找到,则将该Bean实例注入到UserServiceImpluserDao属性上。
  4. 测试阶段
    当从Spring容器获取UserServiceImpl实例时,其内部的userDao属性已经被注入了指定名称的UserDao实现类实例。因此,在调用out()方法时,会调用到指定名称所对应的具体实现类的方法。

3.4.7. 总结

场景一:属性注入

  • 在类的成员变量上直接使用@Autowired注解,Spring会自动查找并注入与该成员变量类型匹配的Bean。
@Autowired
private UserDao userDao;

场景二:set注入

  • 在setter方法上使用@Autowired注解,Spring会在Bean实例化后调用该方法,注入匹配类型的Bean。
@Autowired
public void setUserDao(UserDao userDao) {this.userDao = userDao;
}

场景三:构造方法注入

  • 在类的构造函数上使用@Autowired注解,Spring会在创建Bean实例时通过构造函数注入依赖。
@Autowired
public UserServiceImpl(UserDao userDao) {this.userDao = userDao;
}

场景四:形参上注入

  • 这与场景三类似,只是注解直接放在构造函数的参数上,Spring会根据类型自动注入。
public UserController(@Autowired UserService userService) {this.userService = userService;
}

场景五:只有一个构造函数,无注解

  • 如果类中只有一个无参或有参构造函数,且无任何注解,Spring在某些情况下仍能通过类型匹配自动注入依赖(前提是IoC容器中只有一个匹配类型的Bean)。

场景六:@Autowired注解和@Qualifier注解联合

  • 当存在多个同类型候选Bean时,单独使用@Autowired可能会导致不确定注入哪一个Bean。此时配合@Qualifier注解,通过指定Bean的名称来精准注入。
@Autowired
@Qualifier(value = "userDaoRedisImpl")
private UserDao userDao;

通过以上场景,我们可以看出@Autowired注解在不同位置和场景下的作用,主要是帮助Spring容器自动管理和注入Bean的依赖关系,实现低耦合、高内聚的设计目标。同时,结合@Qualifier注解能够更精细地控制注入行为,解决多实例选择问题。


3.5. 实验二:@Resource注入

  1. @Resource注解 :
    • 来源于Java EE规范(JSR-250),在Java EE环境中通常无需额外引入依赖就能使用,但在非Java EE环境如Java SE中,需添加如上所述的依赖包。
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>
    • 默认注入策略为按名称(byName)。若在注解中未指定name属性,它会尝试匹配字段名或setter方法名作为Bean的名称进行注入;若无法按名称找到匹配的Bean,则会尝试按类型(byType)进行注入。
    • 通常应用于字段或setter方法上,例如:
@Resource(name = "userDaoRedisImpl")
private UserDao userDao;

  1. @Autowired注解 :
    • 是Spring框架提供的注解,专门用于Spring环境下的依赖注入。
    • 默认注入策略为按类型(byType),即Spring容器会查找并注入与被注解字段或方法参数类型相匹配的Bean。若存在多个相同类型的候选Bean,则需要配合@Qualifier注解明确指定Bean的名称。
    • 可以应用于字段、setter方法、构造方法以及构造方法参数上,例如:
@Autowired
private UserService userService;// 或者配合@Qualifier
@Autowired
@Qualifier("userServiceDBImpl")
private UserService userService;

简而言之,@Resource@Autowired都能实现依赖注入,但默认的注入策略不同。@Resource倾向于按名称注入,而@Autowired倾向于按类型注入。在具体使用时,开发者可以根据项目的实际情况和需求来选择合适的注入方式。


3.5.1. 根据name注入

项目结构

修改UserDaoImpl类

package com.sakurapaid.spring6.resource.dao;import org.springframework.stereotype.Repository;@Repository("myUserDao") // 标示一个数据库访问层的实现类
public class UserDaoImpl implements UserDao {/*** 打印信息,表示Dao层操作已经结束。* 此方法没有参数。* 也没有返回值。*/@Overridepublic void print() {System.out.println("Dao层执行结束...");}
}

修改UserServiceImpl类

package com.sakurapaid.spring6.resource.service;import com.sakurapaid.spring6.resource.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service("myService") // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {@Resource(name = "myUserDao")private UserDao userDao;/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {userDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

测试输出

package com.sakurapaid.spring6.resource;import com.sakurapaid.spring6.resource.controller.UserControllers;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** 用户相关测试类*/
public class UserTest {/*** 测试方法,用于验证 UserController 的功能。* 该方法不接受参数,也不返回任何值。*/@Testpublic void test() {// 创建 Spring 应用上下文,加载配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 从上下文中获取 UserController 的实例UserControllers userControllers = context.getBean(UserControllers.class, "myController");// 调用 UserController 的方法进行测试userControllers.out();}
}


在这个例子中,我们看到UserServiceImpl类中的UserDao依赖是通过@Resource注解并指定name="myUserDao"进行注入的。

以下是根据名称注入的原理过程:

  1. Spring容器初始化
    当通过ClassPathXmlApplicationContext创建Spring应用上下文并加载配置文件bean.xml时,Spring开始扫描并注册所有符合Spring Bean定义规则的类。
  2. 识别资源注入
    Spring在初始化UserServiceImpl Bean时,会发现UserDao成员变量上使用了@Resource注解,并指定了名称为myUserDao
  3. 依赖查找
    根据@Resource注解中指定的名称myUserDao,Spring容器会在已注册的Bean定义中查找名称匹配的Bean。若在bean.xml或其他自动扫描的组件中有一个Bean定义的id或name为myUserDao,并且它的类型与UserDao一致或兼容,那么Spring将会找到这个Bean。
  4. 依赖注入
    找到匹配的Bean后,Spring容器将其实例注入到UserServiceImpluserDao属性中,完成依赖注入。
  5. 测试阶段
    在测试类UserTest中,我们创建了一个Spring应用上下文,并通过getBean方法根据名称"myController"获取UserControllers实例。假设UserControllers内部也通过@Resource@Autowired注解正确注入了UserService实例,那么当调用userControllers.out()方法时,UserService中的out方法会被执行,其中包含从UserDao注入的对象执行的数据库操作。

综上所述,根据名称注入的过程就是Spring容器根据注解中指定的名称在IoC容器中查找匹配的Bean实例,并将其注入到相应属性的过程。在这里,UserServiceImpl中的UserDao依赖就是通过名称myUserDao成功注入的。


3.5.2. name未知注入

当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名。

修改UserDaoImpl类

package com.sakurapaid.spring6.resource.dao;import org.springframework.stereotype.Repository;@Repository("myuserDao") // 标示一个数据库访问层的实现类
public class UserDaoImpl implements UserDao {/*** 打印信息,表示Dao层操作已经结束。* 此方法没有参数。* 也没有返回值。*/@Overridepublic void print() {System.out.println("Dao层执行结束...");}
}

修改UserServiceImpl类

这里并没有给@Resource进行name指定

package com.sakurapaid.spring6.resource.service;import com.sakurapaid.spring6.resource.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service("myService") // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {@Resourceprivate UserDao myuserDao;/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {myuserDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

但测试输出一样能成功

当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名。


3.5.3. 其他情况

如果上面两种情况都不是,没有指定name,而且根据属性名也找不到

那么就会根据类名ByType进行注入

修改UserControllers类

package com.sakurapaid.spring6.resource.controller;import com.sakurapaid.spring6.resource.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;@Controller("myController") // 表示这是一个控制器类,用于处理用户相关的HTTP请求
public class UserControllers {//根据名字进行注入/*@Resource(name = "myService")private UserService userService;*///根据类型进行注入@Resourceprivate UserService userService;public UserControllers(UserService userService) {this.userService = userService;}/*** 调用UserService中的out方法,然后在控制台打印结束信息。* 这个方法没有参数和返回值,主要用于演示。*/public void out() {userService.out(); // 调用UserService的out方法System.out.println("Controller层执行结束...");}}

修改UserServiceImpl类

package com.sakurapaid.spring6.resource.service;import com.sakurapaid.spring6.resource.dao.UserDao;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;@Service("myService") // 标示一个Spring框架的服务组件
public class UserServiceImpl implements UserService {@Resourceprivate UserDao myuserDao;/*** 执行数据库操作,并在操作完成后输出提示信息。*/@Overridepublic void out() {myuserDao.print(); // 执行UserDao的print方法,通常用于打印数据库信息或其他操作System.out.println("Service层执行结束..."); // 输出服务层执行结束的提示信息}
}

这里的@Resource没有指定name,并且属性名userService和myService也对不上

所以这里就根据UserService这个类进行注入

得到的结果是一样的


3.6. Spring全注解开发

全注解开发是指在Spring框架中,我们不再使用传统的XML配置文件来定义Bean和配置组件扫描等,而是通过编写Java配置类来替代。


比如以前写的spring配置文件

<?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-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启组件扫描,让Spring容器自动发现和装配标注了相应注解的Bean。参数:base-package:指定需要扫描的包路径,Spring会在此路径及其子包下查找组件。--><context:component-scan base-package="com.sakurapaid.spring6"/></beans>

现在用一个类文件完成,这是一样的效果

package com.sakurapaid.spring6.resource.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan(basePackages = "com.sakurapaid.spring6.resource")
public class ResourceConfig {}

测试输出

package com.sakurapaid.spring6.resource;import com.sakurapaid.spring6.resource.config.ResourceConfig;
import com.sakurapaid.spring6.resource.controller.UserControllers;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** 用户相关测试类*/
public class UserTest {/*** 测试方法,用于验证 UserController 的功能。* 该方法不接受参数,也不返回任何值。*/@Testpublic void test() {// 创建 Spring 应用上下文,加载配置文件--用xml文件方式//ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");// 创建 Spring 应用上下文,加载配置文件--用注解类方式ApplicationContext context = new AnnotationConfigApplicationContext(ResourceConfig.class);// 从上下文中获取 UserController 的实例UserControllers userControllers = context.getBean(UserControllers.class);// 调用 UserController 的方法进行测试userControllers.out();}
}


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

cannot find -xml2: No such file or directory的解决方法

一&#xff0c;问题现象 在编译库的时候出现如下图所示的报错&#xff1a;C:/msys64/mingw32/bin/…/lib/gcc/i686-w64-mingw32/13.2.0/…/…/…/…/i686-w64-mingw32/bin/ld.exe: ca nnot find -lxml2: No such file or directory collect2.exe: error: ld returned 1 exit s…

高性能 MySQL 第四版(GPT 重译)(三)

第八章&#xff1a;查询性能优化 在前几章中&#xff0c;我们解释了模式优化和索引&#xff0c;这对于高性能是必要的。但这还不够——您还需要设计良好的查询。如果您的查询不好&#xff0c;即使是设计最佳的模式和索引也不会表现良好。 查询优化、索引优化和模式优化是相辅…

【类脑智能】脑网络通信模型分类及量化指标(附思维导图)

脑网络通信模型分类及量化指标(附思维导图) 参考论文&#xff1a;Brain network communication_ concepts, models and applications 概念 脑网络通信模型是一种使用图论和网络科学概念来描述和量化大脑结构中信息传递的模型。这种模型可以帮助研究人员理解神经信号在大脑内…

智能合约语言(eDSL)—— 使用rust实现eDSL的原理

为理解rust变成eDSL的实现原理&#xff0c;我们需要简单了解元编程与宏的概念,元编程被描述成一种计算机程序可以将代码看待成数据的能力&#xff0c;使用元编程技术编写的程序能够像普通程序在运行时更新、替换变量那样操作更新、替换代码。宏在 Rust 语言中是一种功能&#x…

基于数据库的全文检索实现

对于内容摘要&#xff0c;信件内容进行全文检索 基于SpringBoot 2.5.6Postgresqljpahibernate实现 依赖 <spring-boot.version>2.5.6</spring-boot.version> <hibernate-types-52.version>2.14.0</hibernate-types-52.version><dependency><…

数字多空策略(实盘+回测+数据)

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…