Java 注解,mysql 教程入门到精通
下面举例说明一下,在生活中一个人往往是具有多种身份,如果我把每种身份当成一种注解该如何使用
先声明一个 Persons 类用来包含所有的身份
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}
这里 @Target 是声明 Persons 注解的作用范围,参数 ElementType.Type 代表可以给一个类进行注解
@Retention 是注解的有效时间,RetentionPolicy.RUNTIME 是指程序运行的时候。
Person 注解
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
@Repeatable 括号内的就相当于用来保存该注解内容的容器。
声明一个 Man 类,给该类加上一些身份。
@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public class Man {
String name="";
}
在主方法中访问该注解。
public static void main(String[] args) {
Annotation[] annotations = Man.class.getAnnotations();
System.out.println(annotations.length);
Persons p1=(Persons) annotations[0];
for(Person t:p1.value()){
System.out.println(t.role());
}
}
运行结果
1
CEO
husband
father
son
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation{
int id();
String msg();
}
上面代码定义了 @TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。
赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开。
@TestAnnotation(id=3, msg="hello annotation")
public class Test {
}
所谓的快捷方式就是注解中定义了名为 value 的元素,并且在使用该注解时,如果该元素是唯一需要赋值的一个元素,那么此时无需使用 key=value 的语法,而只需在括号内给出 value 元素所需的值即可。这可以应用于任何合法类型的元素,记住,这限制了元素名必须为 value,简单案例如下
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
int value() default 0;
String name() default "";
}
public class QuicklyWay {
@IntegerVaule(20)
public int age;
@IntegerVaule(value = 10000, name = "MONEY")
public int money;
}
注解是不支持继承的,因此不能使用关键字 extends 来继承某个 @interface,但注解在编译后,编译器会自动继承 java.lang.annotation.Annotation 接口
这里总共定义了 4 个注解来演示注解的声明
定义一个可以注解在 Class,interface,enum 上的注解
定义一个可以注解在 METHOD 上的注解
定义一个可以注解在 FIELD 上的注解
定义一个可以注解在 PARAMETER 上的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetType {
/**
定义注解的一个元素 并给定默认值
@return
*/
String value() default "定义在类接口枚举类上的注解元素 value 的默认值";
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetMethod {
/**
定义注解的一个元素 并给定默认值
@return
*/
String value() default "定义在方法上的注解元素 value 的默认值";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetField {
/**
定义注解的一个元素 并给定默认值
@return
*/
String value() default "定义在字段上的注解元素 value 的默认值";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetParameter {
/**
定义注解的一个元素 并给定默认值
@return
*/
String value() default "定义在参数上的注解元素 value 的默认值";
}
编写一个测试处理类处理以上注解
@MyAnTargetType
public class AnnotationTest {
@MyAnTargetField
private String field = "我是字段";
@MyAnTargetMethod("测试方法")
public void test(@MyAnTargetParameter String args) {
System.out.println("参数值 === " + args);
}
public static void main(String[] args) {
// 获取类上的注解 MyAnTargetType
MyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);
System.out.println("类上的注解值 === " + t.value());
MyAnTargetMethod tm = null;
try {
// 根据反射获取 AnnotationTest 类上的 test 方法
Method method = AnnotationTest.class.getDeclaredMethod("test", String.class);
// 获取方法上的注解 MyAnTargetMethod
tm = method.getAnnotation(MyAnTargetMethod.class);
System.out.println("方法上的注解值 === " + tm.value());
// 获取方法上的所有参数注解 循环所有注解找到 MyAnTargetParameter 注解
Annotation[][] annota
tions = method.getParameterAnnotations();
for (Annotation[] tt : annotations) {
for (Annotation t1 : tt) {
if (t1 instanceof MyAnTargetParameter) {
System.out.println("参数上的注解值 === " + ((MyAnTargetParameter) t1).value());
}
}
}
method.invoke(new AnnotationTest(), "改变默认参数");
// 获取 AnnotationTest 类上字段 field 的注解 MyAnTargetField
MyAnTargetField fieldAn = AnnotationTest.class.getDeclaredField("field").getAnnotation(MyAnTargetField.class);
System.out.println("字段上的注解值 === " + fieldAn.value());
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果
类上的注解值 === 定义在类接口枚举类上的注解元素 value 的默认值
方法上的注解值 === 测试方法
参数上的注解值 === 定义在参数上的注解元素 value 的默认值
参数值 === 改变默认参数
字段上的注解值 === 定义在字段上的注解元素 value 的默认值
学习了上面相关的知识,我们已经可以自己定义一个注解了。其实 Java 语言本身已经提供了几个现成的注解。
@Deprecated
这个元素是用来标记过时的元素,想必大家在日常开发中经常碰到。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量。
@Override
这个大家应该很熟悉了,用于标明此方法覆盖了父类的方法。
@SuppressWarnings
用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。之前说过调用被 @Deprecated 注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过 @SuppressWarnings 达到目的。
@SafeVarargs
参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。
@FunctionalInterface
函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式编程很火,所以 Java 8 也及时添加了这个特性。
函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。
我们进行线程开发中常用的 Runnable 就是一个典型的函数式接口,从源码可以看到它使用了 @FunctionalInterface 注解。
注解的功能很强大,Spring 和 Hebernate 这些框架在日志和有效性中大量使用了注解功能。
注解通常配合反射或者切面一起使用来实现相应的业务逻辑。
我这里向大家演示使用注解和切面进行日志记录的案例
定义一个系统日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/**
操作说明
*/
String value() default "";
}
找到使用了注解的方法,利用切面进行日志记录
这里有些类或者参数只做演示,没有给出具体的实现(如 SysLogMapper、SysLogEntity 等),以看懂注解配合切面使用为目的
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogMapper sysLogMapper;
@Pointcut("@annotation(com.jourwon.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if (syslog != null) {
//注解上的描述
sysLog.setOperation(syslog.value());
}
//保存系统日志
sysLogMapper.insertSelective(sysLog);
}
}
评论