zoukankan      html  css  js  c++  java
  • 使用代理类解决静态变量的一个问题

    目前业务有个需求,需要使用多线程同时去获取不同类型的数据。在获取数据时,需要做签名检验,签名校验的包由第三方提供 (我们无法修改),现在的问题是,线程启动后,第一个类型的数据可以被成功获取,但是后面类型的数据就会提示签名失败。但是单独拉后面类型的数据时,又可以成功,进过排查,发现是第三方包中的一个核心类其中有很多变量被设置为了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值却不一样了,实现了逻辑 

  • 相关阅读:
    微软外服 AlI In One
    js 循环多次和循环一次的时间的性能对比 All In One
    vue inject All In One
    Excel 表格数据倒置 All In One
    SVG tickets All In One
    OH MY ZSH All In One
    js array for loop performance compare All In One
    mac terminal show You have new mail All In one
    新闻视频 26 制作母版页
    转自牛腩 母版页和相对路径
  • 原文地址:https://www.cnblogs.com/limingluzhu/p/5968253.html
Copyright © 2011-2022 走看看