概念:java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法
JDK内置系统注解:
@Override 用于修饰此方法覆盖了父类的方法;
@Deprecated 用于修饰已经过时的方法;
@Suppvisewarnings 用于通知java编译器禁止特定的编译警告。
注解按照运行机制划分
源码注解:注解只在源码中存在,编译成.class文件就不存在了;
编译时注解:注解在源码和.class文件中都存在(例:JDK自带注解);
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解(Autowired);
自定义注解的元注解:
- @Target—作用域(constructor(构造方法声明),field(字段声明),local_variable(局部变量声明),method(方法声明),package(包声明),parameter(参数声明),type(类,接口声明))
- @Retention—生命周期(source:只在源码显示,编译时会丢弃。class:编译时会记录到class中,运行时忽略。runtime:运行时存在,可以通过反射读取)
- Inherited—标识注解(允许子类继承)
- Documented—生成Javadoc
- 若成员只有一个,必须为value,这个参数赋值可以不写value=XXX
成员以无参无异常方式声明。成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation, Enumeration.
上面用了注解,程序中获得注解信息的方法是反射。
Class cls = Class.forName(“”);//使用类加载器加载类
cls.isAnnotationPresent(xx.class);//判断cls这个类上是否有xx的注解,找到类上注解
xx a = (xx)cls.getAnnotation(xx.class);//取得其上的注解
下面附一个实例,是框架如何使用注解描述参数。
结构图
A.java
package javaBasic;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER ,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface A {
String value();
String name() default "bingone";
int age() default 20;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
B.java
package javaBasic;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER ,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface B {
String value();
String name() default "bingone";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
DataClass.java
package javaBasic;
public class DataClass {
public String name;
public int age;
public DataClass(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
RunClass.java
package javaBasic;
public class RunClass {
public static void run(@A("str") String str,@B("age")int age){
System.out.println("In run Method str:" + str + "age:" + age);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
TestClass.java
package javaBasic;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/** * 通过注解传参数 * @author gg_gogoing * */
public class TestClass {
public static void parseMethod(DataClass data,Object obj,String mthname){
//验证是否有注解
//若有则两种方法:
//1. 在编译时刻已经有了对应的表,查表即可
//2. 如下的遍历方式。
if(obj instanceof RunClass){
String str = null;
int age = 0;
Method [] methods = (Method[])obj.getClass().getMethods();
for(Method method :methods){
if(method.getName().equals(mthname)){
Annotation[][] annotations = method.getParameterAnnotations();
for(Annotation[] tt : annotations){
for(Annotation t:tt){
if(t instanceof A){
str = data.name;
}else if(t instanceof B){
age = data.age;
}
}
}
RunClass.run(str, age);
}
}
}
}
public static void main(String[] args) throws Exception, RuntimeException { // 主方法
//不直接传参数,而是将一个类中的数据传入
DataClass dc = new DataClass("gg_gogoing", 20);
parseMethod(dc, new RunClass(), "run");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
日志记录简化,只需要在action或者controller的方法上加logging注解即可。注解:@Logging(description = "{username}登录"),description是注解内容,{}中为动态参数,是传入该方法中的指定po的属性。
注解类代码:
- package cn.com.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 日志注解
- * @author ZhangShaobo
- * @date 2017-09-04
- */
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface Logging {
- String description();
- }
具体的日志实现类,该类中loginfo方法需要在拦截器中调用。
- package cn.com.util;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.apache.poi.ss.formula.functions.T;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import cn.com.annotation.Logging;
- /**
- * 日志注解的具体实现类
- * @author ZhangShaobo
- * @date 2018-09-04
- */
- public class LogAnnoUtil {
- private static Logger log = LoggerFactory.getLogger(LogAnnoUtil.class);
- /**
- * 根据注解记录日志
- * @param t action对象
- * @param methodName 调用的action方法名称
- * @param map 参数map
- */
- public static <T> void loginfo(T t, String methodName, Map map){
- Method [] methods = t.getClass().getDeclaredMethods();
- for (final Method m : methods) {
- if (m.getName().equals(methodName)) {
- if(m.isAnnotationPresent(Logging.class)){
- String desc = m.getAnnotation(Logging.class).description();
- List<String> list = descFormat(desc);
- for (String s : list) {
- String value = map.get(s).toString();
- desc = desc.replace("{"+s+"}", value);
- }
- // 暂时只是输出到了控制台,具体需要存库或者存缓存,需要改这段代码
- log.info(desc);
- }
- }
- }
- }
- /**
- * 获取日志信息中的动态参数,然后替换
- * @param desc
- * @return
- */
- private static List<String> descFormat(String desc){
- List<String> list = new ArrayList<>();
- Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}");
- Matcher matcher = pattern.matcher(desc);
- while(matcher.find()){
- String t = matcher.group(1);
- list.add(t);
- }
- return list;
- }
- }
拦截器中写调用,以上代码实现的是在struts框架中用注解记录日志,springmvc或者springboot需要适当更改代码。有好的建议或者有不明白的地方欢迎评论沟通!