目前業務有個需求,須要使用多線程同時去獲取不一樣類型的數據。在獲取數據時,須要作簽名檢驗,簽名校驗的包由第三方提供 (咱們沒法修改),如今的問題是,線程啓動後,第一個類型的數據能夠被成功獲取,可是後面類型的數據就會提示簽名失敗。可是單獨拉後面類型的數據時,又能夠成功,進過排查,發現是第三方包中的一個核心類其中有不少變量被設置爲了static, 每一個線程都會去設置這些static變量值,致使簽名檢驗失敗。如下是幾種解決策略:java
1. 將多線程換成單線程; 加鎖,使得每次只有一個線程在真正執行。 因爲第三方包一個static變量會在初始化時追加上次的記錄,致使這種方法也不能解決問題。多線程
2. 將多線程換成多進程。每一個進程處理一個類型,類型之間互相獨立。(能夠解決,可是一樣代碼卻得部署多個應用,不爽!)this
3. 想辦法讓不一樣類型對應的靜態變量互相隔離--使用不一樣的classloader加載類,使得對應的類互相不同的 。可是這種狀況,不能將新定義的對象爲‘原’對象。如如下代碼是會拋出異常的:url
URLClassLoader classLoader1 = new URLClassLoader(urls); URLClassLoader classLoader2 =new URLClassLoader(urls); Class<?> clazz1= classLoader1.loadClass(Hello.class.getName()); Class<?> clazz2= classLoader2.loadClass(Hello.class.getName()); Hello hello1=(Hello) clazz1.newInstance(); Hello hello2=(Hello) clazz2.newInstance();
這個hello1是不能強制轉換爲Hello對象的,由於Hello.class和clazz1是不同的類,因此這裏只能使用spa
URLClassLoader classLoader1 = new URLClassLoader(urls); URLClassLoader classLoader2 =new URLClassLoader(urls); Class<?> clazz1= classLoader1.loadClass(Hello.class.getName()); Class<?> clazz2= classLoader2.loadClass(Hello.class.getName()); Object hello1=clazz1.newInstance(); Object hello2= clazz2.newInstance();
這樣帶入的問題是,上層使用hello1和hello2對象的地方,都得是object對象。.net
4.想辦法解決這個問題---代理類線程
下面是解決的方法:代理
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; /** * Created by litao on 16/10/16. */ public class HelloProxy implements MethodInterceptor { private String name; public static HashMap<String, Object> delegates = new HashMap<String, Object>(); public Hello getInstance(String name) throws ClassNotFoundException, IllegalAccessException, InstantiationException { this.name = name; if (delegates.get(name) == null) { URL url = Hello.class.getProtectionDomain().getCodeSource().getLocation(); URL[] urls = new URL[]{url}; URLClassLoader classLoader = new URLClassLoader(urls, null); Class clazz = classLoader.loadClass(Hello.class.getName()); Object obj = clazz.newInstance(); delegates.put(name, obj); } Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Hello.class); // 回調方法 enhancer.setCallback(this); // 建立代理對象 return (Hello) enhancer.create(); } public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object realObj= delegates.get(name); Method method2 = realObj.getClass().getMethod(method.getName(), method.getParameterTypes()); return method2.invoke(realObj, args); } }
定義HelloProxy對象,用它來「代替」Hello類,對Hello類的操做,轉爲對HelloProxy操做,可是底層卻仍是使用Hello類來完成,而且Hello能夠是「加載」成的不一樣類對象code
下面是使用例子對象
/** * Created by litao on 16/10/16. */ public class Hello { private static String name; public void setName(String _name) { name=_name; } public void sayHello() { System.out.println("name:"+name); } }
/** * Created by litao on 16/10/16. */ public class LoadMain { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Hello helloProxy1=new HelloProxy().getInstance("hello1"); Hello helloProxy2 =new HelloProxy().getInstance("hello2"); helloProxy1.setName("zhangsan"); helloProxy2.setName("lisi"); helloProxy1.sayHello(); helloProxy2.sayHello(); } }
輸出結果是:zhangsan
lisi
這裏,「同」類的static值卻不同了,實現了邏輯