• [Spring Framework]注解开发③(依赖注入)


    自动装配

    Spring为了使用注解简化开发,并没有提供构造函数注入setter注入对应的注解,只提供了自动装配的注解实现。

    项目环境介绍

    我们有一个Spring配置类:

    @Configuration
    @ComponentScan("impl")
    public class SpringConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    另个接口,两个实现类,且他们均在impl文件夹中:

    public interface BookDao {
        public void save();
    }
    @Repository
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    public interface BookService {
        public void save();
    }
    @Service
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
    	public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    工程结构:
    在这里插入图片描述

    注解实现按照类型注入

    在BookServiceImpl类的bookDao属性上添加@Autowired注解

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
        
    //	  public void setBookDao(BookDao bookDao) {
    //        this.bookDao = bookDao;
    //    }
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后我们运行测试demo:

    public class App8 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookService bookService = ctx.getBean("bookService", BookService.class);
            bookService.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结果成功:
    在这里插入图片描述

    @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉

    为什么setter方法可以删除呢?

    这是因为自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值,普通反射只能获取public修饰的内容,暴力反射除了获取public修饰的内容还可以获取private修改的内容,所以此处无需提供setter方法

    @Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,显然这里就不适用了,因为按照类型注入就无法区分到底注入哪个对象。

    比如我们添加一个类:

    @Repository
    public class BookDaoImpl2 implements BookDao {
        public void save() {
            System.out.println("book dao save ...2");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果我们直接运行控制台会报错NoUniqueBeanDefinitionException
    在这里插入图片描述

    所以我们这里使用按照名称注入

    注解实现按照名称注入

    在使用名称注入之前,我们先来看一个特殊情况:

    我们先给两个Dao类分别起个名称:

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    @Repository("bookDao2")
    public class BookDaoImpl2 implements BookDao {
        public void save() {
            System.out.println("book dao save ...2" );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们再运行就成功了:
    在这里插入图片描述

    此时就可以注入成功,但是得思考个问题:

    • @Autowired是按照类型注入的,给BookDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?

    • @Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao而容器中也有一个booDao,所以可以成功注入。

    也就是说,如果是下图中的情况,就不行了:
    在这里插入图片描述
    因为按照类型会找到多个bean对象,此时会按照bookDao名称去找,因为IOC容器只有名称叫bookDao1bookDao2,所以找不到,会报NoUniqueBeanDefinitionException

    接下来我们使用名称注入:

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        @Qualifier("bookDao1")
        private BookDao bookDao;
        
        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @Qualifier注解后的值就是需要注入的bean的名称。

    注意:@Qualifier不能独立使用,必须和@Autowired一起使用

    结果成功:
    在这里插入图片描述

    简单数据类型注入

    引用类型看完,简单类型注入就比较容易懂了。简单类型注入的是基本数据类型或者字符串类型,下面在BookDaoImpl类中添加一个name属性,用其进行简单类型注入

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    数据类型换了,对应的注解也要跟着换,这次使用@Value注解,将值写入注解的参数中就行了

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("csdn")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果:
    在这里插入图片描述

    注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。

    介绍完后,会有一种感觉就是这个注解好像没什么用,跟直接赋值是一个效果,还没有直接赋值简单,所以这个注解存在的意义是什么?

    注解读取properties配置文件

    @Value一般会被用在从properties配置文件中读取内容进行使用,具体如何实现?

    步骤1:resource下准备properties文件

    jdbc.properties

    name=csdn888
    
    • 1

    步骤2: 使用注解加载properties配置文件

    在配置类上添加@PropertySource注解

    @Configuration
    @ComponentScan("impl")
    @PropertySource("jdbc.properties")
    public class SpringConfig {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    步骤3:使用@Value读取配置文件中的内容

    @Repository("bookDao")
    public class BookDaoImpl implements BookDao {
        @Value("${name}")
        private String name;
        public void save() {
            System.out.println("book dao save ..." + name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:

    • 如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个

      @PropertySource({"jdbc.properties","xxx.properties"})
      
      • 1
    • @PropertySource注解属性中不支持使用通配符*,运行会报错

      @PropertySource({"*.properties"})
      
      • 1
    • @PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件

      @PropertySource({"classpath:jdbc.properties"})
      
      • 1

    总结

    知识点1:@Autowired

    名称@Autowired
    类型属性注解 或 方法注解(了解) 或 方法形参注解(了解)
    位置属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面
    作用为引用类型属性设置值
    属性required:true/false,定义该属性是否允许为null

    知识点2:@Qualifier

    名称@Qualifier
    类型属性注解 或 方法注解(了解)
    位置属性定义上方 或 标准set方法上方 或 类set方法上方
    作用为引用类型属性指定注入的beanId
    属性value(默认):设置注入的beanId

    知识点3:@Value

    名称@Value
    类型属性注解 或 方法注解(了解)
    位置属性定义上方 或 标准set方法上方 或 类set方法上方
    作用为 基本数据类型 或 字符串类型 属性设置值
    属性value(默认):要注入的属性值

    知识点4:@PropertySource

    名称@PropertySource
    类型类注解
    位置类定义上方
    作用加载properties文件中的属性值
    属性value(默认):设置加载的properties文件对应的文件名或文件名组成的数组
  • 相关阅读:
    OpenMMlab导出yolov3模型并用onnxruntime和tensorrt推理
    L1-095 分寝室(PTA)
    C++ 配置VSCode开发环境
    Vue3+Ts实现父子组件间传值的两种方式
    BlenderGIS插件 城市建筑3D模型自动生成 教程
    树和二叉树的相关概念及结构
    Redis学习笔记
    uni-app进阶之生命周期【day8】
    Redis 主从架构
    Compose学习-> Text()
  • 原文地址:https://blog.csdn.net/zyb18507175502/article/details/125786856