前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >自定义注解2-动态修改注解的属性值

自定义注解2-动态修改注解的属性值

原创
作者头像
并发笔记
修改2020-10-27 10:28:57
4.6K0
修改2020-10-27 10:28:57
举报
文章被收录于专栏:并发笔记并发笔记

    经过上一节的,我们可以自己解析spel表达式。那么我现在的想法是,在注解的第一层aop中解析spel,然后将解析后的值设置到属性中,那么在之后的aop中就不用解析了。

找出注解中值存放位置

    继续上一节的代码,在上一节的AOP中添加注解@Order(0),再新增一个注解,添加@Order(1)。注意order这个注解有坑的,最好先百度完再使用。

代码语言:txt
复制
@Component
@Aspect
@Order(0)
public class InterestResolveELAspect {
	// resolve spel... 
}

@Component
@Aspect
@Order(1)
public class InterestHandleAspect {
	@Around("@annotation(anno)")
    public Object invoked(ProceedingJoinPoint pjp, Interest anno) throws Throwable {
		String key = anno.key();
		String unless = anno.unless();
		logger.info("call InterestHandleAspect.invoked, resolvedKey:[{}], resolvedUnless:[{}]"
                , key, unless);
		return pjp.proceed();
	}
}

    我们在上一节代码中的String keySpel = anno.key()打下断点。查看当前栈的变量。

操作流程图
操作流程图

    发现注解的对象是一个Proxy的实例,Proxy的作用就是为java类生一个代理对象,有这个代理对象去调用真实方法,就像这样

代码语言:txt
复制
public interface A {
    String func1();
}

public class B implements A {
    
    @Override
    public String func1() { //do something ... }
    
    public String func2() { //do something ... };
}

public static void main(String ...args) {
    B bInstance = new B();
    
    B bProxy = Proxy.newProxyInstance(
        B.class.getClassLoader(),    // B 类的类加载器
        B.class.getInterfaces(), // B 类所实现的接口,如果你想拦截B类的某个方法,必须让这个方法在某个接口中声明并让B类实现该接口
        new InvocationHandler() { // 调用处理器,任何对 B类所实现的接口方法的调用都会触发此处理器
            @Override
            public Object invoke (Object proxy, // 这个是代理的实例,method.invoke时不能使用这个,否则会死循环
                                  Method method, // 触发的接口方法
                                  Object[] args // 此次调用该方法的参数
                                  ) throws Throwable {
                System.out.println(String.format("调用 %s 之前", method.getName()));
                /**
                 * 这里必须使用B类的某个具体实现类的实例,因为触发时这里的method只是一个接口方法的引用,
                 * 也就是说它是空的,你需要为它指定具有逻辑的上下文(bInstance)。
                 */
                Object obj = method.invoke(bInstance, args); 
                System.out.println(String.format("调用 %s 之后", method.getName()));
                return obj; //返回调用结果
            }
        }
    );
}

    再回想注解实质上是一个接口,它本身没有逻辑,那么它的值存在什么地方呢?那么答案就是Proxy实例中了。

    这个Proxy实例有一个类型为AnnotationInvocationHandler的变量h,我回到上面创建Proxy对象的代码中,Proxy.newProxyInstance()的第三个参数就是InvocationHandler,而这个变量h就是它的实现类。

    继续往变量h里看,它有一个字段memberValues,是一个map,而在这个map中,我发现了注解值存放的位置。key为注解的属性名,value就是属性值。

修改注解值

    找到了注解值存放位置,那么修改就简单了

代码语言:txt
复制
@Component
@Aspect
@Order(0)
public class InterestResolveELAspect {
	 @Around("@annotation(anno)")
    public Object invoked(ProceedingJoinPoint pjp, Interest anno) throws Throwable {
		// resolve spel
        String key = resolve spel;
        Boolean unless = resolve spel;
		
		InvocationHandler h = Proxy.getInvocationHandler(anno);
        Field hField = h.getClass().getDeclaredField("memberValues");
        hField.setAccessible(true);
        Map<String, Object> memberValues = (Map<String, Object>) hField.get(h);
        memberValues.put("key", key);
        memberValues.put("unless", unless.toString());

		return pjp.proceed();
	} 
}

    赶紧测试一下,看看InterestHandleAspect打印的内容是不是你想要的。。。。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 找出注解中值存放位置
  • 修改注解值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档