Spring依赖注入(DI)之注解形式

目录

1.前言

2.使用注解实现依赖注入

3. 依赖注入相关的注解

3.1 为什么会需要使用这些注解?

3.2 相关的注解

3.2.1 类的定义注解

3.2.2 注入相关的注解

4. 源码分析

4.1 初始化过程

4.2 Bean的获取

4.3 Autowired的工作原理

4.4 如果确保被自动装配的对象在装配时已经完成初始化?

4.5 如果解决循环依赖问题


1.前言

上一篇文章中《Spring 控制反转(IOC)及依赖注入(DI)》https://blog.csdn.net/luofen521/article/details/120273369介绍了Spring 中IOC及DI的一些概念,并主要分析了XML形式setter方式注入的源码,这篇文章主要介绍使用@Autowired注解字段注入的方式来实现依赖注入的源码。

2.使用注解实现依赖注入

首先我们定义一个StudentDao,用于模拟操作数据库的操作接口,定义了getStudent方法,如下:

public interface StudentDao {
    Student getStudent();
}

然后我们创建一个实现类StudentDaoImpl,在getStudent方法模拟了从数据库获取数据的操作,并在接口的定义加上@Repository注解,如下:

@Repository
public class StudentDaoImpl implements StudentDao {

    @Override
    public Student getStudent() {
        Student student = new Student();
        student.setId(24);
        student.setName("zhangsan");
        return student;
    }
}

Dao层好了,我们现在来定义Service层

首先,创建一个SudentService接口,并定义了getStudent方法,如下:

public interface StudentService {
    Student getStudent();
}

然后创建一个实现类StudentServiceImpl,在类的定义上加上@Service注解,并引入了StudentDao,用于调用Dao层的方法,在定义StudentDao时加上了@Resource注解,代码如下:

@Service
public class StudentServiceImpl implements StudentService{

    @Resource
    StudentDao studentDao;

    @Override
    public Student getStudent() {
        return studentDao.getStudent();
    }
}

到此,Service层也定义好了,我们在测试单元中去验证下,首先也是定义了StudentService ,并在定义上面加上了@Resource注解,然后调用其getStudent方法,代码如下:

@SpringBootTest
class ApplicationTests {

    @Resource
    StudentService studentService;

    @Test
    void contextLoads() {
        Student student = studentService.getStudent();
        System.out.println("学生id:" + student.getId());
        System.out.println("学生姓名:" + student.getName());
    }
}

运行结果如下:

 可以看到,我们没有手动地去创建Servcie、Dao层对应类的对象,而是通过@Repository、@Service、@Resource等注解完成了Bean的创建和关联。

接下来我们看看这些注解的含义

3. 依赖注入相关的注解

3.1 为什么会需要使用这些注解?

在Spring早期的版本,是使用配置文件来管理bean的定义,当项目越来越复杂时,配置文件的管理成了一个很艰巨的任务,如下:

<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="book" class="Book" scope="singleton">
        <property name="bookName" value="平凡的世界"/>
        <property name="author" value="路遥"/>
    </bean>
    <bean id="book2" class="Book2" scope="prototype">
        <property name="bookName" value="平凡的世界"/>
        <property name="author" value="路遥"/>
    </bean>
    <bean id="bookService" class="BookService">
        <property name="book" ref="book" />
    </bean>
</beans>

所以在Spring后面的版本,逐渐引入了注解来标识哪些类需要由Spring容器来管理,这样做主要是为了管理方便。

看下面这样,是不是要简单得多?

@Service
public class StudentServiceImpl implements StudentService {

}

3.2 相关的注解

3.2.1 类的定义注解

其实除了上述用到的Service、Repository注解,还有@Controller、@Component也是常用的注解,它们用于定义类时标识类对象的创建有Spring来统一管理,我们先来看看他们的定义

@Controller

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

@Service

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

@Repository

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

}

会发现@Controller、@Service、@Repository除了名字不同,实现一模一样,并且定义上都使用了 @Component注解。

