探索如何实现类似@Slf4j那样实现逻辑处理

前情提要:

目的是要在别的项目里调用编写好的记录日志jar包:

一开始想的操作是使用openFeign去远程调用日志项目的接口的。这样项目只要导入我们编写的Client包,注入即可使用了,如下图:
package com.hmall.api.client;

import com.hmall.api.config.DefaultFeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Collection;

// 参数中的url为远程调用地址
@FeignClient(url = "http://192.168.0.23:8080",configuration = DefaultFeignConfig.class)
public interface CartClient {

    @DeleteMapping("/carts")
    void deleteCartItembyIds(@RequestParam("ids") Collection<Long> ids);
}

// 使用
@Autowired
CartClient cartClient;

// 调用
cartClient.deleteCartItembyIds(itemIds);

然后想试下能不能实现类似下面这样去处理代码逻辑,也就是类似Lombok只要添加@Slf4j,不用注入也能直接使用Logger类实例log:
package com.swp.ch.po;

import com.swp.ch.annotation.Hello;

// 自定义的注解
@Hello
public class MyTest {

    public void record(){
        
        // 假设log是要记录的日志
        String log;
        // 自动注入类的实例,类似于@Slf4j的log.info(String msg);
        myWebLog.record(log);
    }
}
 很神奇,于是搜了很久,了解到了Lombok是用注解处理器进行操作的,遂继续深入:
如何自定义注解处理器,并在里面创建新类添加静态方法,像这样调用"MyLog.record(String log) "来记录日志执行相关操作,这个成功了。然后试下能不能改成创建interface,但是interface不能创建静态方法,并且也要使用openFeign调用远程接口,嘻嘻完蛋了(悲)。

分享下如何通过在已有类上添加自定义注解为其创建新类或者接口:

这里附上大佬博客,那时候找资料刷到的:Java注解(三):自定义Java编译时注解处理器_自定义注解处理器-优快云博客icon-default.png?t=O83Ahttps://blog.youkuaiyun.com/u014454538/article/details/122531293?spm=1001.2014.3001.5506

进入正文: 

(1)定义注解:
package com.swp.xy.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// @Target(ElementType.TYPE) 指定注解应用范围为类

// @Retention(RetentionPolicy.SOURCE) 
source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Hello {
}

(2)定义注解处理器,继承AbstractProcessor,重写其中方法(主要为process方法,该方法返回值为boolean类型,返回为true则不会再被别的注解处理器处理):
package com.swp.xy.processor;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.Set;

// 通过@SupportedAnnotationTypes注册注解处理器,与重写getSupportedAnnotationTypes()方法等价
@SupportedAnnotationTypes("com.swp.xy.annotation.Hello")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
@AutoService(Processor.class)
public class HelloProcessor extends AbstractProcessor {
    // 用于消息传递和元素的处理的工具实例
    private Messager messager;
    private Elements elementUtils;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        // 通过init()方法,完成初始化工作
        this.messager = processingEnv.getMessager();
        this.elementUtils = processingEnv.getElementUtils();
        this.filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 一条简单的信息打印,用于辅助process方法是否被执行
        messager.printMessage(Diagnostic.Kind.NOTE, "source version -- " + getSupportedSourceVersion());
        // 遍历注解处理器可以处理的注解,获得被注解说明的元素
        for (TypeElement annotation : annotations) {
            String annotationName = annotation.getSimpleName().toString();
            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
            for (Element element : elements) {
                // 该注解处理器只处理@Hello,而且@Hello的Target为Type,进一步要求为CLASS
                if (element.getKind() != ElementKind.CLASS) {
                    messager.printMessage(Diagnostic.Kind.ERROR, "@" + annotationName + "must be used for a class");
                }
                // 获取被说明类的包名和类名,为创建对应的Hello类做准备
                TypeElement classElement = (TypeElement) element;
                String simpleName = classElement.getSimpleName().toString();
                PackageElement packageElement = elementUtils.getPackageOf(classElement);
                String packageName = packageElement.getQualifiedName().toString();
                // 借助JavaPoet模板引擎,生成对应的Hello类
                TypeSpec typeSpec = generateClassFile(simpleName, packageName);

                // 生成对应的Java文件
                JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();
                try {
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate Java file for class ", element);
                }
            }
        }
        return roundEnv.processingOver();
    }

    private TypeSpec generateClassFile(String simpleName, String packageName) {
        // 定义FieldSpec,存储类名
        FieldSpec nameField = FieldSpec.builder(String.class, "className", Modifier.PRIVATE).build();

        // 定义无参构造函数,自动完成类名的初始化
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addStatement("this.$N = $S", nameField, simpleName)
                .build();

        // 定义sayHello方法
        String msg = "Hello, this is ";
        MethodSpec sayHello = MethodSpec.methodBuilder("sayHello")
                .addModifiers(Modifier.PUBLIC)
                .addStatement("System.out.println($S + className)", msg)
                .build();

        // 创建类
        ClassName helloClass = ClassName.get(packageName, simpleName + "Hello");
        return TypeSpec.classBuilder(helloClass)
                .addModifiers(Modifier.PUBLIC)
                .addField(nameField)
                .addMethod(constructor)
                .addMethod(sayHello)
                .build();
    }
}

PS: 这里还使用了JavaPoet模板简化创建类的过程,TypeSpec是其独有的类。

// JavaPoet对应Maven依赖
<dependency>
   <groupId>com.squareup</groupId>
   <artifactId>javapoet</artifactId>
   <version>1.13.0</version>
</dependency>
(3)使用@AutoService注册,不注册的话会报错,提示"未找到该类":

        (3.1)先引入对应auto-service依赖:

<dependency>
    <groupId>com.google.auto.service</groupId>
    <artifactId>auto-service</artifactId>
    <version>1.1.1</version>
</dependency>

        (3.2)在自定义的注解处理器上加上@AutoService(Processor.class),be like:

@SupportedAnnotationTypes("com.swp.xy.annotation.Hello")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
// 这里
@AutoService(Processor.class)
// 上面那句
public class HelloProcessor extends AbstractProcessor {
(4) 想使用的话要把编写的注解处理器导出为jar包,再在要使用的项目里导入即可。

回到主题,创建新类那不对啊,我要的是往已有的类里面添加一个静态final字段,并且还不用我自己注入那种,然后去搜了如何修改已有类的字段,要使用Javassist或者ASM来修改字节码,这个倒是可以实现不用@Autowired也能自动注入。思路是使用自定义注解处理器+javassist来为已有类添加新字段,并且加在其上面加@Autowired注解,再定义配置类让spring帮我们注入。
但是Javassist是生成新类(悲),要替换原来的类的话要使用热加载hotSwapper,有点过于麻烦了,遂放弃。这里附上Javassist学习链接:Javassist修改已有类加新的属性注解_ctfield添加注解-优快云博客文章浏览阅读2.3k次,点赞3次,收藏3次。一、新建类PersonServicename字段目前是有两个注解Autowired和JsonFormat。package com.david.test.test_springboot_schema.javassist;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import com.fasterxml.jackson._ctfield添加注解https://blog.youkuaiyun.com/u010989191/article/details/111947283

欢迎讨论,求佬分享,目前还是想着用openFeign要简单方便很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值