Java进阶学习第二十节——自定义注解与反射解析注解

教学内容

  • sping boot底层技术介绍
  • 为什么要使用注解
  • JDK中见的注解
  • 注解作用域
  • 自定义注解三步曲
  • 自定义注解并使用注解
  • 反射解析注解
  • 反射解析注解综合案例
  • 只有一个成员的注解
  • 注解允许子类继承
  • 生成API文档时包含注解
  • 怎样生成Java API文档

spring boot使用到的底层技术主要有:

  • 1.反射 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
  • 2.注解 一种代码级别的说明,它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
  • 3.java配置 Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置,他的实现主要依赖于2个注解:
    • 1.@Configuration 声明当前类是一个配置类,相当于spring配置的xml文件
    • 2.@Bean注解在方法上,声明当前方法返回值为一个Bean

为什么要使用注解,学习注解有什么好处?

  • 1.能够读懂别人的代码,特别是框架相关的代码
  • 2.spring boot中大量的使用了注解来代替xml配置
  • 3.让编程更加简洁,代码理解更加清晰
  • 4.使用注解显得高大上啊,高别人一头,高工资就出来了

JDK中常见的注解

注解 作用
@Override 重写
@Deprecated 过时
@SuppressWarnings 去掉警告
  • 定义一个Coder类,里面有2个用@Override注解的过时的方法和一个普通方法
public class Coder {
	
	@Deprecated
	void sleep(){};
	
	@Deprecated
	void eat(){};
	
	void coding(){};
}
  • 写2个类来继承Coder类,用@Override重写父类方法一个加@SuppressWarnings注解,一个不加
  • 没加@SuppressWarnings注解的会发出警告The method sleep() from the type Coder is deprecated
public class Coder1 extends Coder{
	@Override
	void sleep() {
		super.sleep();
	}
}
  • @SuppressWarnings注解可以加在类上也可以加在方法上,加在类上全类里忽略警告,加在方法上,忽略该方法警告
@SuppressWarnings("deprecation")
public class Coder2 extends Coder{
	@Override
	void sleep() {
		super.sleep();
	}
	@Override
	void eat() {
		super.eat();
	}
}

注解的作用域

  • 源码注解(SOURCE)只在源码中显示,编译时丢弃(了解)
  • 编译时注解(CLASS)编译时会记录到.class文件中,运行时丢弃(了解)
  • 运行时注解(RUNTIME)运行时存在,可通过反射解析(重点)

自定义注解三步曲

  • 元注解(注解的注解)
  • 使用自定义注解
  • 反射解析注解

自定义并使用注解

在这里插入图片描述

  • 元注解:可以注解(动词)到注解(名词)上的注解(名词)就叫元注解,也就是说元注解的@Target必然有一个值是 ANNOTATION_TYPE

  • @Target注解表示该注解能加到哪些属性上

  • @Retention表示该注解的作用域

  • @Inherited表示允许子类继承

  • @Documented表示生成javadoc时会包含注释

  • 定义注解用@interface关键字

  • 里面的字段用 类型 名字();格式定义

  • 一般我们会给字段一个默认值,格式为:类型 名字()default 默认值;

  • 注解有一个比较特殊的字段名叫 value(); 如果注解只有一个字段,那么这个字段建议使用value(),如果使用注解时,没有指明字段名,那么默认的就是给value()赋值

  • 自定义一个注解

@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
@Inherited
public @interface Introduce {
	String description() default "";
	String author() default "";
	int value() default 20;
}
  • 使用注解
@Introduce(20)
public class Book {
	@Introduce(author="lzl", description="这是学习注解的代码",value=200)
	String name;
	
	@Introduce(author="haha",description="书里有什么内容呢", value=20000)
	public void show() {
	}
}

反射解析注解

  • 使用反射可以解析运行时注解,根据解析的结果来确定程序的功能,其作用与解析XML没有区别
  • 要求掌握类、字段、和方法上面的注解
  • 1.解析类上面的注解
/*
 * 解析类上面的注解
 * 1.先通过反射获得该类
 * 2.判断该类上是否有指定注解类
 * 3.如果有该注解,那么就获得该注解
 * 4.获得注解里面字段的值
 */
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void analyticalType() throws ClassNotFoundException {
	Class class1 = Class.forName("com.fs.pojo.Book");
	boolean flag = class1.isAnnotationPresent(Introduce.class);
	if (flag) {
		Introduce introduce = (Introduce) class1.getAnnotation(Introduce.class);
		String author = introduce.author();
		String description = introduce.description();
		int value = introduce.value();
		System.out.println("author:" + author + ",decription:" + description + ",value:" + value);
	}
}
  • 2.解析方法上的注解
