不同于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