Skip to content

12 Spring IoC注解式开发

12.1 回顾注解

注解的存在主要是为了简化 XML 的配置。Spring 6 倡导全注解开发。
我们来回顾一下:

  • 第一:注解怎么定义,注解中的属性怎么定义?
  • 第二:注解怎么使用?
  • 第三:通过反射机制怎么读取注解?

注解怎么定义,注解中的属性怎么定义?

```java title:Component.java package com.powernode.annotation;

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Component { String value(); }

以上是自定义了一个注解:Component  

**该注解上面修饰的注解包括:Target 注解和 Retention 注解,这两个注解被称为元注解。**  

- Target 注解用来设置 Component 注解可以出现的位置,以上代表表示 Component 注解只能用在类和接口上。  

- Retention 注解用来设置 Component 注解的保持性策略,以上代表 Component 注解可以被反射机制读取。  

`String value()`; 是 Component 注解中的一个属性。该属性类型 String,属性名是 value。

注解怎么使用?
```java title:User.java
package com.powernode.bean;

import com.powernode.annotation.Component;

@Component(value = "userBean")
public class User {
}

用法简单,语法格式:@注解类型名(属性名=属性值, 属性名=属性值, 属性名=属性值......)

userBean 为什么使用双引号括起来,因为 value 属性是 String 类型,字符串。

另外如果属性名是 value,则在使用的时候可以省略属性名,例如: ```java title:User.java package com.powernode.bean;

import com.powernode.annotation.Component;

//@Component(value = "userBean") @Component("userBean") public class User { }

通过反射机制怎么读取注解?  

接下来,我们来写一段程序,当 Bean 类上有 Component 注解时,则实例化 Bean 对象,如果没有,则不实例化对象。  

我们准备两个 Bean,一个上面有注解,一个上面没有注解。
```java title:User.java
package com.powernode.bean;

import com.powernode.annotation.Component;

@Component("userBean")
public class User {
}

```java title:Vip.java package com.powernode.bean;

public class Vip { }

假设我们现在只知道包名:com. powernode. bean。至于这个包下有多少个 Bean 我们不知道。哪些 Bean 上有注解,哪些 Bean 上没有注解,这些我们都不知道,如何通过程序全自动化判断。
```java title:Test.java
package com.powernode.test;

import com.powernode.annotation.Component;

import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 动力节点
 * @version 1.0
 * @className Test
 * @since 1.0
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 存放Bean的Map。key存储beanId。value存储Bean。
        Map beanMap = new HashMap<>();

        String packageName = "com.powernode.bean";
        String path = packageName.replaceAll("\\.", "/");
        URL url = ClassLoader.getSystemClassLoader().getResource(path);
        File file = new File(url.getPath());
        File[] files = file.listFiles();
        Arrays.stream(files).forEach(f -> {
            String className = packageName + "." + f.getName().split("\\.")[0];
            try {
                Class clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Component.class)) {
                    Component component = (Component) clazz.getAnnotation(Component.class);
                    String beanId = component.value();
                    Object bean = clazz.newInstance();
                    beanMap.put(beanId, bean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        System.out.println(beanMap);
    }
}

执行结果: ![[12.01-1.png]]


12.2 声明 Bean 的注解

负责声明 Bean 的注解,常见的包括四个: - @Component
- @Controller
- @Service
- @Repository

源码如下: ```java title:Component.java package com.powernode.annotation;

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Component { String value(); }

```java title:Controller.java
package org.springframework.stereotype;

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;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

```java title:Service.java package org.springframework.stereotype;

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; import org.springframework.core.annotation.AliasFor;

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

```java title:Repository.java
package org.springframework.stereotype;

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;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

通过源码可以看到,@Controller、@Service、@Repository 这三个注解都是 @Component 注解的别名。

也就是说:这四个注解的功能都一样。用哪个都可以。

只是为了增强程序的可读性,建议: - 控制器类上使用:Controller
- service 类上使用:Service
- dao 类上使用:Repository

他们都是只有一个 value 属性。value 属性用来指定 bean 的 id,也就是 bean 的名字。 ![[12.02-1.png]]


12.3 Spring 注解的使用

如何使用以上的注解呢? - 第一步:加入 aop 的依赖
- 第二步:在配置文件中添加 context 命名空间
- 第三步:在配置文件中指定扫描的包
- 第四步:在 Bean 类上使用注解

第一步:加入 aop 的依赖
我们可以看到当加入 spring-context 依赖之后,会关联加入 aop 的依赖。所以这一步不用做。

![[12.03-1.png]]

第二步:在配置文件中添加 context 命名空间
```xml title:spring.xml

第三步:在配置文件中指定要扫描的包  
```xml title:spring.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.powernode.spring6.bean"/>
</beans>

第四步:在 Bean 类上使用注解
```java title:User.java package com.powernode.spring6.bean;

import org.springframework.stereotype.Component;

@Component(value = "userBean") public class User { }

编写测试程序:
```java title:AnnotationTest.java
package com.powernode.spring6.test;

import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);
    }
}

