注解中使用函数类型的成员

注解中其实不能使用类型为函数的成员

你是否也曾想在注解中使用函数变量,像这样

public @interface HeadSpecialSet {
    BiConsumer<ReqHead, String> setter();
    String param();
}

 而事实上会报编译错误。

需求

在我工作的过程中,不止一次希望java能支持这样的功能,之前碰到该问题,都没有使用函数的方式去解决该问题,而是使用反射,线程变量之类的方式解决的该问题。下面我们看看一个例子,看是否能使用函数相关的功能解决一些实际问题。

这里是一个分布式情况下通讯的需求场景,A系统会调用BCDE..系统:

A系统(本系统) -> B系统

                             -> C系统

                             -> D系统

1、RPC框架是架构组写的,提供jar包,子系统引用RPC框架,调用RpcApi.send(t)即可。

public class RpcApi {
    public static <T> String send(T body) {
        Request<T> objectRequest = generateReq(body);
        return sendNative(objectRequest);
    }

    private static <T> Request<T> generateReq(T t) {
        Request<T> req = new Request<>();
        req.body = t;
        // head都是由架构组写的,passwd,username都是通过认证体系获取,ip为执行机ip
        req.head = new ReqHead();
        req.head.setIp("本地IP");
        req.head.setPassword("登录用户密码");
        req.head.setUsername("登录用户名");
        return req;
    }

    private static <T> String sendNative(Request<?> request) {
        return "对方系统返回结果";
    }
}

public class Request<T> {
    public ReqHead head;
    public T body;
}

2、给B,C,D系统的req, resp对象由业务系统发布,如下是a系统查B系统的资源的入参结构

public class QueryBResource {
    // 按条件查询
    Date conditionTimeBegin;
}

3、A本可以愉快地像下面代码一样调用B,C,D系统

SendApi.send(new QueryBResource());

4、忽然一天,需求上线E系统,且E系统不支持统一认证,并给A系统一个用户名eadmin,要求访问E系统的时候统使用eadmin替换Head里的当前用户发送到E系统,假设E系统接收的参数为QueryEResource 

解决方案

5、业务系统调用api时,只能封装body,无法封装Request<T>,如何让业务系统代码能通知到api代码执行的时候去修改Head里的值呢,我的第一首选肯定是使用注解+函数,但上面已经看到注解中不能使用函数

6、注解虽然不支持函数,但支持Class,Enum等高级类型,这里先使用Class与method字面量注解完成需求内容。需要架构组提供HeadSpecialSet 注解,并在api代码中获取body 的注解内容,然后使用反射完成值的重赋予。如下,在QueryResource(Request body)上使用注解通知api模块,需要将Head里的username设置为固定值,api模块代码解析注解,并使用反射设置req.head

@HeadSpecialSet(clazz = Request.class, setMethod = "setUsername", param = "eadmin ")
public class QueryEResource {
    // 按条件查询
    Date conditionTimeBegin;
}


private static <T> Request<T> generateReq(T t) {
        Request<T> req = new Request<>();
        req.body = t;
        // head都是由架构组写的,passwd,username都是通过认证体系获取,ip为执行机ip
        req.head = new ReqHead();
        /*req.head.setIp("本地IP");
        req.head.setPassword("登录用户密码");
        req.head.setUsername("登录用户名");*/

// 反射对head属性进行设置
        HeadSpecialSet setMethod = t.getClass().getAnnotation(HeadSpecialSet.class);
        Method method = null;
        try {
            method = setMethod.clazz().getMethod(setMethod.setMethod(), String.class);
            method.invoke(req.head, setMethod.param());
        } catch (Exception e) {
        }
        return req;
    }

7、这种方式我现在一般不使用了,因为反射不能检查编译错误。

8、但如何使用函数呢,这里只需要将函数与某一个变量建立映射关系即可以,可以使用map, 数组,enum,这里我喜欢使用枚举enum

// 加入枚举类
public enum BiConsumerEnum {
    SET_IP(ReqHead::setIp),
    SET_USERNAME(ReqHead::setUsername),
    SET_PASSWD(ReqHead::setPassword);

    BiConsumer<ReqHead, String> bi;
    BiConsumerEnum(BiConsumer<ReqHead, String> bi) {
        this.bi = bi;
    }
}

// 业务端代码与框架端代码修改
public @interface HeadSpecialSet {
    BiConsumerEnum setter();
    String param();
}
public class SendApi {
 ......
    private static <T> Request<T> generateReq(T t) {
        Request<T> req = new Request<>();
        req.body = t;
        // head都是由架构组写的,passwd,username都是通过认证体系获取,ip为执行机ip
        req.head = new ReqHead();
        /*req.head.setIp("本地IP");
        req.head.setPassword("登录用户密码");
        req.head.setUsername("登录用户名");*/

        HeadSpecialSet setMethod = t.getClass().getAnnotation(HeadSpecialSet.class);
        setMethod.setter().bi.accept(req.head, setMethod.param());
        return req;
    }
}
@HeadSpecialSet(setter = BiConsumerEnum.SET_USERNAME, param = "eadmin")
public class QueryEResource {
    // 按条件查询
    Date conditionTimeBegin;
}

9、如果还想支持多个属性的设置,只需要把@HeadSpecialSet设置为可重复即可,具体可以学习一下@Repeat的注解
 

10、使用线程变量也是可以的,有兴趣的可以思考看看

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值