java 代码scope注解_java如何让代码变得优雅——自定义注解

一、什么是注解

java中,注解分两种,元注解和自定义注解。

我们常用的一些注解,如:@Autowired、@Override等都是自定义注解。

二、java的元注解

可以理解为描述注解的注解,除了这几个元注解,所有注解都是自定义注解。

@Document:表示是否将注解信息添加在java文档中

@Target:表示注解用于什么地方。

ElementType.CONSTRUCTOR: 用于描述构造器

ElementType.FIELD: 成员变量、对象、属性(包括enum实例)

ElementType.LOCAL_VARIABLE: 用于描述局部变量

ElementType.METHOD: 用于描述方法

ElementType.PACKAGE: 用于描述包

ElementType.PARAMETER: 用于描述参数

ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明

@Retention:定义该注解的生命周期

RetentionPolicy.SOURCE:在编译阶段丢弃。不写入字节码。如:@Override,@SuppressWarnings都属于这类注解。

RetentionPolicy.CLASS:在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式

RetentionPolicy.RUNTIME:始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解信息。

@Inherited:定义该注释和子类的关系

是一个标记注解,阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

三、自定义注解

自定义注解编写规则:

Annotation型定义为@interface,所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。

参数成员只能用public或默认(default)这两个反问权修饰

参数成员只能用八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这些类型的数组。

要获取类方法和字段的注解信息,必须通过java的反射技术来获取Annotation对象,除此之外没有别的获取注解对象的方法

四、注解实现:记录访问日志

4.1 pom文件

org.springframework.boot

spring-boot-starter-web

org.aspectj

aspectjweaver

1.8.9

org.apache.commons

commons-lang3

com.google.guava

guava

30.1-jre

org.springframework.boot

spring-boot-starter-test

test

复制代码

4.2 自定义一个注解

/**

* @Author: KD

* @Date: 2021/1/1 1:02 下午

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface TheLog {

/**

* 业务对象名称

*

* @return

*/

public String name();

/**

* 描述了如何获取id的表达式

*

* @return

*/

public String idExpression();

}

复制代码

这个类中定义了注解的两个属性,第一个是name,第二个是一个Spel表达式。

4.3 切面

/**

* @Author: KD

* @Date: 2021/1/1 1:02 下午

*/

@Aspect

@Component

public class TheAspect {

private static final Logger LOGGER = LoggerFactory.getLogger(TheAspect.class);

@Autowired

HttpServletRequest request;

@Around("@annotation(com.example.zhujie.anno.TheLog)")//对标注了TheLog注解的方法设置切面

public Object log(ProceedingJoinPoint pjp) throws Exception {

Method method = ((MethodSignature)pjp.getSignature()).getMethod();

TheLog theLog = method.getAnnotation(TheLog.class);

Object response = null;

try {

// 目标方法执行

response = pjp.proceed();

} catch (Throwable throwable) {

throw new Exception(throwable);

}

if (StringUtils.isNotEmpty(theLog.idExpression())) {

SpelExpressionParser parser = new SpelExpressionParser();

Expression expression = parser.parseExpression(theLog.idExpression());

EvaluationContext context = new StandardEvaluationContext();

// 获取参数值

Object[] args = pjp.getArgs();

// 获取运行时参数的名称

LocalVariableTableParameterNameDiscoverer discoverer

= new LocalVariableTableParameterNameDiscoverer();

String[] parameterNames = discoverer.getParameterNames(method);

// 将参数绑定到context中

if (parameterNames != null) {

for (int i = 0; i < parameterNames.length; i++) {

context.setVariable(parameterNames[i], args[i]);

}

}

// 将方法的resp当做变量放到context中,变量名称为该类名转化为小写字母开头的驼峰形式

if (response != null) {

context.setVariable(

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),

response);

}

// 解析表达式,获取结果

String itemId = String.valueOf(expression.getValue(context));

// 执行日志记录

handle(theLog.name(), itemId);

}

return response;

}

private void handle(String name, String theId) {

// 通过日志打印输出

LOGGER.info("theType = " + name + ",theId = " + theId);

}

}

复制代码

这里主要是通过aspectj中的类库来实现反射,获取注解属性,然后解析Spel表达式,最后在handle方法中实现日志的打印输出。

4.4 使用注解

/**

* @Author: KD

* @Date: 2021/1/1 12:41 下午

*/

@Controller

public class TestController {

@GetMapping("test")

@ResponseBody

@TheLog(name="test1",idExpression="#id")

public String test(String id){

return id;

}

}

复制代码

其中idExpression是Spel表达式,"#id"表示获取方法中名为id的局部变量。

访问url:

localhost:8080/test?id=123123

复制代码

日志打印如下:

2021-01-01 13:43:38.157 INFO 52624 --- [nio-8080-exec-1] com.example.zhujie.anno.TheAspect : theType = test1,theId = 123123

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值