0%

Spring IoC 注解式开发

  • Spring IoC注解式开发
    • 声明Bean的注解
      • @Component:组件
      • @Controller:控制器
      • @Service:业务
      • @Repository:仓库(Dao)
    • 负责注入的注解
      • @Value
      • @Autowired
      • @Qualifier
      • @Resource

十二、★★★Spring IoC注解式开发

  • 注解的存在主要是为了简化 XML 的配置。
    • Spring6 倡导全注解开发。

12.1 回顾注解

  • 注解其实就是一个标记。

  • 元注解:标注注解的注解。

    • @Target 注解:用来修饰注解可以出现的位置。

      • @Target(value = {ElementType.TYPE, ElementType.FIELD})

        • 表示被 @Target 修饰的注解可以出现在类上、属性上
      • 使用某个注解的时候,如果注解的属性名是 value 的话,value 可以省略

        @Target({ElementType.TYPE, ElementType.FIELD})

      • 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,那么大括号可以省略:

        @Target(ElementType.TYPE)

    • @Retention 注解:用来标注注解最终可以保留到什么时刻。

      • @Retention(RetentionPolicy.RUNTIME)
        • 表示被 @Retention 修饰的注解最终保留在 class 文件当中,并且可以被反射机制读取。
      • @Retention(RetentionPolicy.SOURCE)
        • 表示被 @Retention 修饰的注解只能保留到源文件中,字节码文件中和运行时看不到该注解。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      package com.f.annotation;

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

      /**
      * 自定义注解
      *
      * @author fzy
      * @date 2024/1/22 12:33
      */
      // @Target注解用来修饰@Component可以出现的位置
      // 以下表示Component注解可以出现在类上、属性上
      //@Target(value = {ElementType.TYPE, ElementType.FIELD})
      // 使用某个注解的时候,如果注解的属性名是value的话,value可以省略
      //@Target({ElementType.TYPE, ElementType.FIELD})
      // 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个元素,那么大括号可以省略
      @Target(ElementType.TYPE)
      // @Retention也是一个元注解。用来标注@Component注解最终保留到到什么时候。
      // 以下表示Component注解最终保留在class文件当中,并且可以被反射机制读取。
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Component {
      // 定义注解的属性

      // String是属性类型
      // value是属性名字
      String value();

      // String[]是属性类型
      // names是属性名字
      String[] names();
      }
      • 自定义注解的使用:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        package com.f.bean;

        import com.f.annotation.Component;

        /**
        * @author fzy
        * @date 2024/1/22 12:56
        */

        @Component(value = "userBean", names = {"jack", "tom"})
        public class User {
        }

12.2 通过反射获取注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.f.test;

import com.f.annotation.Component;

/**
* 通过反射机制读取注解
*
* @author fzy
* @date 2024/1/22 13:03
*/
public class ReflectAnnotationTest {
public static void main(String[] args) throws Exception {
// 获取类
Class<?> clazz = Class.forName("com.f.bean.User");
// 判断这个类上有没有这个注解
if (clazz.isAnnotationPresent(Component.class)) {
// 获取这个注解
Component annotation = clazz.getAnnotation(Component.class);
// 访问注解属性
String value = annotation.value();
String[] names = annotation.names();
System.out.println(value);
for (String name : names) {
System.out.println(name);
}
}
}
}
1
2
3
userBean
jack
tom

