教学内容
- 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个人,其中包括:[老八, 老平, 老毒]