# 解析java注解
**Repository Path**: miuu_note/parsing_java_annotations
## Basic Information
- **Project Name**: 解析java注解
- **Description**: 利用反射,解析java注解
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2019-04-24
- **Last Updated**: 2020-12-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
[TOC]
> 简介:[慕课网学习笔记]
在项目开发中,注解的使用无处不在。注解的使用简化了代码,减少了程序员的工作量。本课程带领小伙伴们全面认识Java的注解,包括为什么使用注解、Java中的常见注解、注解的分类和如何自定义注解,最后通过一个实战案例来演示注解在实际项目中的应用。
## 第1章 概述
### 1-1 Java注解概述
#### 1)为什么要学习注解?它的好处?学完能做什么?
1.能够读懂别人写的代码,特别是框架相关的内容
2.让编程更新简洁,代码更加清晰,如使用注解可以把配置文件或者写的程序的逻辑省略掉
3.显得专业,有水准(spring\mybatis应用了大量的注解)
4.会使用自定义注解来解决问题
#### 2)概念:注解是java1.5版本引入的,Java提供了一种原程序中的元素关联任何信息和任何元数据的方法.
## 第2章 Java中的常见注解
### 2-1 JDK中的注解
@Override 使用的实现类,表示方法覆盖
@Deprecated 使用的接口,表示方法过时
@Suppvisewarnings
使用:@SuppressWarnings("deprecation") 在方法上使用,压制警告提示
### 2-2 Java第三方注解
常见第三方注解
```mermaid
graph LR
常见第三方注解:
A[Spring]
A --> a-1[autowird]
A --> a-2[Service]
A --> a-3[Repository]
B[Mybatis]
B --> b-1[InsertProvider]
B --> b-2[UpdateProvider]
B --> b-3[Options]
```
如mybatis使用配置文件和注解形式的不同:
```java
//类的实现:
public class UserManagerImpl implements UserManager {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
```
```xml
//配置文件
```
```java
//引入@Autowired注解
public class UserManagerImpl implements UserManager{
@Autowired
private UserDao userDao;
...
//自动将实例注入到spring容器中
}
```
## 第3章 注解的分类
### 3-0 原理
官方定义的,类似于在代码中打一个标记,有分编译时和运行时。
对注解进行解析的代码在编译器和运行时中,简单说就是编译器和运行时发现类似的@xxx就看看是不是有对应的注解类型,有就对其进行相应的处理。
至于如何去找这个注解标记,那就去看看编译器和jvm的源码了,一般学习不用纠结。
就好比定义一个整型变量就是用int,这就是语法!
如果第三方的话,一般是属于运行时注解,通过反射调用的。即通过反射调用某个方法,加载某个类等等操作的时候,获取注解信息(反射api有提供方法获取),然后针对相应的注解进行处理。
### 3-1 Java注解的分类
```mermaid
graph TB
A[按照运行机制分类]
A-->a1[源码注解]
A-->a2[编译时注解]
A-->a3[运行时注解]
B[按照来源分类]
B-->b1[来自JDK的注解]
B-->b2[来自第三方的注解]
B-->b3[自定义注解]
```
#### 含义:
**源码注解**: 注解只在源码中存在,编译成.class(二进制字节码)文件就不存在了.
**编译时注解**:注解在源码和.class文件中都存在 (jdk注解属于编译时注解 @Override等)
**运行时注解**:在运行阶段还起作用,甚至会影响运行逻辑的注解.(如@Autowired,在程序运行时,把成员变量自动注入到spring容器中)
**元注解**:注解的注解(自定义注解用到)
## 第4章 自定义注解
### 4-1 Java自定义注解
#### 语法要求
```java
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
```
使用@interface关键字定义注解
成员以无参无异常方式声明
可以给成员指定一个默认值
成员的类型是受限的,合法的类型包含原始类型(int,double等基本数据类型)及String,Class,Annotation,Enumeration
如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=),
如@RequestParam(value = "organizationId",required = false) Long organizationId,其value可以省略
没有成员的注解,称为标识注解,如@Deprecated即为此类注解,标识过期
#### 详解:
1) @Target({ElementType.METHOD,ElementType.TYPE})
注解作用域列表:
CONSTRUCTOR - 构造方法声明
FIELD - 字段声明
LOCAL_VARIABLE - 局部变量声明
METHOD - 方法声明
PACKAGE - 包声明
PARAMETER - 参数声明
TYPE - 类,接口
2)@Retention(RetentionPolicy.RUNTIME) [运行机制-生命周期]
SOURCE -只在源码显示,编译时会丢弃
CLASS - 编译时会记录到class中,运行时忽略
RUNTIME - 运行时存在,可以通过反射读取
3)@Inherited: 允许子类继承
4)@Documented 生成javadoc时会包含注解信息
### 4-2 使用自定义注解
使用注解的语法:
@<注解名>(<成员名1>=<成员值1>,<成员名2>=<成员值2>,...)
```java
@Description(desc=‘“I am eyeColor”,author=“Mooc boy”,age=18)
public String eyeColor(){
return “red”;
}
```
### 4-3 解析注解
#### 概念:
通过反射获取类,函数或成员上的运行时注解信息从而实现动态控制程序运行的逻辑.
```java
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String value();
}
```
```java
public interface Person {
/** 定义三个方法 */
public String name();
public int age();
@Deprecated
public void sing();
}
```
```java
@Description("I am class annotation")
public class Child implements Person {
@Override
@Description("I am method annotation")
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
@Override
public void sing() {
}
}
```
```java
public class ParseAnn {
public static void main(String[] args) {
//1.使用类加载器加载类(反射)
try {
Class c = Class.forName("com.testmiuu.club.annotation.Child");
//2.找到类上面的注解
//判断这个类上是否存在Description的注解
boolean isExist = c.isAnnotationPresent(Description.class);
if(isExist) {
//拿到注解实例 需要强转
Description d = (Description) c.getAnnotation(Description.class);
System.out.println(d.value());
}
//4.找到方法上的注解
//遍历所有的方法
Method[] ms = c.getMethods();
for (Method m : ms) {
//指定拿Description的注解
boolean isMExist = m.isAnnotationPresent(Description.class);
if(isMExist) {
Description d = (Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
//另外一种解析方法
for (Method m : ms) {
Annotation[] as = m.getAnnotations();
for (Annotation a : as) {
//遍历查找Description的注解
if(a instanceof Description) {
Description d = (Description)a;
System.out.println(d.value());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
```
@Inherited:在实现接口没有作用,只有实现的是继承类才有作用
只会继承类上面的注解,而不会继承方法上的注解
## 第5章 项目实战
### 5-1 项目需求
#### 项目说明
一个公司的持久层架构,用来代替Hibernate的解决方案,核心代码就是通过注解来实现的.
#### 需求
1.有一张用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号.
2.方便的对每个字段或字段的组合条件进行检索,并打印出SQL.
3.使用方式要足够简单,见代码示例.
### 5-2 项目实现(上)
首先,考虑代码如何与数据库进行映射(表名,字段名)
Filter类和数据库表结构相似,写上注解
@Table(“user”)
@Column(“id”)
```java
@Setter
@Getter
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
}
```
```java
/**
* Table
* @author yuzhong 2019/4/23
* 三方认证
*/
@Target({ElementType.TYPE}) //ElementType.TYPE 表示作用域是类或接口
@Retention(RetentionPolicy.RUNTIME) //生命周期
public @interface Table {
String value();
}
```
```java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
```
接下来,考虑query()方法的实现,返回SQL
### 5-3 项目实现(下)
```java
package com.testmiuu.club.hibernate;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author yuzhong 2019/4/23
*/
public class Test {
public static void main(String[] args) {
Filter f1 = new Filter();
f1.setId(10); //查询id为10的用户
f1.setEmail("hha@qq.com");
Filter f2 = new Filter();
f2.setUserName("lucy"); //模糊查询用户名为lucy的用户
Filter f3 = new Filter();
f3.setEmail("2211735722@qq.com,yuzhong@163.com,yuzhong@sina.com"); //查询邮箱为其中任意一个的用户
String sq1 = query(f1);
String sq2 = query(f2);
String sq3 = query(f3);
System.out.println(sq1);
System.out.println(sq2);
System.out.println(sq3);
Student s1 = new Student();
s1.setId(1);
s1.setUserName("快乐");
s1.setAddress("上海");
System.out.println(query(s1));
}
private static String query(Object f) {
StringBuilder sb = new StringBuilder();
//1.获取到class
Class c = f.getClass();
//2.获取到table的名字
boolean exists = c.isAnnotationPresent(Table.class);
if (!exists) {
return null;
}
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
sb.append("select * from ").append(tableName).append(" where 1=1");
//3.遍历所有的字段
Field[] fArray = c.getDeclaredFields();
for (Field field : fArray) {
//4.处理每个字段对应的SQL
//4.1 拿到字段名
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
Column annotation = field.getAnnotation(Column.class);
String columnName = annotation.value();
//4.2 拿到字段值
//取字段的名称
String fieldName = field.getName();
//获取get方法的名字,通过反射调用这个方法,拿到对应的值 get+字段名 首字母大写
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
//4.3 拼装SQL
if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
continue;
}
sb.append(" and ").append(columnName);
//判断字符串要加单引号
if (fieldValue instanceof String) {
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sb.append(" in(");
for (String v : values) {
sb.append("'").append(v).append("'").append(",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
} else {
sb.append("=").append("'").append(fieldValue).append("'");
}
} else if (fieldValue instanceof Integer) {
sb.append("=").append(fieldValue);
}
}
return sb.toString();
}
}
```
## 第6章 课程总结
### 6-1 课程总结
#### 1.认识注解
#### 2.注解的作用范围@Target和生命周期@Retention
作用范围: 包/类/字段/方法/方法的参数/局部变量
生命周期:源文件SOURCE/编译CLASS/运行RUNTIME
#### 3.能读懂注解
#### 4.能在实际项目中用到注解解决问题,并能自定义注解