执行结果: ![[12.03-2.png]]

如果注解的属性名是 value,那么 value 是可以省略的。 ```java title:Vip.java package com.powernode.spring6.bean;

import org.springframework.stereotype.Component;

@Component("vipBean") public class Vip { }

```java title:AnnotationTest.java
package com.powernode.spring6.test;

import com.powernode.spring6.bean.Vip;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
        System.out.println(vipBean);
    }
}

执行结果: ![[12.03-3.png]]

如果把 value 属性彻底去掉,spring 会被 Bean 自动取名吗?会的。 并且默认名字的规律是:Bean 类名首字母小写即可。 ```java title:BankDao.java package com.powernode.spring6.bean;

import org.springframework.stereotype.Component;

@Component public class BankDao { }

也就是说,这个 BankDao 的 bean 的名字为:bankDao  

测试一下:
```java title:AnnotationTest.java
package com.powernode.spring6.test;

import com.powernode.spring6.bean.BankDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class);
        System.out.println(bankDao);
    }
}

执行结果: ![[12.03-4.png]]

我们将 Component 注解换成其它三个注解,看看是否可以用: ```java title:BankDao.java package com.powernode.spring6.bean;

import org.springframework.stereotype.Controller;

@Controller public class BankDao { }

执行结果:
![[12.03-5.png]]

剩下的两个注解大家可以测试一下。  

如果是多个包怎么办?有两种解决方案:
- 第一种:**在配置文件中指定多个包,用逗号隔开。**  
- 第二种:**指定多个包的共同父包。**  

先来测试一下逗号(英文)的方式:  

创建一个新的包:bean 2,定义一个 Bean 类。
```java title:Order.java
package com.powernode.spring6.bean2;

import org.springframework.stereotype.Service;

@Service
public class Order {
}

配置文件修改: ```xml title:spring.xml

测试程序:
```java title:AnnotationTest.java
package com.powernode.spring6.test;

import com.powernode.spring6.bean.BankDao;
import com.powernode.spring6.bean2.Order;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        BankDao bankDao = applicationContext.getBean("bankDao", BankDao.class);
        System.out.println(bankDao);
        Order order = applicationContext.getBean("order", Order.class);
        System.out.println(order);
    }
}

执行结果: ![[12.03-6.png]]

我们再来看看,指定共同的父包行不行: ```xml title:spring.xml

执行测试程序:
![[12.03-7.png]]


---
## 12.4 选择性实例化 Bean

假设在某个包下有很多 Bean,有的 Bean 上标注了 Component,有的标注了 Controller,有的标注了 Service,有的标注了 Repository,现在由于某种特殊业务的需要,只允许其中所有的 Controller 参与 Bean 管理,其他的都不实例化。这应该怎么办呢?

```java title:com.powernode.spring6.bean3
package com.powernode.spring6.bean3;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

