注解实现原理

一、前言

关于注解这一块内容,我个人觉得比较模糊的一个原因就是,我知道如何去定义一个注解,如何使用它,至于为什么要这样用?底层原理的话就不太晓得了,更是在学了Spring框架后越发觉得原理很重要,为什么一个配置就可以自动将类进行装载,随用随取呢?

特此记录学习日志,以便查漏补缺。

二、注解回顾

注解的理解

其实注解和注释是很像的,注释是给人看的,注解是给程序看的

来看一段代码

1
2
3
4
5
6
7
8
@Repository
public class Admin extends Entity {
private Integer id;
private String account;
private String password;
private String name;
private String remark;
}

@Repository就是一个注解,通过查看源码,我们发现注解的本质是个接口,该接口继承自Annotation接口。而注解中的属性,本质是接口中的抽象方法

1
2
3
4
5
6
7
8
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(annotation = Component.class)
String value() default "";
}

属性只能是以下几种数据类型:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上类型的数组

元注解(用于描述注解的注解)

  • @Target:描述注解能够作用的位置
  • ElementType取值:Type(可以作用于类);METHOD(可作用于方法);FIELD(可作用于成员变量)

  • @Retention:描述注解被保留的阶段
  • @Documented:描述注解是否被抽取到api文档中
  • @Inherited:描述注解是否被子类继承

三、自定义注解的使用

案例1.

使用测试框架测试方法,生成日志文件

案例2.

读取注解并根据字段上的注解改变字段的值

image

四、Spring中的注解

我们一般就是使用注解,对于注解的定义和读取这块一般都是框架什么的给我们搞定了,我们不看源码一般不知道是怎么回事的,也就不清楚注解到底是怎么运行起来的,简单的理解就是注解需要靠反射去读取,然后做相应的处理。

常用注解

1.类型类注解

包括@Repository、@Controller、@Service、@Component

2.设置类注解

重点了解@Autowire、@Qualifier以及bytype、byname等不同的自动装配机制

3.web类注解

关注@RequestMapping、@GetMapping、@PostMapping等路径匹配注解,以及@PathVariable、@RequestParam等参数获取注解。

4.功能类注解

包括@ImportResource引用配置、@ComponentScan注解自动扫描、@Transactional事务注解等等

简单描述常用注解

Spring中类型类注解形式

Spring类型类注解形式包括:

  • @Repository 存储层(持久层,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能 )
  • @Service 业务层 (只是标注该类处于业务逻辑层)
  • @Controller 展示层(前端请求的处理,转发、重定向、包括调用service层的方法)

Spring MVC就是用这些注解对应用进行分层之后,就能将请求处理,义务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。

五、类型类注解原理

那么为何加了个@Repository注解之后,这个UserBean就被装载进Sring容器中生成了一个bean呢?

查看代码发现这些注解都被一个元注解 @Component 标注

@Component是一个泛化的概念,就是跟< bean>一样,可以托管到Spring容器进行管理。仅仅表示一个组件(Bean),可作用在任何层次,当你的一个类被@Component所注释,那么就意味着同样可用上述三个注解来替代。

这些注解在部分功能上是一样的:

@Service, @Controller , @Repository = {@Component + 一些特定的功能}

我们一般使用了注解,在Spring配置文件中就需要配置注解的包扫描:

1
<context:component-scan base-package="xyz.yolin.*"/>

扫描,看看哪个类上使用到了这些注解,扫描到的就需要特殊处理将其注册到Spring容器。想一下,这里Spring其实就会对这个标签进行解析,核心代码:

1
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

然后具体的处理流程就是在ComponentScanBeanDefinitionParser处理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

// Actually scan for bean definitions and register them.
//得到扫描器
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//扫描文件,并转化为spring bean,并注册
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注册其他相关组件
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

return null;
}

上述代码的主要作用就是扫描base-package 下的文件,然后把它转换为Spring中的bean结构,接着将其注册到容器中

请我喝杯咖啡吧~

支付宝
微信