12.3 组件扫描原理

  • 为了方便后面代码编写,将 @Component 注解的 names 属性注释掉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.f.annotation;

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

    /**
    * 自定义注解
    *
    * @author fzy
    * @date 2024/1/22 12:33
    */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Component {
    // 定义注解的属性

    // String是属性类型
    // value是属性名字
    String value();

    // String[]是属性类型
    // names是属性名字
    //String[] names();
    }
  • 需求:给定一个包名,将这个包下的所有的带 @Component 注解的类进行实例化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    package com.f.client;

    import com.f.annotation.Component;

    import java.io.File;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.nio.charset.StandardCharsets;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;

    /**
    * @author fzy
    * @date 2024/1/22 13:15
    */
    public class ComponentScan {
    public static void main(String[] args) {
    Map<String, Object> singletonObjects = new HashMap<>();
    // 目前只知道一个包的名字,扫描这个包下所有的类。
    // 当这个类上有@Component注解的时候,实例化该对象,然后放到Map集合中。
    String packageName = "com.f.bean";
    String packagePath = packageName.replaceAll("\\.", "/");
    // com是在类的根路径下的一个目录
    URL url = ClassLoader.getSystemClassLoader().getResource(packagePath);
    // 获取绝对路径
    String path = url.getPath();
    path = URLDecoder.decode(path, StandardCharsets.UTF_8); // 对获取到的URL进行解码
    // 获取绝对路径下的所有文件
    File file = new File(path);
    File[] files = file.listFiles();
    Arrays.stream(files).forEach(f -> {
    try {
    // 得到指定包下所有类的全限定类名
    String className = packageName + "." + f.getName().split("\\.")[0];
    // 通过反射机制解析注解
    Class<?> clazz = Class.forName(className);
    if (clazz.isAnnotationPresent(Component.class)) {
    // 获取类上的注解
    Component annotation = clazz.getAnnotation(Component.class);
    String id = annotation.value();
    Object obj = clazz.newInstance();
    // 实例化对象,将其放到Map集合中
    singletonObjects.put(id, obj);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    });
    System.out.println(singletonObjects);
    }
    }

12.4 ★声明Bean的注解

  • 负责声明 Bean 的注解,常见的包括四个:

    • @Component:组件
    • @Controller:控制器
    • @Service:业务
    • @Repository:仓库(Dao)
  • 上面四个注解中,@Controller@Service@Repository 都为 @Component 的别名(@AliasFor),其实这四个注解的功能都一样,用哪个都可以,但是在不同用途的 Bean 上使用不同的注解可以增强程序的可读性

    • 普通 bean@Component
    • 控制器类上使用:@Controller(控制层)。
    • service 类上使用:@Service(业务层)。
    • dao 类上使用:@Repository(持久层)。

    **上面四个注解中,都只有一个 value 属性,用来指定 beanid**。

12.5 ★Spring注解的使用

  1. 加入 aop 依赖。

    • 当加入 spring-context 依赖之后,会自动关联加入 aop 的依赖。

      ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-SSM-notebook/img/Spring/aop依赖.png)

  2. 在配置文件中添加 context 命名空间

    1
    2
    3
    4
    5
    6
    7
    8
    <?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">

    </beans>
  3. 在配置文件中指定扫描的包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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">
    <!--给Spring框架指定要扫描哪些包中的类-->
    <context:component-scan base-package="com.f.spring6.bean"/>
    </beans>
  4. 在相应的包中的 Bean 类上使用注解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.f.spring6.bean;

    import org.springframework.stereotype.Component;

    /**
    * @author fzy
    * @date 2024/1/22 13:58
    */
    @Component("user")
    public class User {
    }
  5. 测试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.f.spring6.test;

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

    /**
    * @author fzy
    * @date 2024/1/22 14:01
    */
    public class IoCAnnotationTest {
    @Test
    public void testBeanComponent() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    User user = applicationContext.getBean("user", User.class);
    System.out.println(user);
    }
    }
  • 注意

    • 如果注解的属性名是 value,那么 value 是可以省略的。

    • 如果把 value 属性彻底去掉,那么 Spring 会为 Bean 自动取名,默认名字是:Bean 类名首字母变小写。例如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      package com.f.spring6.bean;

      import org.springframework.stereotype.Component;

      /**
      * @author fzy
      * @date 2024/1/22 13:58
      */
      @Component
      public class User {
      }
      • User Bean 的默认 id 就是 user

12.6 多个包扫描

  • 如果要对多个包进行扫描,有两种解决方案:

    • 第一种:在配置文件中指定多个包,用逗号隔开。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <?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">
      <!--给Spring框架指定要扫描哪些包中的类-->
      <context:component-scan base-package="com.f.spring6.bean, com.f.spring6.dao"/>
      </beans>
    • 第二种:指定多个包的共同父包。

      • Spring 会扫描指定的包中的类,及其子包中的类。
      • 但会牺牲一些效率。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <?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.f.spring6"/>
      </beans>

