`

Javassist实现动态代理

阅读更多

Javassist实现动态代理

 

动态代理模式简述:之所以会出现代理这种模式就是因为我们常有这么一种需求:在被代理类的方法调用前后执行一些其它的逻辑,这些逻辑不适合由被代理类来实现,那这些逻辑谁来实现?当然是代理类。那代理类是谁?从哪里来?代理类是我们利用字节码生成工具动态创建的,然后利用反射实例化而得到代理对象。

 

tips:这篇文章讲的不是动态代理模式的思想而是怎么实现(简单实现)一个和java.lang.reflect.Proxy类相似的类。

 

按照JDK动态代理的套路来,先定义一个InvocationHandler来给客户端添加代理的逻辑(这个类和JDK中的一模一样):

 

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. /** 
  6.  * @author lixiaohui 
  7.  * @date 2016年9月27日 下午9:53:59 
  8.  *  
  9.  */  
  10. public interface InvocationHandler {  
  11.       
  12.     /** 
  13.      * 业务逻辑填充 
  14.      *  
  15.      * @param proxy 生成的代理对象 
  16.      * @param method 调用的方法 
  17.      * @param args 调用该方法的参数 
  18.      * @return 调用该方法的返回值 
  19.      * @throws Throwable throws if any exception 
  20.      */  
  21.     Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
  22.       
  23. }  

 

 

接下来是Proxy类,代理类由该类动态生成并且都继承自该类,和JDK的一样,这也是为什么JDK的动态代理只能代理接口而不能代理父类,这里我就按照JDK的套路来,当然理解了这个后要实现代理父类也没什么难度了。Proxy类是怎么生成类的呢?首先代理的是接口,所以先遍历所有接口,再遍历接口的所有方法,为代理类生成与这些方法同签名同返回值的方法,也就是相当于实现(implement)这些接口的方法,至于生成过程具体是怎么样的我们不用管这也是用javassist的好处(字节码是怎么生成的是javassist的事,我们只需要用它的API即可)。

 

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy;  
  2.   
  3. import java.util.Objects;  
  4. import java.util.concurrent.atomic.AtomicInteger;  
  5.   
  6. import javassist.CannotCompileException;  
  7. import javassist.ClassPool;  
  8. import javassist.CtClass;  
  9. import javassist.CtConstructor;  
  10. import javassist.CtField;  
  11. import javassist.CtMethod;  
  12. import javassist.Modifier;  
  13. import javassist.NotFoundException;  
  14.   
  15. import org.slf4j.Logger;  
  16. import org.slf4j.LoggerFactory;  
  17.   
  18. import cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyWeakHashMap;  
  19.   
  20. /** 
  21.  * 负责代理类类的生成 
  22.  *  
  23.  * <ul> 
  24.  * 生成的代理类: 
  25.  * <li>public final 修饰(和JDK动态代理不同的是:JDK生成的代理类也是final的, 但不一定是public的, 当所代理的接口中有至少一个以上的接口不是public时生成的代理就不是public的)</li> 
  26.  *  
  27.  * <li>类名以$Proxy为前缀, 后缀为数字, 如cc.lixiaohui.$Proxy0, cc.lixiaohui.$Proxy1...</li> 
  28.  *  
  29.  * <li>生成的代理类继承自{@link Proxy}</li> 
  30.  *  
  31.  * <li>生成的代理类所在package只有一种情况下才是确定的: 当所有接口中有且只有一个接口是non-public时.其他情况所在package不确定</li> 
  32.  * </ul> 
  33.  *   
  34.  * @author lixiaohui 
  35.  * @date 2016年9月27日 下午9:51:24 
  36.  *  
  37.  */  
  38. public class Proxy {  
  39.       
  40.     private static final Logger logger = LoggerFactory.getLogger(Proxy.class);  
  41.       
  42.     /** 
  43.      * 生成的代理类名前缀 
  44.      */  
  45.     private static final String PROXY_CLASSNAME_PREFIX = "$Proxy";  
  46.       
  47.     /** 
  48.      * 类后缀数字生成器 
  49.      */  
  50.     private static final AtomicInteger SUFFIX_GENERATOR = new AtomicInteger();   
  51.       
  52.     private static final boolean SHOULD_BE_FINAL = true;  
  53.     private static final boolean SHOULD_BE_ABSTRACT = false;  
  54.     private static final boolean SHOULD_BE_PUBLIC = true;  
  55.       
  56.     protected InvocationHandler invocationHandler;  
  57.       
  58.     /** 
  59.      * 弱引用已生成的Class的缓存, ClassLoader和被代理Class都相同时生成的代理Class才是相同的(这个类自己实现的,简单扩展一下java.util.Map就可以实现) 
  60.      * <类加载器, 被代理Class, 生成的代理Class> 
  61.      */  
  62.     private static CompoundKeyWeakHashMap<ClassLoader, Class<?>, Class<?>> proxyClassCache = new CompoundKeyWeakHashMap<ClassLoader, Class<?>, Class<?>>();  
  63.       
  64.     protected Proxy(InvocationHandler invocationHandler) {  
  65.         this.invocationHandler = invocationHandler;  
  66.     }  
  67.       
  68.     public static Object newProxyInstance(ClassLoader classLoader, Class<?> targetClass, InvocationHandler invocationHandler)   
  69.             throws Exception {  
  70.         // check not null  
  71.         classLoader = Objects.requireNonNull(classLoader, "classLoader cannot be null");  
  72.         targetClass = Objects.requireNonNull(targetClass, "targetClass cannot be null");  
  73.         invocationHandler = Objects.requireNonNull(invocationHandler, "invocationHandler cannot be null");  
  74.           
  75.         Class<?> proxyClass = proxyClassCache.get(classLoader, targetClass);  
  76.         // 有缓存  
  77.         if (proxyClass != null) {  
  78.             logger.debug("get proxy from cache");  
  79.             return proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);  
  80.         }  
  81.           
  82.         // singleton instance of classpool   
  83.         ClassPool pool = ClassPool.getDefault();  
  84.         //生成代理类的全限定名  
  85.         String qualifiedName = generateQualifiedName(targetClass);  
  86.         // 创建代理类  
  87.         CtClass proxy = pool.makeClass(qualifiedName);  
  88.         // 设被代理类继承自Proxy  
  89.         setSuperClass(pool, proxy);  
  90.         // 获取被代理类的所有接口  
  91.         CtClass[] interfaces = pool.get(targetClass.getName()).getInterfaces();  
  92.           
  93.         int methodIndex = 0;  
  94.         // 遍历这些接口  
  95.         for (CtClass parent : interfaces) {  
  96.             proxy.addInterface(parent);  
  97.               
  98.             // 获取该接口的所有方法  
  99.             CtMethod[] methods = parent.getDeclaredMethods();  
  100.             for (int j = 0; j < methods.length; ++j) {  
  101.                 CtMethod method = methods[j];  
  102.                 String fieldSrc = String.format("private static java.lang.reflect.Method method%d = Class.forName(\"%s\").getDeclaredMethods()[%d];"  
  103.                         , methodIndex, parent.getName(), j);  
  104.                 logger.debug("field src for method {}: {}", method.getName(), fieldSrc);  
  105.                 // 生成字段  
  106.                 CtField ctField = CtField.make(fieldSrc, proxy);  
  107.                 // 添加字段  
  108.                 proxy.addField(ctField);  
  109.                 // 生成对应的Method  
  110.                 generateMethod(pool, proxy, method, methodIndex);  
  111.                   
  112.                 ++methodIndex;  
  113.             }  
  114.         }  
  115.         // 设置代理类的类修饰符  
  116.         setModifiers(proxy, SHOULD_BE_PUBLIC, SHOULD_BE_FINAL, SHOULD_BE_ABSTRACT);  
  117.         // 生成构造方法  
  118.         generateConstructor(pool, proxy);  
  119.         // 持久化class到硬盘, for use of debug  
  120.         proxy.writeFile(".");  
  121.         // to java.lang.Class  
  122.         proxyClass = proxy.toClass(classLoader, null);  
  123.         // 缓存  
  124.         proxyClassCache.put(classLoader, targetClass, proxyClass);  
  125.         return proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);  
  126.     }  
  127.   
  128.     /** 
  129.      * 生成代理类的全限定名  
  130.      */  
  131.     private static String generateQualifiedName(Class<?> targetClass) throws Exception {  
  132.         CtClass theInterface = null;  
  133.         for (CtClass parent : ClassPool.getDefault().get(targetClass.getName()).getInterfaces()) {  
  134.             if (theInterface == null) {  
  135.                 theInterface = parent;  
  136.             }  
  137.             if (!Modifier.isPublic(parent.getModifiers())) {  
  138.                 theInterface = parent;  
  139.                 break;  
  140.             }  
  141.         }  
  142.         String name = theInterface.getPackageName() + "." + PROXY_CLASSNAME_PREFIX + SUFFIX_GENERATOR.getAndIncrement();  
  143.         return name;  
  144.     }  
  145.   
  146.   
  147.     /** 
  148.      * 设置类的修饰符 
  149.      */  
  150.     private static void setModifiers(CtClass proxy, boolean shouldBePublic, boolean shouldBeFinal, boolean shouldBeAbstract) {  
  151.         int modifier = 0;  
  152.         modifier = shouldBePublic ? modifier | Modifier.PUBLIC : modifier;  
  153.         modifier = shouldBeFinal ? modifier | Modifier.FINAL : modifier;  
  154.         modifier = shouldBeAbstract ? modifier | Modifier.ABSTRACT : modifier;  
  155.         logger.error(Modifier.toString(modifier));  
  156.         proxy.setModifiers(modifier);  
  157.     }  
  158.   
  159.     /** 
  160.      * 生成构造函数 
  161.      */  
  162.     private static void generateConstructor(ClassPool pool, CtClass proxy) throws NotFoundException, CannotCompileException {  
  163.         CtConstructor ctConstructor = new CtConstructor(new CtClass[]{pool.get(InvocationHandler.class.getName())}, proxy);  
  164.         String methodBodySrc = String.format("super(%s);""$1");  
  165.         logger.debug("constructor body for constructor {}: {}", ctConstructor.getName(), methodBodySrc);  
  166.         ctConstructor.setBody(methodBodySrc);  
  167.         proxy.addConstructor(ctConstructor);  
  168.     }  
  169.   
  170.   
  171.     /** 
  172.      * 生成代理方法 
  173.      */  
  174.     private static void generateMethod(ClassPool pool, CtClass proxy, CtMethod method, int methodIndex) throws NotFoundException, CannotCompileException {  
  175.         CtMethod ctMethod = new CtMethod(method.getReturnType(), method.getName(), method.getParameterTypes(), proxy);  
  176.         String methodBodySrc = String.format("return super.invocationHandler.invoke(this, method%d, $args);", methodIndex);  
  177.         logger.debug("method body for method {}: {}", method.getName(), methodBodySrc);  
  178.         ctMethod.setBody(methodBodySrc);  
  179.         proxy.addMethod(ctMethod);  
  180.     }  
  181.   
  182.     /** 
  183.      * 把proxy类的父类设置为Proxy 
  184.      *  
  185.      */  
  186.     private static void setSuperClass(ClassPool pool, CtClass proxy) throws CannotCompileException, NotFoundException {  
  187.         proxy.setSuperclass(pool.get(Proxy.class.getName()));  
  188.     }  
  189.   
  190. }  

 

 

使用测试

 使用方式和JDK的没啥区别

先定义两个被代理的接口:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. public interface Talkable {   
  4.     Object talk(String words) throws Exception;   
  5. }  

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. interface Smileable {  
  4.     Object smile() throws Exception;  
  5. }  

 

Person实现上面两接口:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. public class Person implements Smileable, Talkable {  
  4.   
  5.     private String name;  
  6.       
  7.     public Person(String name) {  
  8.         this.name = name;  
  9.     }  
  10.       
  11.     public Object talk(String words) throws Exception {  
  12.         System.out.println(name + " says: " + words);  
  13.         return words;  
  14.     }  
  15.       
  16.     public Object smile() throws Exception {  
  17.         System.out.println(name + " start smiling");  
  18.         System.out.println(name + " stop smiling");  
  19.         return null;  
  20.     }  
  21.   
  22. }  

 

 

代理逻辑的实现:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import cc.lixiaohui.demo.javassist.proxy.InvocationHandler;  
  6. import cc.lixiaohui.demo.javassist.proxy.Proxy;  
  7.   
  8. public class JavassistProxyFactory implements InvocationHandler{  
  9.   
  10.     //被代理类的对象  
  11.     private Object target;  
  12.       
  13.     public JavassistProxyFactory(Object target) {  
  14.         this.target = target;  
  15.     }  
  16.       
  17.     /*  
  18.      * @see cc.lixiaohui.demo.javassist.proxy.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 
  19.      */  
  20.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  21.         System.out.println("------- intercept before --------");  
  22.                 // 调用原来的方法  
  23.         Object result = method.invoke(target, args);  
  24.         System.out.println("--------intercept after ---------");  
  25.         return result;  
  26.     }  
  27.     // 获取代理类的对象  
  28.     public Object getProxy() throws Exception {  
  29.         return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass(), this);  
  30.     }  
  31.       
  32. }  

 

测试主程序:

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. import java.lang.reflect.Modifier;  
  4.   
  5. import org.junit.Test;  
  6.   
  7. public class Tester {  
  8.       
  9.     @Test  
  10.     public void testJavassist() throws Exception {  
  11.         Person person = new Person("小明");  
  12.         Object proxy = new JavassistProxyFactory(person).getProxy();  
  13.                 // System.gc(); // 主动触发gc  
  14.         Object proxy1 = new JavassistProxyFactory(person).getProxy();  
  15.         ((Talkable) proxy).talk("hello world");  
  16.         ((Smileable) proxy).smile();  
  17.           
  18.         System.out.println("package: " + proxy.getClass().getPackage().getName());  
  19.         System.out.println("classname: " + proxy.getClass().getName());  
  20.         System.out.println("modifiers: " + Modifier.toString(proxy.getClass().getModifiers()));  
  21.                 System.out.println(proxy.getClass() == proxy1.getClass()); // 测试缓存是否起作用  
  22.     }  
  23.   
  24. }  

 

结果输出:


 
 

 

在 Proxy类中生成代理Class时把这个Class持久化了到硬盘中,通过反编译工具查看生成的代理类的源码:

 

Java代码 
  1. /*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/  
  2. package cc.lixiaohui.demo.javassist.proxy.example;  
  3.   
  4. import cc.lixiaohui.demo.javassist.proxy.InvocationHandler;  
  5. import cc.lixiaohui.demo.javassist.proxy.Proxy;  
  6. import java.lang.reflect.Method;  
  7.   
  8. public final class $Proxy0 extends Proxy implements Smileable, Talkable {  
  9.     private static Method method0 = java.lang.Class.forName("cc.lixiaohui.demo.javassist.proxy.example.Smileable").getDeclaredMethods()[0];  
  10.     private static Method method1 = java.lang.Class.forName("cc.lixiaohui.demo.javassist.proxy.example.Talkable").getDeclaredMethods()[0];  
  11.   
  12.     public Object smile() {  
  13.         return this.invocationHandler.invoke(this, method0, new Object[0]);  
  14.     }  
  15.   
  16.     public Object talk(String paramString) {  
  17.         return this.invocationHandler.invoke(this, method1, new Object[] { paramString });  
  18.     }  
  19.   
  20.     public $Proxy0(InvocationHandler paramInvocationHandler) {  
  21.         super(paramInvocationHandler);  
  22.     }  
  23. }  
0
1
分享到:
评论
2 楼 莫名的拉风 2017-05-27  
minisnoopy2u 写道
您好,ClassLoader, targetClass, proxyClass为什么说要保存“弱”引用呢?直接put进去的不是引用吗?CompoundKeyWeakHashMap的定义能贴一下吗?

package cc.lixiaohui.demo.javassist.proxy.util;

import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

/**
* 基于{@link java.util.HashMap}的CompoundKeyMap的实现, 所有key为弱引用
*
* @author lixiaohui
* @date 2016年10月1日 下午12:37:08
*
*/
public class CompoundKeyWeakHashMap<K, P, V> implements CompoundKeyMap<K, P, V> {

private Map<KeyHolder<CompoundKey<K, P>>, V> map = new HashMap<KeyHolder<CompoundKey<K, P>>, V>();


/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#get(java.lang.Object, java.lang.Object)
*/
public V get(K key, P param) {
key = Objects.requireNonNull(key, "key cannot be null");
param = Objects.requireNonNull(param, "param cannot be null");

return map.get(newKey(key, param));
}

private KeyHolder<CompoundKey<K, P>> newKey(K key, P param) {
return new KeyHolder<CompoundKey<K, P>>(new CompoundKeyImpl<K, P>(key, param));
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#get(java.lang.Object, java.lang.Object, java.lang.Object)
*/
public V get(K key, P param, V defValue) {
key = Objects.requireNonNull(key, "key cannot be null");
param = Objects.requireNonNull(param, "param cannot be null");

V value = get(key, param);
return value == null ? defValue : value;
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#put(java.lang.Object, java.lang.Object, java.lang.Object)
*/
public V put(K key, P param, V value) {
return map.put(newKey(key, param), value);
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#putIfAbsent(java.lang.Object, java.lang.Object, java.lang.Object)
*/
public V putIfAbsent(K key, P param, V value) {
return map.putIfAbsent(newKey(key, param), value);
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#entrySet()
*/
public Set<Entry<CompoundKeyMap.CompoundKey<K, P>, V>> entrySet() {
throw new UnsupportedOperationException();
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#keys()
*/
public Set<CompoundKeyMap.CompoundKey<K, P>> keys() {
throw new UnsupportedOperationException();
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#values()
*/
public Collection<V> values() {
return map.values();
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#size()
*/
public int size() {
return map.size();
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap#isEmpty()
*/
public boolean isEmpty() {
return map.isEmpty();
}

static class KeyHolder<T> extends WeakReference<T> {

public KeyHolder(T referent) {
super(referent);
}

@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
return get().equals(((KeyHolder<T>) obj).get());
}

@Override
public int hashCode() {
return get().hashCode();
}

}

static class CompoundKeyImpl<K, P> implements CompoundKey<K, P> {

private K key;

private P param;

CompoundKeyImpl(K key, P param) {
super();
this.key = key;
this.param = param;
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap.CompoundKey#getKey()
*/
public K getKey() {
return key;
}

/*
* @see cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyMap.CompoundKey#getParam()
*/
public P getParam() {
return param;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((param == null) ? 0 : param.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompoundKeyImpl<?, ?> other = (CompoundKeyImpl<?, ?>) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (param == null) {
if (other.param != null)
return false;
} else if (!param.equals(other.param))
return false;
return true;
}


}

}
1 楼 minisnoopy2u 2017-05-26  
您好,ClassLoader, targetClass, proxyClass为什么说要保存“弱”引用呢?直接put进去的不是引用吗?CompoundKeyWeakHashMap的定义能贴一下吗?

相关推荐

    Java 动态代理.md

    动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。 在了解动态代理前,我们需要先了解一下什么是代理模式。 代理模式 代理模式...

    collection-agent:通过javaagent和javassist技术实现对java的ArrayList和HashMap的增强,避免在虚拟器一次load大量数据时导致OOM

    通过javaagent和javassist技术实现对java的ArrayList和HashMap的增强,在操作集合元素时判断集合元素个数, 当集合元素个数大于设置的上限时,抛出异常,终止此次操作,从而避免在集合元素过大导致OOM. #使用方式: ...

    java6string源码-dynamic-proxy:利用ASM、CGLIB、ByteBuddy、Javassist和JDKDynamicP

    接下来,让我们看一些实现动态代理的例子。 1.2 创建调用者 首先,让我们定义一个接口。 public interface EchoService { String echo(String message); } 通过使用运行时代码生成技术,您可以在不定义Class的情况下...

    基于YMP框架实现的简单HTTP请求透传代理模块.rar

    [新增] 优化调整框架配置支持自定义加载器和代理工厂并新增基于Javassist的代理工厂接口实现; [新增] 空操作代理工厂(使用它表示需要禁用框架的AOP特性, 主要用于Android应用); [新增] 为ClassUtils类新增通过读取...

    instrumentation的工具包

    java代理,实现对程序的监测,对类字节码修改,使用javassist.jar。

    java8集合源码分析-java-agent:基于java5Instrumentapi实现的mock框架

    方法执行时,并不执行原来的代码,而是动态解析groovy代码,调用groovy方法。 我们可以在相关的groovy代码中配置哪些类的方法执行groovy方法,哪些类的方法执行原来的逻辑。 当然,在groovy方法中,我们可以执行原有...

    Lejagent

    上一篇文章中主要是使用Javassist在JavaAgent中动态修改内容,但是只能写代码中,这里我们使用系统替代和数据库两种方式实现。一,系统数值:1.1,入参打印:配置类的方法打印全部入参1.2,打印返回结果:如果方法有...

    SSH 框架所需JAR包

    6.cglib-nodep-2.1_3.jar(支持cglib动态代理的包) 如果用BasicDataSource来配置数据库连接,还要加入2个包: 7.commons-pool.jar 8.commons-dbcp.jar Hibernate需要的jar包: 1.hibernate3.jar(hibernate的核心jar...

    SSH 项目 整合jar包

    6.cglib-nodep-2.1_3.jar(支持cglib动态代理的包) 如果用BasicDataSource来配置数据库连接,还要加入2个包: 7.commons-pool.jar 8.commons-dbcp.jar 三、Hibernate需要的jar包: 1.hibernate3.jar(hibernate的...

    web项目常用jar包及说明.zip

    6.cglib-nodep-2.1_3.jar(支持cglib动态代理的包) 如果用BasicDataSource来配置数据库连接,还要加入2个包: 7.commons-pool.jar 8.commons-dbcp.jar Hibernate需要的jar包: 1.hibernate3.jar(hibernate的核心...

    TestList:只是为了测试

    9.集成sliding 类似于微信的下拉功能,也可以通过PTR来实现,自定义header(和PTR的实现原理不一样,PTR通过layout来布局header和内容;而sliding通过设置view的属性来达到设置偏移) 10.增加swipe listview,原理是...

    QsBase:Android AOP面向切面框架,使用场景:

    javassist) MVP(或MVVM)架构+ AOP面向切面编程,替代弃反射,代理等操作,稳定性和执行效率高积累很多框架封装思想,能够轻松驾驭任何典型的APP项目开发轻量级框架,轻松实现类似EventBus,ButterKnife(MVVM架构...

Global site tag (gtag.js) - Google Analytics