前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java字节码深挖 第二站:CGLIB

Java字节码深挖 第二站:CGLIB

作者头像
相思不扫积久弥厚
发布2023-10-26 14:25:01
1430
发布2023-10-26 14:25:01
举报

不同于jdk自带的动态代理只能针对接口生成代理类,cglib可以对类实现代理,实现也非常的傻瓜式,我们只需要实现net.sf.cglib.proxy.MethodInterceptor接口,使用方法与jdk自带的动态代理类似。

net.sf.cglib.proxy.MethodInterceptor接口中只有一个待实现的方法:

代码语言:javascript
复制
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;

其中第一个参数便是被调用的对象本身,第二个参数是被调用的方法,第三个参数是被调用的参数,第四个参数是被代理的方法,其中第一个和第三个参数比较好理解,method和proxy可能造成歧义,我这里解释一下,method是当前对象的方法,调用这个方法会再次进入intercept回调,而proxyMethod是被代理的方法,调用proxyMethod.invokeSuper(Object obj, Object[] args)方法即可调用原始的方法。

实现了本接口后,我们便可以使用 Enhancer类生成clz类增强后的代理实例了

代码语言:javascript
复制
        Enhancer enhancer = new Enhancer();
        //设置代理目标
        enhancer.setSuperclass(clz);
        //设置单一回调对象,在调用中拦截对目标方法的调用
        enhancer.setCallback(methodInterceptor);
        //设置类加载器
        enhancer.setClassLoader(clz.getClassLoader());
        Object obj=enhancer.create();

关于应用,我使用cglib实现了参数校验框架,实现逻辑如下:

    1.我们可以设置校验规则注解,将其注在需要被校验的成员变量上,该注解内部需要实现Validate接口方法,并将该类设为默认validate值。

    2.递归检查注解@RecursionCheck,将其注在同样需要校验的对象成员变量上。

    3.关联注解@Relevancy(String[]),将其注在可能改变成员变量值的方法上,注解参数为字符串数组,需要把可能被改变值的成员变量名称写入。

核心实现逻辑如下:

    1.net.sf.cglib.proxy.MethodInterceptor接口实现:

代码语言:javascript
复制
public class ValCheckInterceptor implements MethodInterceptor {
    public static MethodInterceptor I = new ValCheckInterceptor();
    private static final Map<Class<?>, Map<Method, Set<Field>>> classMap = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Class<?> clz = o.getClass().getSuperclass();
        classMap.computeIfAbsent(clz, this::initClassCheckMethod);
        Object result = methodProxy.invokeSuper(o, objects);
        Map<Method, Set<Field>> methodFieldMap = classMap.get(o.getClass().getSuperclass());
        if (methodFieldMap != null && methodFieldMap.containsKey(method)) {
            Set<Field> fields = methodFieldMap.get(method);
            for (Field field : fields) {
                field.setAccessible(true);
                for (Annotation annotation : field.getAnnotations()) {
                    if (annotation instanceof RecursionCheck) {
                        Object f = field.get(o);
                        if (f != null && f.getClass().equals(field.getType())) {
                            Object feh = ValCheckProxyFactory.getProxyInstance(f, this);
                            field.set(o, feh);
                        }
                        continue;
                    }
                    Class<? extends Validate> validateClz = null;
                    try {
                        Method validateCMethod = annotation.getClass().getMethod("validate");
                        validateClz = (Class<? extends Validate>) validateCMethod.invoke(annotation);
                    } catch (Throwable ignored) {
                    }
                    if (validateClz != null) {
                        Validate validate = validateClz.newInstance();
                        Object value = field.get(o);
                        validate.validate(o, field, value, annotation);
                    }
                }
            }
        }
        return result;
    }

    public Map<Method, Set<Field>> initClassCheckMethod(Class<?> clz) {
        Map<Method, Set<Field>> methodFieldMap = new ConcurrentHashMap<>();
        try {
            Set<Field> fields = ReflectUtil.getFields(clz);
            for (Field field : fields) {
                PropertyDescriptor pd = null;
                pd = new PropertyDescriptor(field.getName(), clz);
                Method setMethod = pd.getWriteMethod();//获得set方法
                methodFieldMap.computeIfAbsent(setMethod, m -> {
                    Set<Field> set = ConcurrentHashMap.newKeySet();
                    set.add(field);
                    return set;
                });
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        //获取对象的关联注解方法
        Arrays.stream(clz.getMethods()).forEach(method1 -> {
            Relevancy relevancy = method1.getAnnotation(Relevancy.class);
            if (relevancy == null) return;
            methodFieldMap.computeIfAbsent(method1, m -> ConcurrentHashMap.newKeySet());
            Set<Field> fields = methodFieldMap.get(method1);
            String[] fstr = relevancy.value();
            for (String s : fstr) {
                fields.add(ReflectUtil.getField(clz, s));
            }
        });
        return methodFieldMap;
    }
}

2.

样例如下

代码语言:javascript
复制
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Max {
    double value();
    String message() default "";

    Class<? extends Validate<Max>> validate() default Max.DefaultValidate.class;

    class DefaultValidate implements Validate<Max> {
        @Override
        public void validate(Object obj, Field field, Object value, Max annotation) throws ValidateException {
            if (!(value instanceof Number)) {
                return;
            }

            double v;
            if (value instanceof BigDecimal) {
                v = ((BigDecimal) value).doubleValue();
            } else {
                v = (double) value;
            }

            if (v > annotation.value()) {
                throw new ValidateException(obj, field, value, annotation, "value should <= " + annotation.value(), annotation.message());
            }
        }
    }
}
代码语言:javascript
复制
public class TestModel extends BaseModel{
    /**
     *
     */
    @Max(100)
    private Integer integer;

    @Max(100)
    private Integer integer2;

    @RecursionCheck
    private TestModel2 m2;

    public TestModel2 getM2() {
        return m2;
    }

    public void setM2(TestModel2 m2) {
        this.m2 = m2;
    }

    public static class TestModel2 extends BaseModel{
        @ValMax(100)
        private Integer integer1;

        public Integer getInteger1() {
            return integer1;
        }

        public void setInteger1(Integer integer1) {
            this.integer1 = integer1;
        }
    }

    public Integer getInteger() {
        return integer;
    }

    public Integer getInteger2() {
        return integer2;
    }

    public void setInteger2(Integer integer2) {
        this.integer2 = integer2;
    }

    public void setInteger(Integer integer) {
        this.integer = integer;
    }
    @Relevancy({"integer","integer2"})
    public void test(int a,int b){
        integer=a+b;
    }
}

最后测试代码如下:

代码语言:javascript
复制
        TestModel model1=new TestModel();
        model1.setInteger(13);
        TestModel model= ValCheckProxyFactory.getProxyInstance(model1, ValCheckInterceptor.I);
        model.setInteger(566);

校验成功:

代码语言:javascript
复制
learning.proxy.ValidateException: [learning.proxy.TestModel$$EnhancerByCGLIB$$bafbcd1.integer] validation [ValMax] failed: value should <= 100.0, value=566
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-052,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档