@Component
public class A {
    public A() {
        System.out.println("A的无参数构造方法执行");
    }
}

@Controller
class B {
    public B() {
        System.out.println("B的无参数构造方法执行");
    }
}

@Service
class C {
    public C() {
        System.out.println("C的无参数构造方法执行");
    }
}

@Repository
class D {
    public D() {
        System.out.println("D的无参数构造方法执行");
    }
}

@Controller
class E {
    public E() {
        System.out.println("E的无参数构造方法执行");
    }
}

@Controller
class F {
    public F() {
        System.out.println("F的无参数构造方法执行");
    }
}

我只想实例化 bean 3 包下的 Controller。配置文件这样写:

```xml title:spring-choose.xml

<context:component-scan base-package="com.powernode.spring6.bean3" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

- `use-default-filters="true"` 表示:使用 spring 默认的规则,只要有 Component、Controller、Service、Repository 中的任意一个注解标注,则进行实例化。 - `use-default-filters="false"` 表示:不再 spring 默认实例化规则,即使有 Component、Controller、Service、Repository 这些注解标注,也不再实例化。 - `<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>` 表示只有 Controller 进行实例化。java title:ChooseTest @Test public void testChoose(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-choose.xml"); } ```

执行结果: ![[12.04-1.png]]

也可以将 use-default-filters 设置为 true(不写就是 true),并且采用 exclude-filter 方式排出哪些注解标注的 Bean 不参与实例化: ```xml title:spring-choose.xml

执行测试程序:
![[12.04-2.png]]


---
## 12.5 负责注入的注解

`@Component @Controller @Service @Repository` 这四个注解是用来声明 Bean 的,声明后这些 Bean 将被实例化。接下来我们看一下,如何给 Bean 的属性赋值。给 Bean 属性赋值需要用到这些注解:
- `@Value`
- `@Autowired`
- `@Qualifier`
- `@Resource`

### 12.5.1 @Value

当属性的类型是简单类型时,可以使用 `@Value` 注解进行注入。
```java title:com.powernode.spring6.bean4.User
package com.powernode.spring6.bean4;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {
    @Value(value = "zhangsan")
    private String name;
    @Value("20")
    private int age;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

开启包扫描: ```xml title:spring-injection.xml

```java title:ValueTest
@Test
public void testValue(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml");
    Object user = applicationContext.getBean("user");
    System.out.println(user);
}

执行结果: ![[12.05-1.png]]

通过以上代码可以发现,我们并没有给属性提供 setter 方法,但仍然可以完成属性赋值。

如果提供 setter 方法,并且在 setter 方法上添加@Value 注解,可以完成注入吗?尝试一下: ```Java title:User.java

package com.powernode.spring6.bean4;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {

private String name;

private int age;

@Value("李四")
    public void setName(String name) {
        this.name = name;
    }

@Value("30")
    public void setAge(int age) {
        this.age = age;
    }

@Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

执行结果: 
![[12.05-2.png]] ![[12.05-3.png]]

### 12.5.2 @Autowired 与 @Qualifier

`@Autowired` 注解可以用来注入非简单类型。被翻译为:自动连线的,或者自动装配。  

单独使用 `@Autowired` 注解,默认根据类型装配【**默认是 byType**】  

看一下它的源码:
```java title: @Autowired 注解源码
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;
}

源码中有两处需要注意:

  • 第一处:该注解可以标注在哪里?

    • 构造方法上
    • 方法上
    • 形参上
    • 属性上
    • 注解上
  • 第二处:该注解有一个 required 属性,

    • 默认值是 true,表示在注入的时候要求被注入的 Bean 必须是存在的,如果不存在则报错。
    • 如果 required 属性设置为 false,表示注入的 Bean 存在或者不存在都没关系,存在的话就注入,不存在的话,也不报错。