12.7 选择性实例化Bean

  • 选择性实例化 Bean,就是只选择满足某些条件的类进行 bean 的实例化,或者排除满足某些条件的类,对这些类不进行 Bean 的实例化。

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      package com.f.spring6.bean2;

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

      /**
      * @author fzy
      * @date 2024/1/22 16:52
      */
      @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的无参数构造方法执行...");
      }
      }
  • 方式一:use-default-filters="false" + context:include-filter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?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">
    <!--
    第一种解决方案:use-default-filters="false"
    如果这个属性是false,表示com.f.spring6.bean2包下所有的带有声明Bean的注解全部失效。
    即@Component、@Controller、@Service、@Repository全部失效
    我们再使用context:include-filter来使得某几种注解生效
    -->
    <context:component-scan base-package="com.f.spring6.bean2" use-default-filters="false">
    <!--只有@Repository生效-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    </beans>
    • use-default-filters="false":不使用 Spring 默认的实例化规则,即所有带有声明 bean 的注解全部失效,用注解 Component、Controller、Service、Repository 标注的 bean 都不进行实例化(让所有的声明 bean 的注解失效)。
    • 再使用 context:include-filter 来使得某几种注解生效。
    • 注意:由于其他三个注解只是 @Component 的别名,所以包含 @Component 也就包含了其他三种注解。
      • 也就是说,当用下面的标签时:<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>,会使得注解 Component、Controller、Service、Repository 标注的 bean 都生效。
  • 方式二:use-default-filters="true" + context:exclude-filter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <?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">
    <!--
    第二种解决方案:use-default-filters="true"
    如果这个属性是true,表示com.f.spring6.bean2包下所有的带有声明Bean的注解全部有效。
    即@Component、@Controller、@Service、@Repository全部有效。
    我们再使用context:exclude-filter来使得某几种注解失效
    use-default-filters默认值就是true,可以不用写
    -->
    <context:component-scan base-package="com.f.spring6.bean2" use-default-filters="true">
    <!--只有@Repository失效-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    </beans>
    • use-default-filters="true":使用 Spring 默认的实例化规则,即所有带有声明 bean 的注解全部生效,用注解 Component、Controller、Service、Repository 标注的 bean 都进行实例化。
      • use-default-filters 默认值就是 true,可以不用写。
    • 再使用 context:exclude-filter 来使得某几种注解失效。
    • 注意:由于其他三个注解只是 @Component 的别名,所以排除 @Component 也就排除了其他三种注解。

12.8 负责注入的注解

  • @Component@Controller@Service@Repository 这四个注解是用来声明 Bean 的,声明后这些 Bean 将被实例化。接下来我们看一下,如何给 Bean 的属性赋值。

    Bean 属性赋值需要用到这些注解:

    • @Value
    • @Autowired
    • @Qualifier
    • @Resource

