文章目录
前言
Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据。为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取注解内容。在编译器生成类文件时,注解可以被嵌入到字节码中。Java虚拟机可以保留注解内容,在运行时可以获取到注解内容
内置注解
java本身提供了7个内置注解,3个在java.lang包,还有4个在java.lang.annotation 中
作用在代码的注解是
@Override
检查方法是不是重写方法,如果父类或实现接口中没有定义就编译报错 |
@Deprecated
不推荐使用这个方法有更好的替代品,就像一起jdk写的方法过时了就使用这个标记了一下
@SuppressWarnings
指示编译器去忽略注解中声明的警告
元注解
就是只有我们写注解类才能使用的注解
@Retention
指定存储的方式
RetentionPolicy.SOURCE
- 标记的注释仅保留在源级别中(源文件),并由编译器忽略。
RetentionPolicy.CLASS
- 标记的注释在编译时由编译器保留,但Java虚拟机(JVM)不保存
RetentionPolicy.RUNTIME
- 标记的注释由JVM保留,因此运行时环境可以通过反射动态获取
@Documented
无论何时使用指定的注释,都应使用Javadoc工具记录这些元素。(默认情况下,注释不包含在Javadoc中。)有关更多信息,请参阅 Javadoc工具页面。
@Target
就像限制使用注解的类型,给的这个注解比如是否可以在类上面使用,方法说明使用等等。有如下阐述
ElementType.TYPE
可以应用于类的任何元素
ElementType.FIELD
可以应用于字段或属性
ElementType.METHOD
可以应用于方法级注释
ElementType.PARAMETER
可以应用于方法的参数
ElementType.CONSTRUCTOR
可以应用于构造函数
ElementType.LOCAL_VARIABLE
可以应用于局部变量
ElementType.ANNOTATION_TYPE
可以应用于注释类型
ElementType.PACKAGE
可以应用于包声明、
@Inherited
@Inherited 注释表明注释类型可以从超类继承。当用户查询注释类型并且该类没有此类型的注释时,将查询类的超类以获取注释类型(默认情况下不是这样)。此注释仅适用于类声明。
请参考:https://www.jianshu.com/p/7f54e7250be3
@Repeatable
java8才有的,有@Repatable标记的注解 可以重复在同一个类、方法、属性等上使用
自定义注解示列(小试牛刀)
如何自定义注解?
使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:
Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型,
而且我们还可以使用default关键字为这个成员变量设定默认值;
/**
* 这是自定义注解的定义和接口i很像
* @author 20190313
*
*/
@Documented
//标记的注释由JVM保留,因此运行时环境可以使用它。
@Retention(RetentionPolicy.RUNTIME)
//可以应用于类的任何元素
@Target({ElementType.TYPE})
public @interface SPI {
//value将成为注解的阐述,如果不传入该阐述默认值为default指定的就是“”啦
String value() default "我是默认值";
}
注意:只有名字为“value”属性,赋值时可以省略属性名
Student使用注解
public class Student {
//添加注解 并传入value值
@FieldName(value = "姓名")//下方省略了get/set方法
private String name;
//这里并没有传入value参数,所以使用的是默认的
@FieldName()
private String pwd;
测试一下
for (Field m : Student.class.getDeclaredFields()) {
//获取注解对象
FieldName fieldName = m.getAnnotation(FieldName.class);
//如果fieldName不为空就说明属性加了这个注解
if(fieldName != null) {
System.out.println("value值为:"+fieldName.value());
}
}
结果:
value值为:姓名
value值为:我是默认值
案例 : 获取类与方法上的注解值:
package com.cpc.annotation.p1;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:20
* @Version: V1.0
*/
public enum TranscationModel {
Read, Write, ReadWrite
}
package com.cpc.annotation.p1;
import java.lang.annotation.*;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:21
* @Version: V1.0
*/
//这是说明这个注解可以使用在啥子地方(类、属性、方法)
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
//注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
//指定被修饰的该Annotation可以被javadoc工具提取成文档.
@Documented
public @interface MyAnnotation1 {
String name();
}
package com.cpc.annotation.p1;
import java.lang.annotation.*;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:22
* @Version: V1.0
*
* MyAnnotation2注解可以用在方法上
* 注解运行期也保留
* 不可继承
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
TranscationModel model() default TranscationModel.ReadWrite;
}
package com.cpc.annotation.p1;
import java.lang.annotation.*;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:24
* @Version: V1.0
*
* MyAnnotation3注解可以用在方法上
* 注解运行期也保留
* 可继承
*/
@Target(ElementType.METHOD)
//这是保存到运行期
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
TranscationModel[] models() default TranscationModel.ReadWrite;
}
package com.cpc.annotation.p1;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:24
* @Version: V1.0
*
* 获取类与方法上的注解值
*/
@MyAnnotation1(name = "abc")
public class Demo1 {
@MyAnnotation1(name = "xyz")
private Integer age;
@MyAnnotation2(model = TranscationModel.Read)
public void list() {
System.out.println("list");
}
@MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
public void edit() {
System.out.println("edit");
}
}
package com.cpc.annotation.p1;
import org.junit.jupiter.api.Test;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:26
* @Version: V1.0
*/
public class Demo1Test {
@Test
public void list() throws Exception {
// 获取类上的注解
MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
System.out.println(annotation1.name());//abc
// 获取方法上的注解
MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
System.out.println(myAnnotation2.model());//Read
}
@Test
public void edit() throws Exception {
//这是获取方法上的注解
MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
//这是拿到注解的属性并且输出
for (TranscationModel model : myAnnotation3.models()) {
System.out.println(model);//Read,Write
}
}
}
案例二:获取类属性上的注解属性值
package com.cpc.annotation.p2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:29
* @Version: V1.0
*/
//@Retention(RetentionPolicy.SOURCE)
//保存到运行事件
@Retention(RetentionPolicy.RUNTIME)
//这是可以应用到属性上
@Target(ElementType.FIELD)
public @interface TestAnnotation {
String value() default "默认value值";
String what() default "这里是默认的what属性对应的值";
}
package com.cpc.annotation.p2;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:30
* @Version: V1.0
*
* 获取类属性上的注解属性值
*
*/
public class Demo2 {
@TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
private static String msg1;
@TestAnnotation("这就是value对应的值1")
private static String msg2;
@TestAnnotation(value = "这就是value对应的值2")
private static String msg3;
@TestAnnotation(what = "这就是what对应的值")
private static String msg4;
}
package com.cpc.annotation.p2;
import org.junit.jupiter.api.Test;
/**
* @Description: 这是获取到类属性上的注解值
* @Author: cpc
* @Date: 2019-11-06 15:30
* @Version: V1.0
*/
public class Demo2Test {
@Test
public void test1() throws Exception {
//这是获取 Demo2 这个类上的 msg1 字典的 TestAnnotation 注解对象
TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
System.out.println(msg1.value());
System.out.println(msg1.what());
}
@Test
public void test2() throws Exception{
TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
System.out.println(msg2.value());
System.out.println(msg2.what());
}
@Test
public void test3() throws Exception{
TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
System.out.println(msg3.value());
System.out.println(msg3.what());
}
@Test
public void test4() throws Exception{
TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
System.out.println(msg4.value());
System.out.println(msg4.what());
}
}
案例三:获取参数修饰注解对应的属性值
package com.cpc.annotation.p3;
import java.lang.annotation.*;
/**
* @Description: 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
* @Author: cpc
* @Date: 2019-11-06 15:32
* @Version: V1.0
*
*/
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
boolean value() default false;
}
package com.cpc.annotation.p3;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:33
* @Version: V1.0
*
* 获取参数修饰注解对应的属性值
*
*/
public class Demo3 {
public void hello1(@IsNotNull(true) String name) {
System.out.println("hello:" + name);
}
public void hello2(@IsNotNull String name) {
System.out.println("hello:" + name);
}
}
package com.cpc.annotation.p3;
import org.junit.jupiter.api.Test;
/**
* @Description: 这是测试获取方法参数上的注解
* @Author: cpc
* @Date: 2019-11-06 15:33
* @Version: V1.0
*/
@SuppressWarnings(value = "unchecked")
public class Demo3Test {
@Test
@SuppressWarnings(value = "unchecked")
public void hello1() throws Exception {
Demo3 demo3 = new Demo3();
//这是获取 demo3 上面的 hello1 方法获取所有类型类string的参数
for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
//这是搞到参数中的 IsNotNull 这个注解
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
//判断是否有这个注解
if(annotation != null){
//获取对应的value值
System.out.println(annotation.value());//true
}
}
}
@Test
@SuppressWarnings(value = "unchecked")
public void hello2() throws Exception {
Demo3 demo3 = new Demo3();
//一样的 这是搞到 hello2 上面的 string 参数注解 ,拿到说有参数
for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
//搞到IsNotNull 这个玩意
IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
if(annotation != null){
//如果不为空就直接输出吧
System.out.println(annotation.value());//false
}
}
}
}
高级:Aop自定义注解的应用
上代码,直接搞
package com.cpc.annotation.springAop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: aop 日志字典类
* @Author: cpc
* @Date: 2019-11-06 15:38
* @Version: V1.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
//这是日志的描述信息
String desc();
}
package com.cpc.annotation.springAop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:38
* @Version: V1.0
*/
@Component
@Aspect
public class MyLogAspect {
/**
* 只要用到了com.cpc.annotation.springAop.MyLog这个注解的,就是目标类
*/
@Pointcut("@annotation(com.cpc.annotation.springAop.MyLog)")
private void MyValid() {
}
@Before("MyValid()")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
System.out.println("[" + signature.getName() + " : start.....]");
System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
}
}
package com.cpc.annotation.springAop;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: cpc
* @Date: 2019-11-06 15:53
* @Version: V1.0
*/
@Component
public class LogController {
@MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
public void testLogAspect(){
System.out.println("这里随便来点啥");
}
}
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Description: 这个类主要是读取配置文件滴
* @Author: cpc
* @Date: 2019-11-06 15:54
* @Version: V1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:applicationContext.xml"})
public class BaseTestCase {
}
import com.cpc.annotation.springAop.LogController;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @Description: 测试aop日志
* @Author: cpc
* @Date: 2019-11-06 15:54
* @Version: V1.0
*/
public class LogControllerTest extends BaseTestCase {
@Autowired
private LogController logController;
@Test
public void testLogAspect(){
logController.testLogAspect();
}
}
AOP + 自定义注解应用篇
-
这是我写的,通过springAOP + 自定义注解 实现数据字典
https://blog.csdn.net/qq_43059674/article/details/102912359 -
刚才那个aop日志非常简单,下面这个是应用在真实项目开发中的:
http://javaxl.com/blog/articles/236 -
ssm中使用拦截器跟自定义注解实现前端token校验
https://blog.csdn.net/weixin_42687829/article/details/102717073