Java注解与原理分析

服务运行时,结合AOP切面编程实现方法的日志采集;

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都食品包装袋小微创业公司专业提供企业网站制作营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

二、注解原理

先来看一个简单的注解使用案例,再细致地分析其中原理,案例并不复杂,就是常见的标注与解析两个关键动作;

public class LogInfo {
@SystemLog(model = "日志模块")
public static void main(String[] args) {
// 生成代理文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 反射机制
Method[] methods = LogInfo.class.getMethods();
for (Method method:methods){
SystemLog systemLog = method.getAnnotation(SystemLog.class) ;
if (systemLog != null){
// 动态代理:com.sun.proxy.$Proxy2
System.out.println(systemLog.getClass().getName());
System.out.println(systemLog.model());
}
}
}
}

这里涉及到两个核心概念:反射机制、动态代理;反射机制可以在程序运行时获取类的完整结构信息,代理模式给目标对象提供一个代理对象,由代理对象持有目标对象的引用;

案例中通过反射机制,在程序运行时进行注解的获取和解析,值得关注的是systemLog对象的类名,输出的是代理类信息;

案例执行完毕后,会在代码工程的目录下生成代理类,可以查看$Proxy2文件;

public final class $Proxy2 extends Proxy implements SystemLog {
public final String model() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}

在对SystemLog解析的过程中,实际上是在使用注解的代理类,$Proxy2继承了Proxy类并实现了SystemLog接口,并且重写了相关方法;有关反射和代理的逻辑,在之前的内容中有详说,此处不赘述;

值得一看是代理类中invoke方法调用,具体的处理逻辑在AnnotationInvocationHandler类的invoke方法中,会对注解原生方法和自定义方法做判断,并对原生方法提供实现;

三、常用注解

1、JDK注解

在JDK中有多个注解是经常使用的,例如Override、Deprecated、SuppressWarnings等;

  • Override:判断方法是否为重写方法;
  • Deprecated:标记过时的API,继续使用会警告;
  • FunctionalInterface:检验是否为函数式接口;
  • SuppressWarnings:代码的警告会静默处理;

这里注意FunctionalInterface注解,从1.8开始引入,检验是否为函数式接口,即接口只能有一个抽象方法,否则编译报错;

2、Lombok注解

在具体的看Lombok组件之前,需要先了解一个概念:代码编译;在open-jdk的描述文档中大致分为三个核心阶段;

第一步:读取命令行上指定的所有源文件,解析为语法树,进行符号表填充;

第二步:调用注解处理器,如果处理器生成任何新的源文件或类文件,编译会重新启动;

第三步:分析器创建的语法树被分析并转换为类文件;

更多细节说明可以参考openjdk文档中Compiler模块的内容,下面再回到Lombok组件上;

Lombok组件在代码工程中的使用非常频繁,通过注解的方式极大的简化Java中Bean对象的编写,提高了效率并且让源码显得简洁;

这里用一段简单的代码演示其效果,在IdKey的类中通过三个常用的Lombok注解,替代了类中很多基础方法的显式生成,查看编译后的文件实际是存在相关方法的;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class IdKey {
private Integer id ;
private String key ;

public static void main(String[] args) {
IdKey idKey01 = new IdKey(1,"cicada") ;
System.out.println(idKey01);
idKey01.setId(2);
idKey01.setKey("smile");
System.out.println(idKey01);
}
}

这里需要了解JDK中注解处理器的相关源码,AbstractProcessor作为超类,编译器在编译时会去检查该类的子类,子类中最核心的是process方法;

-- 1、Lombok处理器
@SupportedAnnotationTypes("*")
public class LombokProcessor extends AbstractProcessor {
private JavacTransformer transformer;
@Override
public boolean process(Set annotations, RoundEnvironment roundEnv) {
transformer.transform(prio, javacProcessingEnv.getContext(), cusForThisRound, cleanup);
}
}

-- 2、AST抽象树
public class JavacTransformer {
public void transform(long priority, Context context, List compilationUnits,
CleanupRegistry cleanup) {
JavacAST ast = new JavacAST(messager, context, unit, cleanup);
ast.traverse(new AnnotationVisitor(priority));
handlers.callASTVisitors(ast, priority);
}
}

-- 3、注解处理抽象类
public abstract class JavacAnnotationHandler {
public abstract void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode);
}

-- 4、Getter注解处理
public class HandleGetter extends JavacAnnotationHandler {
@Override
public void handle(AnnotationValues annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) {
JavacNode node = annotationNode.up();
List onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);
switch (node.getKind()) {
case FIELD:
createGetterForFields(level, fields, annotationNode, true, lazy, onMethod);
break;
}
}
}

IdKey类从简洁的源码编译为复杂的字节码文件,通过注解对结构处理时关联一个核心概念,叫AST抽象树,会涉及到很多语法、词法的解析逻辑;

四、自定义注解

在系统开发中通过自定义注解可以处理各种麻烦的重复逻辑,其最明显的好处就是可以大量地消除冗余的代码块;

1、同步控制

代码中可能存在很多方法是限制重复请求的,加锁处理是很常用的手段,此时完全可以通过注解结合AOP切面编程简化代码的复杂程度;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SyncLock {
String lockKey(); // 锁的Key
int time () default 3000 ; // 有效时间
int retryNum () default 3 ; // 重试次数
}

通过注解标记在方法上,可以极大简化同步锁的编码步骤,只是在读取KEY的时候需要设计好解析规则,结合反射原理进行获取即可;

基于相同的原理,也适应与日志采集、系统告警等功能,在之前的内容中都有详细的总结;

2、类型引擎

在数据处理的逻辑中,经常有这样一种场景,同一份数据要动态推送到多种数据源中存储,比如常见的MySQL表和ES索引双写模式,这就需要对实体对象做不同的解析逻辑;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BizType {
EsIndexEnum esIndexEnum () ; // ES索引解析适配
MySqlTableEnum mySqlTableEnum () ; // MySQL表解析适配
ExcelEnum excelEnum () ; // Excel解析适配
}

首先声明一个类型解析的注解,可以标记在实体对象的字段属性上,然后根据各种数据源的类型枚举,去适配不同解析工厂的执行逻辑,比如常用数据类型、格式、或者完全自定义。

网页标题:Java注解与原理分析
标题来源:http://www.shufengxianlan.com/qtweb/news48/304148.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联