12.8.1 ★@Value

  • 用于简单类型注入

  • 当属性的类型是简单类型时,可以使用 @Value 注解进行注入。

    • @Value 注解用于代替<property name="" value=""/>
  • 用在属性上

    • @Value 可以直接写在属性上,并且可以不用提供对应的 set 方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    package com.f.spring6.bean3;

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

    import javax.sql.DataSource;
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.logging.Logger;

    /**
    * @author fzy
    * @date 2024/1/24 12:43
    */
    @Component
    public class MyDataSource implements DataSource {
    @Value("com.mysql.cj.jdbc.Driver")
    private String driver;
    @Value("jdbc:mysql://localhost:3306/javaweb")
    private String url;
    @Value("root")
    private String username;
    @Value("root")
    private String password; //使用@Value注解注入的话,可以用在属性上,并且可以不提供set方法。

    @Override
    public String toString() {
    return "MyDataSource{" +
    "driver='" + driver + '\'' +
    ", url='" + url + '\'' +
    ", username='" + username + '\'' +
    ", password='" + password + '\'' +
    '}';
    }

    //public void setDriver(String driver) {
    // this.driver = driver;
    //}
    //
    //public void setUrl(String url) {
    // this.url = url;
    //}
    //
    //public void setUsername(String username) {
    // this.username = username;
    //}
    //
    //public void setPassword(String password) {
    // this.password = password;
    //}

    ......
    }
  • 用在 set 方法上

    • @Value 也可以写在属性对应的 set 方法上,实现属性值的注入。

      为了简化代码,一般不提供 set 方法,而是直接在属性上使用 @Value 注解完成属性赋值。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      package com.f.spring6.bean3;

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

      /**
      * @author fzy
      * @date 2024/1/24 12:56
      */
      @Component
      public class User2 {
      private String name;
      private int age;

      // @Value注解也可以使用在方法上
      @Value("张三")
      public void setName(String name) {
      this.name = name;
      }

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

      @Override
      public String toString() {
      return "User2{" +
      "name='" + name + '\'' +
      ", age=" + age +
      '}';
      }
      }
  • 用在构造方法的形参上

    • @Value 注解也可以用在构造方法的形参上。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      package com.f.spring6.bean3;

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

      /**
      * @author fzy
      * @date 2024/1/24 13:02
      */
      @Component
      public class Product {
      private String name;
      private double price;

      public Product(@Value("苹果") String name, @Value("10.0") double price) {
      this.name = name;
      this.price = price;
      }

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

12.8.2 @Autowired

  • @Autowired 注解用来注入非简单类型

    • Autowired:自动连线,或自动装配。
    • @Autowired:通过注解的方式进行自动装配。
    • @Autowired 注解不需要 set 方法。
  • 单独使用 @Autowired 注解,默认根据类型进行装配,即默认是 byType,如果需要根据名字进行自动装配,则需要配合 @Qualifier 注解。

    • @Autowired 注解是根据类型进行自动装配,如果只使用 @Autowired 注解的话,Spring 容器中不能存在两个相同类型的实例,例如两个类实现了同一个接口,如果要使用 @Autowired 注解并且 Spring 容器中存在两个相同类型的实例,则需要配合 @Qualifier 注解,根据名称进行装配。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package org.f.spring6.dao;

    /**
    * @author fzy
    * @date 2024/1/24 13:11
    */
    public interface OrderDao {
    void insert();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package org.f.spring6.dao.impl;

    import org.f.spring6.dao.OrderDao;
    import org.springframework.stereotype.Repository;

    /**
    * @author fzy
    * @date 2024/1/24 13:12
    */
    @Repository
    public class OrderDaoImplForMysql implements OrderDao {
    @Override
    public void insert() {
    System.out.println("Mysql数据库正在保存订单信息...");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package org.f.spring6.dao.impl;

    import org.f.spring6.dao.OrderDao;
    import org.springframework.stereotype.Repository;

    /**
    * @author fzy
    * @date 2024/1/24 13:27
    */
    @Repository
    public class OrderDaoImplForOracle implements OrderDao {
    @Override
    public void insert() {
    System.out.println("Oracle数据库正在保存订单信息...");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package org.f.spring6.service;

    import org.f.spring6.dao.OrderDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    /**
    * @author fzy
    * @date 2024/1/24 13:13
    */
    @Service
    public class OrderService {
    // @Autowired注解使用的时候,不需要指定任何属性,直接使用这个注解即可。
    // 这个注解的作用是根据类型byType进行自动装配
    @Autowired
    private OrderDao orderDao;

    public void generate() {
    orderDao.insert();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    <?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="org.f.spring6"/>
    </beans>
    1
    2
    3
    4
    5
    6
    @Test
    public void testAutowired() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowired.xml");
    OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
    orderService.generate();
    }
    • 报异常:

      1
      org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderService': Unsatisfied dependency expressed through field 'orderDao': No qualifying bean of type 'org.f.spring6.dao.OrderDao' available: expected single matching bean but found 2: orderDaoImplForMysql,orderDaoImplForOracle

12.8.3 @Qualifier

  • @Autowired 注解和 @Qualifier 注解联合起来使得可以根据名称进行装配,在 @Qualifier 注解中指定 Bean 名称。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package org.f.spring6.service;

    import org.f.spring6.dao.OrderDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;

    /**
    * @author fzy
    * @date 2024/1/24 13:13
    */
    @Service
    public class OrderService {
    // @Autowired注解使用的时候,不需要指定任何属性,直接使用这个注解即可。
    // 这个注解的作用是根据类型byType进行自动装配
    @Autowired
    // @Autowired和@Qualifier联合使用,可以根据名字进行自动装配
    @Qualifier("orderDaoImplForMysql")
    private OrderDao orderDao;

    public void generate() {
    orderDao.insert();
    }
    }
    • 输出结果:

      1
      Mysql数据库正在保存订单信息...

12.8.4 ★@Resource

  • @Resource 注解也可以完成非简单类型注入

  • @Resource 注解是JDK 扩展包中的,也就是说属于 JDK 的一部分,而 @Autowired 注解是 Spring 框架自己的。

    • @Resource 是JDK 标准规范中的,更具有通用性,更推荐使用。
  • @Resource 注解默认根据名称装配 byName,未指定 name 时,使用属性名作为 name。通过 name 找不到的话会自动启动通过类型 byType 装配

  • @Resource 注解用在类上、属性上、方法上,不能用在构造方法上。

  • @Resource 注解属于 JDK 扩展包,所以不在 JDK 当中,需要额外引入以下依赖(如果是 JDK8 的话不需要额外引入依赖,高于 JDK11 或低于 JDK8 则需要引入以下依赖。):

    1
    2
    3
    4
    5
    6
    <!--@Resource注解的依赖-->
    <dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
    </dependency>
1
2
3
4
5
6
7
8
9
package cn.f.spring6.dao;

/**
* @author fzy
* @date 2024/1/24 13:52
*/
public interface StudentDao {
void deleteById();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.f.spring6.dao.impl;

import cn.f.spring6.dao.StudentDao;
import org.springframework.stereotype.Repository;

/**
* @author fzy
* @date 2024/1/24 13:52
*/
@Repository
public class StudentDaoImplForMysql implements StudentDao {
@Override
public void deleteById() {
System.out.println("Mysql数据库正在删除学生信息...");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.f.spring6.service;

import cn.f.spring6.dao.StudentDao;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

/**
* @author fzy
* @date 2024/1/24 13:52
*/
@Service
public class StudentService {
//@Resource(name = "studentDaoImplForMysql")
// 如果直接使用@Resource,那在这里Spring框架会在XML配置文件指定的包下寻找id为studentDao的bean。
// 如果还是找不到,再根据类型自动装配
@Resource
private StudentDao studentDao;

public void deleteStudent() {
studentDao.deleteById();
}
}
1
2
3
4
5
6
7
8
<?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="cn.f.spring6"/>
</beans>
1
2
3
4
5
6
@Test
public void testResource() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-resource.xml");
StudentService studentService = applicationContext.getBean("studentService", StudentService.class);
studentService.deleteStudent();
}

12.9 全注解开发

  • 全注解开发就是不再使用 Spring 配置文件,而是写一个配置类来代替配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.f.spring6;

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

/**
* 编写一个类,代替Spring框架的配置文件
*
* @author fzy
* @date 2024/1/24 14:04
*/
// 配置类使用@Configuration注解进行标注
@Configuration
// 通过@ComponentScan注解配置要扫描的包
@ComponentScan({"cn.f.spring6.dao", "cn.f.spring6.service"})
public class Spring6Config {
}
1
2
3
4
5
6
@Test
public void testConfigClass() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
StudentService studentService = applicationContext.getBean("studentService", StudentService.class);
studentService.deleteStudent();
}
  • 获取 Spring 容器不再是 new ClassPathXmlApplicationContext() 对象了,而是 new AnnotationConfigApplicationContext()
---------------The End---------------