Spring IoC(控制反转)之常用注解
1. 前言
上一节,我们通过注解的方式,实现了 Spring 对于 bean 的管理,那么如何实现的,是否还记得呢,我们回顾一下
两个重要点:
1. 注解实例化的类上,需要使用一个注解 @Repository
;
2.Spring 的配置文件中,需要使用组件扫描 <context:component-scan>
。
疑问导出:
组件扫描的作用我们清楚,是为了扫描路径之下带有注解的类,但是为什么类上面的注解是 @Repository
呢?或者说,是否还有其余的注解可以实现呢?
本节,我们一起来学习下 Spring IoC 的常用注解。
2. 注解的详解
在我们详细讲解注解之前,首先明确一点:
注解配置和 xml 配置实现的功能都是一样的,只不过实现的方式不同,那么也就是说,xml 文件可以实现的,通过注解都可以完全办得到。比如实例化对象,设置属性,设置作用范围,生命周期的方法执行等等…
2.1 注解分类介绍
按功能划分:
- 创建对象: 对应的就是在 xml 文件中配置的一个 bean 标签,可以定义 id、name、class 等属性;
- 注入数据: 对应的就是在 bean 标签下,使用 property 标签给类中的依赖属性赋值;
- 作用范围: 对应的就是设置 bean 标签的 scope 属性,不设置默认也是单例;
- 生命周期: 对应的就是设置 bean 标签的 init-method 和 destroy-method 方法。
2.1.1 创建对象的注解介绍
从 Spring 的官网得知一段话:
@Repository
注释是针对满足的存储库(也被称为数据访问对象或 DAO)的作用,或者固定型的任何类的标记。
也就是说,我们上一节中使用的注解,一般用于 dao 层使用。那么,我们都知道,JAVAEE 体系结构,一般开发分为三个层级:
- 表现层: 主要作用为处理数据生成静态的页面响应给浏览器展示 ;
- 业务层: 主要作用为业务逻辑代码编写,数据的获取,数据的封装返回等等操作都在这里;
- 持久层: 主要作用为跟数据库打交道,对于数据的持久化操作等。
那么,如果是创建的表现层或者业务层代码,应该使用什么注解呢?
好了,看一下创建对象注解的划分:
- @Component :一般用于通用组件的类上使用的注解;
- @Service : 一般用于业务层类上使用的注解;
- @Controller : 一般用于控制层类上使用的注解;
- @Repository :一般用于持久层类上使用的注解。
官网解释:
Spring 提供进一步典型化注解:
@Component
,@Service
,和@Controller
。@Component
是任何 Spring 托管组件的通用构造型。@Repository
,@Service
和@Controller
是@Component
针对更特定用例的专业化(分别在持久性,服务和表示层)。
慕课解释:
@Component
注解是 Spring 框架中通用的一个注解,用于组件扫描实例化对象使用, 那么其余的三个注解 @Controller
,@Service
,@Repository
都是 @Component
注解的衍生注解,作用跟 @Componet
注解的作用一致。
那么意义在于, 三个注解,对应的是三个开发层级 ,一般来讲我们将 @Controller
作为表现层的使用,@Service
作为业务层的注解,@Repository
作为持久层使用的注解。我们下面通过案例演示一下。
2.2 创建对象的注解
实例说明
四种注解的测试,本节重点讲解创建对象使用的注解,而作用范围 scope 和生命周期的两个注解,我们放在后续对应的小节进行讲解测试。
置于注入数据的注解,是比较重要的一个内容, 我们放在依赖注入这节详细讲解。
各位同学… 稍安勿躁,我们一起慢慢成长。
创建工程省略
我们继续使用上一节的注解工程实例即可,那么为了演示三个注解,我们分别创建三个层级对应的代码:
- 表现层的
UserController
- 业务层的
UserService
- 实现类
UserServiceImpl
持久层 dao 代码已经创建过了,这里不多解释。创建好的所有代码如下:
UserController 代码:
@Controller
public class UserController {
public void saveUser(){
System.out.println("这是controller的执行保存..");
}
}
UserService 和实现类代码:
public interface UserService {
public void saveUser();
}
@Service
public class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("执行service中的保存逻辑");
}
}
项目结构如下:
上面是本案例的工程以及代码结构:
类虽然看起来很多,实际没有业务逻辑代码,只不过在各个层级使用了三个注解来注入到容器,目的是测试当 Spring 的配置文件加载扫描后,是否可以从容器中获取三种注解(@Controller
@Service
@Repository
)注入的 bean 对象。
Tips: Spring 的配置文件
context:component-scan
标签的扫描层级 需要包含三个包路径,例如我的工程实例代码如下:
<context:component-scan base-package="com.wyan"></context:component-scan>
测试类与测试结果:
结论:
可以三个注解都可以将对象注入到 Spring 的容器,那么以后开发时候按照规范或者习惯,分层开发,使用对应的注解。但它并不是必须这么做,你使用任意一种都可以,只不过,代码的可读性会差。
所以,我们一般表现层使用 @controller
,业务层使用 @service
, 持久层使用 @dao
。
至于 @Component
如果有其余的类,不属于三个层级,可以采用 @Component
作为通用组件扫描注入容器。
2.3 注解注入规则
刚刚通过三个注解都可以完成了 bean 的实例化注入,通过测试代码也获取到了容器中的三个对象实例,那么这里不知道大家是否发现一个问题:
我们知道,Spring 这个容器本质是个 map 集合来存储实例化后的对象。既然是个 map 集合,就应该对应的有 key 和 value。
我们都知道 value 肯定是实例化后的 bean ,那么 key 是什么呢?
注入规则:
1. 四种注解都支持 value 的属性作为自定义的 bean id ;
2. 如果 value 属性没有指定,那么默认以类的简单名称(类名首字母小写)作为 bean 对象的 id。
所以我们可以看到:
当我们只使用注解没有自定义 id 的时候可以通过,每个类的首字母小写来获取对象实例,那么如果有了自定义的 id,上述代码是否继续可用呢?
自定义 id 获取实例:
改造类上面的注解,设置自定的 id,更改的注解如下:
@Controll("uc")
@Service("us")
@Repository("ud")
测试结果:
测试结果:
为了区分测试结果,我在测试代码中,只修改了 controller 的获取方式,将 id 改成了 uc 。service 和 dao 并没有修改。
从控制台打印可以看到,只有 controller 对象可以成功获取,service 和 dao 都失败了,因为我们已经使用了自定义的 id,所以容器中没有默认的以类名作为 id 的 bean 对象实例。
3. 小结
本章节重点讲解注解的使用:
- Spring 支持的注解有四种分类;
- Spring 创建对象的注解四种分类;
- Spring 创建对象注入容器的规则。
学习的苦只是一时之苦,学不到的苦是一世之苦,与君共勉…