我们先在属性上使用 @Autowired 注解: ```java title: UserDao 接口 package com.powernode.spring6.dao;

public interface UserDao { void insert(); }

```java title: UserDaoForMySQL 实现类
package com.powernode.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository // 纳入bean管理
public class UserDaoForMySQL implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在向mysql数据库插入User数据");
    }
}

```java title: UserService 服务类 package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;

@Service // 纳入bean管理 public class UserService {

@Autowired // 在属性上注入
private UserDao userDao;

// 没有提供构造方法和setter方法。

public void save() {
    userDao.insert();
}

} xml title: spring-injection.xml 配置文件 ```

```java title: 测试方法 @Test public void testAutowired() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-injection.xml"); UserService userService = applicationContext.getBean("userService", UserService.class); userService.save(); }

执行结果:
![[12.05-4.png]]
以上构造方法和 setter 方法都没有提供,经过测试,仍然可以注入成功。  

接下来,再来测试一下 `@Autowired` 注解出现在 setter 方法上:
```java title: UserService 设置器方法
package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.insert();
    }
}

执行结果: ![[12.05-5.png]]

我们再来看看能不能出现在构造方法上: ```java title: UserService 构造方法 package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;

@Service public class UserService {

private UserDao userDao;

@Autowired
public UserService(UserDao userDao) {
    this.userDao = userDao;
}

public void save() {
    userDao.insert();
}

} 执行结果: ![[12.05-6.png]] 再来看看,这个注解能不能只标注在构造方法的形参上:java title: UserService 构造方法参数 package com.powernode.spring6.service; import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; public UserService(@Autowired UserDao userDao) { this.userDao = userDao; } public void save() { userDao.insert(); } } ```

执行结果: ![[12.05-7.png]]

还有更劲爆的,当有参数的构造方法只有一个时,@Autowired 注解可以省略。 ```java title: UserService 构造方法省略 package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import org.springframework.stereotype.Service;

@Service public class UserService {

private UserDao userDao;

public UserService(UserDao userDao) {
    this.userDao = userDao;
}

public void save() {
    userDao.insert();
}

} 执行结果: ![[12.05-8.png]] 当然,如果有多个构造方法,`@Autowired` 肯定是不能省略的。java title: UserService 多构造方法 package com.powernode.spring6.service; import com.powernode.spring6.dao.UserDao; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; public UserService(UserDao userDao) { this.userDao = userDao; } public UserService() { } public void save() { userDao.insert(); } } ```

执行结果: ![[12.05-9.png]]

到此为止,我们已经清楚 @Autowired 注解可以出现在哪些位置了。

@Autowired 注解默认是 byType 进行注入的,也就是说根据类型注入的,如果以上程序中,UserDao 接口还有另外一个实现类,会出现问题吗? ```java title: UserDaoForOracle 实现类 package com.powernode.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository // 纳入bean管理 public class UserDaoForOracle implements UserDao { @Override public void insert() { System.out.println("正在向Oracle数据库插入User数据"); } }

当你写完这个新的实现类之后,此时 IDEA 工具已经提示错误信息了:
![[12.05-10.png]]

错误信息中说:不能装配,`UserDao` 这个 Bean 的数量大于 1。  

怎么解决这个问题呢?当然要 byName,根据名称进行装配了。  

`@Autowired` 注解和 `@Qualifier` 注解联合起来才可以根据名称进行装配,在 `@Qualifier` 注解中指定 Bean 名称。
```java title: UserDaoForOracle 实现类命名
package com.powernode.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository // 这里没有给bean起名,默认名字是:userDaoForOracle
public class UserDaoForOracle implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

```java title: UserService 使用 @Qualifier package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service;

@Service public class UserService {

private UserDao userDao;

@Autowired
@Qualifier("userDaoForOracle") // 这个是bean的名字。
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

public void save() {
    userDao.insert();
}

} `` 执行结果: ![[12.05-11.png]] 总结: -@Autowired注解可以出现在:属性上、构造方法上、构造方法的参数上、setter 方法上。 - 当带参数的构造方法只有一个,@Autowired注解可以省略。 -@Autowired注解默认根据类型注入。如果要根据名称注入的话,需要配合@Qualifier` 注解一起使用。

