首页 > 代码库 > 使用代理类解决静态变量的一个问题

使用代理类解决静态变量的一个问题

目前业务有个需求,需要使用多线程同时去获取不同类型的数据。在获取数据时,需要做签名检验,签名校验的包由第三方提供 (我们无法修改),现在的问题是,线程启动后,第一个类型的数据可以被成功获取,但是后面类型的数据就会提示签名失败。但是单独拉后面类型的数据时,又可以成功,进过排查,发现是第三方包中的一个核心类其中有很多变量被设置为了static, 每个线程都会去设置这些static变量值,导致签名检验失败。以下是几种解决策略:

1. 将多线程换成单线程; 加锁,使得每次只有一个线程在真正执行。 由于第三方包一个static变量会在初始化时追加上次的记录,导致这种方法也不能解决问题。

2. 将多线程换成多进程。每个进程处理一个类型,类型之间互相独立。(可以解决,但是同样代码却得部署多个应用,不爽!)

3. 想办法让不同类型对应的静态变量互相隔离--使用不同的classloader加载类,使得对应的类互相不一样的 。但是这种情况,不能将新定义的对象为‘原’对象。如以下代码是会抛出异常的:

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是不一样的类,所以这里只能使用

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对象。

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可以是“加载”成的不同类对象

下面是使用例子

/** * 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值却不一样了,实现了逻辑 

 

使用代理类解决静态变量的一个问题