依赖注入(Dependency Injection, DI)
想象一下你正在做一道菜,比如炒鸡蛋。这道菜的主要材料是鸡蛋和油。在做饭的过程中,你需要这两个材料来完成这道菜。
现在,如果我们把“炒鸡蛋”这个过程看作是一个“客户端”对象,那么“鸡蛋”和“油”就是它的依赖项。在传统的做法中,你可能会在炒鸡蛋的代码中直接写上如何获取鸡蛋和油(比如去冰箱里拿鸡蛋,从橱柜里拿油)。但这样做的话,你的炒鸡蛋过程就和获取鸡蛋、油的具体方式紧密地绑定在一起了。
依赖注入的做法就像是这样:你不再在炒鸡蛋的过程中去管鸡蛋和油是从哪里来的,你只需要告诉别人(比如你的助手或者一个智能厨房设备):“嘿,我需要鸡蛋和油来炒鸡蛋。” 然后,这个“别人”就会帮你准备好鸡蛋和油,并且在你开始炒鸡蛋的时候递给你。
在这个过程中,“别人”就扮演了依赖注入容器的角色,它负责创建和管理依赖项(鸡蛋和油),并在需要的时候将它们注入到客户端对象(炒鸡蛋的过程)中。
一、
在Spring框架中,@Autowired
和@Qualifier
注解经常一起使用,以解决当Spring容器中存在多个相同类型的bean时,如何明确指定注入哪一个bean的问题。
-
@Autowired
注解用于自动装配Spring容器中的bean。当Spring看到@Autowired
注解时,它会尝试将标记了该注解的字段、setter方法或构造器参数与容器中的bean进行匹配,并自动注入相应的bean。如果没有找到匹配的bean,Spring会抛出异常(除非设置了@Autowired(required = false)
)。 -
@Qualifier
注解用于指定注入bean的名称(或ID)。当存在多个相同类型的bean时,仅使用@Autowired
可能不足以确定要注入哪一个bean,因为Spring无法区分它们。这时,可以通过@Qualifier
注解来明确指定要注入的bean的名称。 -
@Autowired @Qualifier("BookServiceImpl") private BookService bookService;
这里,bookService
字段被标记为自动装配,并且通过使用@Qualifier("BookServiceImpl")
指定了要注入的bean的名称是BookServiceImpl
。这意味着Spring将查找容器中名为BookServiceImpl
的BookService
类型的bean,并将其注入到bookService
字段中。
这种方式特别有用,当你有多个实现了同一接口的bean,并且想要根据名称来选择注入哪一个时。例如,如果你有两个BookService
的实现类,一个用于处理电子书,另一个用于处理纸质书,你可以通过为它们指定不同的名称(如eBookServiceImpl
和paperBookServiceImpl
),并在需要时通过@Qualifier
注解来指定注入哪一个。
二、
在Spring Boot中,将BookServiceImpl
变成Bean(即让Spring容器管理BookServiceImpl
的实例)通常非常直接,因为Spring Boot的自动配置特性会自动扫描并注册带有@Component
、@Service
、@Repository
或@Controller
等注解的类作为Bean。
如果你的BookServiceImpl
类实现了某个接口(比如BookService
),并且你想要它作为一个Bean被Spring容器管理,你可以简单地在BookServiceImpl
类上添加@Service
注解(或者@Component
,但@Service
更具体地表示这是一个服务层组件)。
// 假设你有一个BookService接口
public interface BookService {
// 定义一些方法
void addBook(Book book);
List<Book> getAllBooks();
// ... 其他方法
}
// BookServiceImpl实现了BookService接口,并且被@Service注解标记为Spring Bean
@Service("bookServiceImpl") // 注意:这里的"bookServiceImpl"是bean的名称,是可选的
public class BookServiceImpl implements BookService {
// 实现接口中的方法
@Override
public void addBook(Book book) {
// 实现添加书籍的逻辑
}
@Override
public List<Book> getAllBooks() {
// 实现获取所有书籍的逻辑
return new ArrayList<>(); // 示例返回空列表
}
// ... 其他方法的实现
}
在这个例子中,@Service
注解告诉Spring这个类是一个服务层组件,应该被注册为一个Bean。"bookServiceImpl"
是该Bean的名称,但它是可选的。如果你不提供名称,Spring将使用类的简单名称(首字母小写)作为Bean的名称,即在这个例子中默认名称将是bookServiceImpl
(注意Spring会自动将首字母小写)。
一旦BookServiceImpl
被注册为Bean,你就可以在Spring容器中的其他地方通过自动装配(比如使用@Autowired
注解)来注入它了。
@RestController
public class BookController {
@Autowired
private BookService bookService; // Spring将自动注入名为"bookServiceImpl"的Bean
// ... 其他代码
}
在这个BookController
中,我们使用了@Autowired
注解来自动装配BookService
类型的Bean。由于Spring容器中只有一个BookService
类型的Bean(即BookServiceImpl
),所以Spring会自动将其注入到bookService
字段中。如果你提供了Bean的名称(如上面的"bookServiceImpl"
),并且需要明确指定注入哪个Bean,你可以使用@Qualifier
注解,但在这个例子中它是不必要的。
在Spring框架中,@Mapper
和@Repository
注解虽然都用于标记Spring容器中的Bean,但它们各自有不同的用途和适用场景。然而,在实际应用中,特别是在使用MyBatis作为ORM框架的Spring Boot项目中,这两个注解经常一起出现,但它们的意义和作用有所不同。
@Mapper
@Mapper
注解是MyBatis提供的,用于标记MyBatis的Mapper接口。当Spring Boot项目集成了MyBatis时,Spring Boot会自动扫描带有@Mapper
注解的接口,并将它们注册为Bean,以便在需要时注入到其他组件中。这样,你就可以在Service层或其他地方通过自动装配来使用这些Mapper接口了。
@Repository
@Repository
注解是Spring框架提供的,用于标记在数据访问层(DAO层)的类上。虽然它本身并不提供特别的功能(除了将类标记为Spring管理的Bean之外),但它主要用于向Spring表明该类是一个DAO组件,用于访问数据库。此外,@Repository
注解还允许Spring在运行时捕获数据访问异常,并将它们转换为Spring的DataAccessException
异常,这是Spring数据访问异常层次结构中的根异常。
一起使用
在Spring Boot项目中,当你使用MyBatis作为ORM框架时,你可能会看到@Mapper
和@Repository
注解一起使用在Mapper接口上。虽然这在实际中并不常见(因为@Mapper
已经足够将Mapper接口注册为Bean了),但这样做有一些潜在的好处:
- 语义清晰:
@Repository
注解明确指出了这是一个数据访问层的组件,有助于代码的可读性和维护性。 - 异常转换:如前所述,
@Repository
注解允许Spring捕获并转换数据访问异常。
然而,需要注意的是,在Spring Boot项目中,如果你已经配置了MyBatis的自动扫描(比如通过@MapperScan
注解),那么通常不需要在Mapper接口上显式地添加@Mapper
或@Repository
注解。Spring Boot会自动识别并注册这些Mapper接口为Bean。
结论
在大多数情况下,对于MyBatis的Mapper接口,你只需要使用@Mapper
注解(或者通过配置来自动扫描Mapper接口)。然而,如果你想要更明确地表明这些接口是数据访问层的组件,并且希望利用@Repository
注解提供的异常转换功能,那么你可以将这两个注解一起使用。但在实践中,这通常不是必需的。