12.5.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 当中,需要额外引入以下依赖:【如果是 JDK 8 的话不需要额外引入依赖。高于 JDK 11 或低于 JDK 8 需要引入以下依赖。】
XML title:如果你是Spring6+版本请使用这个依赖.xml <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency>

一定要注意:如果你用 Spring 6,要知道 Spring 6 不再支持 JavaEE,它支持的是 JakartaEE 9。(Oracle 把 JavaEE 贡献给 Apache 了,Apache 把 JavaEE 的名字改成 JakartaEE 了,大家之前所接触的所有的 javax.* 包名统一修改为 jakarta.* 包名了。)

```XML title:如果你是spring5-版本请使用这个依赖.xml javax.annotation javax.annotation-api 1.3.2

@Resource 注解的源码如下:
![[12.05-12.png]]

试一下:
```Java title:给这个UserDaoForOracle起名xyz.java
package com.powernode.spring6.dao;

import org.springframework.stereotype.Repository;

@Repository("xyz")
public class UserDaoForOracle implements UserDao {
    @Override
    public void insert() {
        System.out.println("正在向Oracle数据库插入User数据");
    }
}

```Java title:在UserService中使用Resource注解根据name注入.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;

@Service public class UserService {

@Resource(name = "xyz")
private UserDao userDao;

public void save() {
    userDao.insert();
}

} 执行测试程序: ![[12.05-13.png]] 我们把 UserDaoForOracle 的名字 xyz 修改为 userDao,让这个 Bean 的名字和 UserService 类中的 UserDao 属性名一致:Java title:UserDaoForOracle.java package com.powernode.spring6.dao; import org.springframework.stereotype.Repository; @Repository("userDao") public class UserDaoForOracle implements UserDao { @Override public void insert() { System.out.println("正在向Oracle数据库插入User数据"); } } ```

```Java title:UserService类中Resource注解并没有指定name.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;

@Service public class UserService {

@Resource
private UserDao userDao;

public void save() {
    userDao.insert();
}

} 执行测试程序: ![[12.05-14.png]] 通过测试得知,当@Resource 注解使用时没有指定 name 的时候,还是根据 name 进行查找,这个 name 是属性名。 接下来把 UserService 类中的属性名修改一下:Java title:UserService的属性名修改为userDao2.java package com.powernode.spring6.service; import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @Service public class UserService { @Resource private UserDao userDao2; public void save() { userDao2.insert(); } } ```

执行结果: ![[12.05-15.png]]

根据异常信息得知:显然当通过 name 找不到的时候,自然会启动 byType 进行注入。 以上的错误是因为 UserDao 接口下有两个实现类导致的。所以根据类型注入就会报错。

我们再来看@Resource 注解使用在 setter 方法上可以吗? ```Java title:UserService添加setter方法并使用注解标注.java package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;

@Service public class UserService {

private UserDao userDao;

@Resource
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

public void save() {
    userDao.insert();
}

} 注意这个 setter 方法的方法名,setUserDao 去掉 set 之后,将首字母变小写 userDao,userDao 就是 name 执行结果: ![[12.05-16.png]] 当然,也可以指定 name:Java title:UserService.java package com.powernode.spring6.service; import com.powernode.spring6.dao.UserDao; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @Service public class UserService { private UserDao userDao; @Resource(name = "userDaoForMySQL") public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void save() { userDao.insert(); } } ```

执行结果: ![[12.05-17.png]]

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


12.6 全注解式开发

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

```java title:Spring6Configuration.java package com.powernode.spring6.config;

import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScans; import org.springframework.context.annotation.Configuration;

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

编写测试程序:不再 `new ClassPathXmlApplicationContext ()` 对象了。
```java title:TestNoXml.java
@Test
public void testNoXml(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
}

执行结果: ![[12.06-1.png]]