整篇基于cglib的3.0 版本实现来介绍下Cglib的一种应用场景。Cglib的底层是调用ASM来实现动态链接的,所以性能方便会比Java原生的Proxy的性能提升很多。Spring框架的AOP也是基于Cglib来实现的,这里不继续扩展ASM框架实现。大家感兴趣的可以阅读cglib源码,并做进一步扩展。
一、场景:通过Cglib的方法拦截器解放以下场景。相同对象的值拷贝,如果是个很大的对象,很容易遗漏或者出错。
/** * 数据转换和处理 */ private ItemDO changeValue(ItemDO newDO, ItemDO oldDO) { //数据赋给新DO数据 newDO.setCount(oldDO.getCount()); newDO.setCapacity(oldDO.getCapacity()); newDO.setUrl(oldDO.getUrl()); newDO.setName(oldDO.getName()); newDO.setTitle(oldDO.getTitle()); newDO.setIntro(oldDO.getIntro()); newDO.setTotalNum(oldDO.getTotalNum()); … return newDO; }
以上场景在code的海洋中大量存在着。下面尝试一下用拦截器去把上面这种代码解放成一行,并且通用于其他类似场景。
二、实现思路:
1、获得一个DO对象的代理。我们叫这个是sdo.
2、给代理添加一个方法拦截器(针对该场景,拦截所有set方法)。
3、对DO进行一些字段的set方法调用,就如平常我们去通过一些各种手段拿到一个DO字段值一样,,,这时每次调用set方法的时候,我们都会在拦截器里面put下来这个被调用的方法签名(key)——方法返回值(key)的Map。
4、new 一个新的或者从DB里面其他里面拿到一个旧时代的对象。但因为是后出现的,所以我们叫他ndo。
5、通过反射去将sdo的新改变的值赋给ndo(通過sdo的interceptor)。
三、实例概览:这里涉及到的几个类简单说明一下。
我们的例子里面的Bean叫做SourceDO。ChangeValueProxyFactory 类里面的方法来获得一个代理类。ChangeValueInterceptorFactory 是一个拦截器工厂,用于获取对象拦截器(以后可以继续扩展改类,增加其他拦截器),ChangeValueSetInterceptor 是具体的set方法拦截器实现。InvokeProxxy 是用来实现上述第5步骤的。通过反射将sdo的值赋值给ndo。
四、具体实现代码
1>ChangeValueProxyFactory
public class ChangeValueProxyFactory<T> { public static Object getChangeValueInstance(Callback callBack, Class<?> proxyClass) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(proxyClass); enhancer.setCallback(callBack); return enhancer.create(); } }
2>ChangeValueInterceptorFactory
public class ChangeValueInterceptorFactory { public static ChangeValueSetInterceptor getInterceptor(Object obj) { if (!(obj instanceof Factory)) { return null; } Factory f = (Factory) obj; Callback[] callBacks = f.getCallbacks(); for (Callback callBack : callBacks) { if (callBack instanceof ChangeValueSetInterceptor) { return (ChangeValueSetInterceptor) callBack; } } return null; } }
3> ChangeValueSetInterceptor
public class ChangeValueSetInterceptor implements MethodInterceptor { private static final String SET = "set"; private Map<String, Object[]> changedPropMap; public ChangeValueSetInterceptor() { changedPropMap = new HashMap<String, Object[]>(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { String name = method.getName(); Object pobj = proxy.invokeSuper(obj, args); if (name.startsWith(SET)) { changedPropMap.put(name, args); } return pobj; } public Map<String, Object[]> getChangedPropMap() { return Collections.unmodifiableMap(changedPropMap); } }
4> InvokeProxxy
public class InvokeProxy { public static void invokeChangeValue(Object sobject, Object nobject, ChangeValueSetInterceptor interceptor) throws Exception { if (sobject == null && nobject == null && interceptor == null) return; Map<String, Object[]> map = interceptor.getChangedPropMap(); if (map == null || map.size() == 0) return; Method[] methoerds = nobject.getClass().getMethods(); for (Method m : methoerds) { String setMethodName = m.getName(); Object[] paramsValues = map.get(setMethodName); if (paramsValues != null && paramsValues.length > 0) { try { Method setMethod = nobject.getClass().getMethod(setMethodName, m.getParameterTypes()); if (setMethod != null) { setMethod.invoke(nobject, paramsValues); } } catch (InvocationTargetException e) { throw new InvocationTargetException(e); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(e); } catch (SecurityException e) { throw new SecurityException(e); } catch (IllegalAccessException e) { throw new IllegalAccessException(); } catch (NoSuchMethodException e) { throw new NoSuchMethodException(); } } } } }
5> SourceDO
public class SourceDO implements Serializable { private int id; private String name; private String note; private Date date; private String address; private boolean happy; private List<String> foods; // some setters and getters …… @Override public String toString() { // TODO Auto-generated method stub return "id:" + id + "|name:" + name + "|note:" + note + "|date:" + date + "|happy:"+happy+"|foods:"+foods.toString()+ "|address:" + address; } }
6> ChangeValueTest
public class ChangeValueTest { public static void main(String[] args) { SourceDO sdo = (SourceDO) ChangeValueProxyFactory.getChangeValueInstance(new ChangeValueSetInterceptor(), SourceDO.class); ChangeValueSetInterceptor interceptor = ChangeValueInterceptorFactory.getInterceptor(sdo); fillSourchDO(sdo); SourceDO ndo = new SourceDO(); fillNewSourchDO(ndo); try { InvokeProxy.invokeChangeValue(sdo, ndo, interceptor); } catch (Exception e) { // log and do something } System.out.println("SourchDO info:" + ndo.toString()); } private static void fillSourchDO(SourceDO sdo) { sdo.setId(32); sdo.setName("山田くん"); sdo.setDate(new Date()); sdo.setHappy(true); List<String> foods=new ArrayList<String>(); foods.add("茄子"); foods.add("草莓"); sdo.setFoods(foods); } private static void fillNewSourchDO(SourceDO ndo) { ndo.setName("山田さん"); ndo.setAddress("不在这里"); ndo.setId(99); ndo.setHappy(false); ndo.setNote("Hey!Say!JUMP"); }}
预期:用sdo的字段值覆盖ndo的字段值,未被改变的字段保留原来ndo的字段值。
Run result:
SourchDO info:SourchDO info:id:32|name:山田くん|note:Hey!Say!JUMP|date:Fri Jul 27 10:51:24 CST 2012|happy:true|foods:[茄子,草莓]|address:不在这里
比如对于之前那么多行的代码。现在只需一行: 至此已经实现了,最初的场景目标。但是还有很多问题有待扩充和解决。比如对于一个DO,这里限制了一种默认的规约,getXxxx和setXxx的方式。如果不是这种规约书写的方法就不能被拦截,不过可以部分字段特殊处理一下。对于什么时候放intercepter和什么场景获取intercepter还需要更多的思考和扩展。
InvokeProxy.invokeChangeValue(oldDO, newDO, itemDO.getInterceptor());//拦截方式 // Copy法:newDO= changeValue(newDO,oldDO); changeValue方法就是开篇的那个private赋值方法。 这里还要注意,因为itemDO的intercepter的代理创建在外层的类里面所以就通过将intercepter Set的方式来存放处理。 |
感慨于,通过拦截的方式的另一个好处,就是减少出错率,比如我们不记得前面的Action到底改变了多少个字段,而且也不关心到itemDO(相当于sdo)到底改变了什么。减少了比如一些编辑业务上的代码丢失部分更新字段的Bug~也不用为copy字段而眼花缭乱,解放Coding的生产力。
相关推荐
2. **性能**:在处理大量代理对象时,CGLib通常比Java动态代理更快,因为它使用的是字节码技术,而Java动态代理需要反射。 3. **灵活性**:CGLib可以修改类的行为,甚至包括私有方法,而Java动态代理只能处理接口...
Enhancer是主要的入口点,用于创建代理对象,你可以向它传递一个类或者接口,它会生成这个类的子类并添加拦截器。MethodInterceptor是拦截器接口,它定义了一个intercept方法,用于在目标方法执行前后插入自定义逻辑...
通过阅读和分析Spring源码,我们可以看到CGLIB是如何与Spring框架无缝集成的,这将对提升我们的编程技巧和问题解决能力大有裨益。同时,CGLIB也是许多其他开源框架和库的基础,如Hibernate等,因此熟悉CGLIB对于成为...
你可以向Enhancer传递一个类或类名作为父类,然后通过回调机制(Callback)来定义代理对象的行为。 2. **MethodInterceptor**:这是CGLIB中的一个接口,用于定义代理对象的方法拦截逻辑。当你在Enhancer中设置一个...
创建代理对象时,我们需要提供一个实现了InvocationHandler接口的实例,并将这个实例传递给Proxy.newProxyInstance()方法。当通过代理对象调用方法时,实际执行的是InvocationHandler的invoke()方法,我们可以在...
代理模式是一种结构型设计模式,其中代理类含有一个对真实主题对象的引用,这样代理对象就可以执行一些预备或清理工作,并将真正的调用传递给真实主题对象。 在Java中,代理模式的应用非常广泛,特别是在框架级开发...
在Java开发中,CGlib常被用来扩展Java类,特别是当目标类无法被继承(如final类)或者不提供接口时,它可以创建目标类的代理对象来实现方法拦截。 CGlib的核心机制基于ASM库,ASM是一个Java字节码操控和分析框架,...
5. **API使用**:CGLIB提供了一个简单的API,开发者可以通过`Enhancer`类来创建代理对象,通过传递一个回调接口或者类来决定哪些方法需要被拦截和如何处理。 6. **版本迭代**:2.2.0版本可能包含了一些错误修复、...
CGlib允许开发者在运行时动态创建子类,从而实现对现有类的增强或代理功能。这个"cglib所有jar包"很可能是包含CGlib库不同版本或者不同模块的集合,方便开发者根据需求选择合适版本。 在Java中,动态代理是一种常见...
1. **Enhancer**:这是CGLib的主要入口点,用于创建代理对象。你可以向Enhancer传递一个类或者接口,它会生成指定类的子类。 2. **Callback**:回调接口,如MethodInterceptor,它定义了当代理对象的方法被调用时...
Java的动态代理是一种在运行时创建代理类的技术,它允许我们动态地生成实现了特定接口的代理对象,从而可以在不修改原有代码的情况下对方法调用进行增强。动态代理主要有两种实现方式,一种是JDK动态代理,另一种是...
你可以向Enhancer传递一个类或者类的Class对象,然后设置回调函数,最后通过call()方法创建代理对象。 2. Callback:这是CGLIB中的回调接口,包括MethodInterceptor和FixedValue等。MethodInterceptor用于在方法调用...
比如,在Spring AOP框架中,CGLib被用来创建代理对象,实现在方法执行前后的拦截器逻辑,如事务管理、性能监控等。 2. **字节码操作**: CGLib的核心是ASM库,它允许在运行时生成和修改类的字节码。通过生成并写入...
Java动态代理是Java编程中一个非常重要的特性,它允许我们在运行时创建代理对象,这些代理对象可以代替原对象执行某些额外的操作,而无需修改原对象的代码。动态代理在AOP(面向切面编程)中广泛应用,如日志记录、...
- **`java.lang.reflect.InvocationHandler`**:接口,需要实现`invoke(Object proxy, Method method, Object[] args)`方法,该方法在代理对象的方法被调用时触发。 - **`java.lang.reflect.Method`**:表示类的方法...
3. **创建动态代理对象**:通过`Proxy.newProxyInstance()`方法创建一个实现了`HelloWorld`接口的动态代理对象。 4. **调用代理对象的方法**:最后通过调用代理对象的`sayHelloWorld()`方法,可以看到输出结果不仅...
- Spring支持两种类型的代理:JDK动态代理(如果目标对象实现了接口)和CGLIB代理(如果目标对象没有接口,使用字节码生成技术)。 - 在Spring配置中,可以使用`@Aspect`注解定义切面,`@Before`、`@After`、`@...
3. **灵活性**:动态代理允许在运行时决定代理对象的行为,便于实现动态织入和切面的灵活配置。 在Java中,动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口实现,适用于目标类实现有接口...
此外,CGLIB还提供了一个名为Callback的接口,它是用于传递回调方法给生成的代理对象的。这些回调方法可以在代理对象的方法调用过程中被调用,提供了更多的灵活性。Callback接口有多个实现,如FixedValue用于返回...