/*
 * 解析方法上的注解
 * 1.先通过反射获得该类
 * 2.再通过反射获得该类的方法
 * 3.判断该上方法是否有指定注解
 * 4.如果有注解,那么获得该方法上的注解
 * 5.解析获得该注解字段属性
 * 
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void analyticalMethod() throws ClassNotFoundException, NoSuchMethodException, SecurityException {
	Class class1 = Class.forName("com.fs.pojo.Book");
	Method method = class1.getMethod("show");
	boolean flag = method.isAnnotationPresent(Introduce.class);
	if (flag) {
		Introduce introduce = (Introduce) method.getAnnotation(Introduce.class);
		String author = introduce.author();
		String description = introduce.description();
		int value = introduce.value();
		System.out.println("author:" + author + ",decription:" + description + ",value:" + value);
	}
}
  • 3.解析字段上的注解,方式与解析方法一致,关键字:Field,建议学生自己写
/*
 * 解析方法上的注解
 * 1.先通过反射获得该类
 * 2.再通过反射获得该类的字段
 * 3.判断该字段上是否有指定注解
 * 4.如果有注解,那么获得该字段上的注解
 * 5.解析获得该注解字段属性
 */
@Test
@SuppressWarnings("rawtypes")
public void analyticalField() throws ClassNotFoundException, NoSuchFieldException, SecurityException {
	Class class1 = Class.forName("com.fs.pojo.Book");
	Field field = class1.getField("name");
	boolean flag = field.isAnnotationPresent(Introduce.class);
	if (flag) {
		Introduce introduce = (Introduce) field.getAnnotation(Introduce.class);
		String author = introduce.author();
		String description = introduce.description();
		int value = introduce.value();
		System.out.println("author:" + author + ",decription:" + description + ",value:" + value);
	}
}

只有一个成员的注解,强烈建议字段设置为value(),虽然设置为其他好像也没错。

加了@Inherited元注解的注解,允许子类继承其注解

  • 我们在Book类上加了注解,并设置了值
  • 我们再创建一个JavaBook类来继承Book类,没写任何东西
  • 解析JavaBook类上的注解,发现也存在注解并且值和其父类Book类一致

public class AnalyticalInherited {
	@Test
	public void analyticalInherited() throws ClassNotFoundException {
		Class class1 = Class.forName("com.fs.pojo.JavaBook");
		boolean flag = class1.isAnnotationPresent(Introduce.class);
		if (flag) {
			Introduce introduce = (Introduce) class1.getAnnotation(Introduce.class);
			String author = introduce.author();
			String description = introduce.description();
			int value = introduce.value();
			System.out.println("author:" + author + ",decription:" + description + ",value:" + value);
		}
	}
	
}

如何生成API文档

  • 选中项目–>project–>Generate javadoc–>然后看图在这里插入图片描述–>Document Title取个名字–>finish

  • 生成的API


下面上代码

Description.java

package com.fs.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
public @interface Description {
	int num();
	String[] names() ;
	String clazzName() default "java1809";
}

Description2.java

package com.fs.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**1
 * 
 * 使用时,一个值都不用加
 *
 */
@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
public @interface Description2 {
	int num() default -1;
	String[] names() default {} ;
	String clazzName() default "";
}

Description3.java

package com.fs.annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * 
 * 使用时,如果不指定给某个字段赋值,那么默认就是给value赋值
 *
 */
@Documented
@Retention(RUNTIME)
@Target({ TYPE, FIELD, METHOD })
public @interface Description3 {
	int num() default 0;
	String[] names() default {};
	String clazzName() default "java1809";
	String value() default "";
}

MyClazz.java(使用注解的类)

package com.fs.annotation;

@Description(names = { "老刘", "老许", "老罗" }, num = 20)
public class MyClazz {
	
	@Description3("****99号")
	public String address;
	
	@Description2(num=19,names= {"老八","老平","老毒"},clazzName="1809班")
	public void study() {
		
	}
	
	
}

GetMyClazzAnnotation.java(反射解析注解)

package com.fs.annotation;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;


public class GetMyClazzAnnotation {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
		/**
		 * 1.获得要解析的元素
		 * 2.判断元素上是否有指定注解
		 * 3.如果有,就拿到该注解
		 * 4.解析该注解
		 */
		// 解析类上面的注解
		Class<MyClazz> myClass = MyClazz.class;
		boolean flag = myClass.isAnnotationPresent(Description.class);
		if (flag) {
			Description description = myClass.getAnnotation(Description.class);
			String clazzName = description.clazzName();
			String[] names = description.names();
			int num = description.num();
			System.out.println(clazzName + "班,有 " + num + "个人,其中包括:" + Arrays.toString(names));
		}
		
		// 解析指定字段上面的注解
		Field address = myClass.getDeclaredField("address");
		boolean flag2 = address.isAnnotationPresent(Description3.class);
		if (flag2) {
			Description3 description3 = address.getAnnotation(Description3.class);
			System.out.println("班级地址:" + description3.value());
		}
		
		
		Method method = myClass.getDeclaredMethod("study");
		boolean flag3 = method.isAnnotationPresent(Description2.class);
		if(flag3) {
			Description2 description2 = method.getAnnotation(Description2.class);
			//解析出注解的属性,只是打印了一下
			String className = description2.clazzName();
			String[] names = description2.names();
			int num = description2.num();
			System.out.println(className+",有"+num+"个人,其中包括:"+Arrays.toString(names));
		}
		
	}
}


结果:

java1809班,有 20个人,其中包括:[老刘, 老许, 老罗]
班级地址:****99号
1809班,有19个人,其中包括:[老八, 老平, 老毒]


猜你喜欢

转载自blog.csdn.net/qq_38846837/article/details/85008653