所以可以这么理解,@Controller、@Service、@Repository =( @Component + 特定的功能),但他们在依赖注入这方面的功能是一样的。

@RestController

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

而RestController结合了@Controller和@ReponseBody,不需要再在方法的返回上加@ReponseBody,下面是@Controller和@RestController的对比

@Controller

@Controller
@RequestMapping("books")
public class SimpleBookController {

    @GetMapping("/{id}", produces = "application/json")
    public @ResponseBody Book getBook(@PathVariable int id) {
        return findBookById(id);
    }

    private Book findBookById(int id) {
        // ...
    }
}

@RestController 

@RestController
@RequestMapping("books-rest")
public class SimpleBookRestController {
    
    @GetMapping("/{id}", produces = "application/json")
    public Book getBook(@PathVariable int id) {
        return findBookById(id);
    }

    private Book findBookById(int id) {
        // ...
    }
}

总结如下:

序号 注解 含义
1 @Component 通用的注解,可以定义Spring管理的任务组件
2 @Controller 用于表现层(spring-mvc中使用),具有转发、重定向等特性
3 @Service 用于业务逻辑层
4 @Repository 用于持久层,并自动处理数据库操作产生的异常
5 @RestControler 用于表现层,集合了@Controller和@ReponseBody的特性

上述主要是类定义时,用到的注解,定义好之后,我们还需要在使用的地方使用引用的注解。

3.2.2 注入相关的注解

上述用到了@Resource注解来把我们需要的对象进行注入,其实常用的还有@Autowired

在上述的代码中,我们使用@Autowired来代替@Resource也是可以的,代码如下:

@Service
public class StudentServiceImpl implements StudentService{

    @Autowired
    StudentDao studentDao;

    @Override
    public Student getStudent() {
        return studentDao.getStudent();
    }
}

那@Resouce和@Autowired有什么区别呢?

首先,我们看看他们的定义,可以看到Resource是java自带的属性;而Autowired是springframework提供的注解

package javax.annotation;
public @interface Resource {



package org.springframework.beans.factory.annotation;

public @interface Autowired {
  

其次,@Autowired是根据类型来装配依赖对象,但是如果我们同一个类型有多个对象会怎么样?比如我们把上面的StudentDaoImpl类再复制一个放到新建的dao文件夹下,这时候就会有有2个类型为StudentDaoImpl的类,这时候我们运行程序就会报下面的错误:

Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: 
Failed to parse configuration class [com.yc.springboot.Application]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: 
Annotation-specified bean name 'studentDaoImpl' for bean class [com.yc.springboot.dao.StudentDaoImpl] 
conflicts with existing, non-compatible bean definition of same name and class [com.yc.springboot.StudentDaoImpl]

	... 63 more

可以看到,在装配StudentDao时,发现有两个相同类型的实现类,所以报错了,这个时候就需要用到@Qualifier注解。首先,我们在dao下的StudentDaoImpl中写value值“StudentDaoImpl2 ”,并设置name为“zhangsan 2”,代码如下:

package com.yc.springboot.dao;


import com.yc.springboot.Student;
import com.yc.springboot.StudentDao;
import org.springframework.stereotype.Repository;

@Repository("StudentDaoImpl2")
public class StudentDaoImpl implements StudentDao {

    @Override
    public Student getStudent() {
        Student student = new Student();
        student.setId(24);
        student.setName("zhangsan 2");
        return student;
    }
}

然后在StudentServiceImpl中引入时,用@Qualifier("StudentDaoImpl2")来指定我们希望装配的对象,如下:

@Service
public class StudentServiceImpl implements StudentService{

    @Autowired
    @Qualifier("StudentDaoImpl2")
    StudentDao studentDao;

    @Override
    public Student getStudent() {
        return studentDao.getStudent();
    }
}

运行结果如下,可以看到,这个时候打印出来的名字为“zhangsan 2”&

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值