背景本文通过如何将一个单例类实例化两次的案例,用代码实践来引入 Java 类加载器相关的概念与工作机制。理解并熟练掌握相关知识之后可以扩宽解决问题的思路,另辟蹊径,达到目的。
(资料图片)
单例模式是最常用的设计模式之一。其目的是保证一个类在进程中仅有一个实例,并提供一个它的全局访问方式。那什么场景下一个进程里需要单例类的两个对象呢?很明显这破坏了单例模式的设计初衷。
这里举例一个我司的特殊场景:
RPC 的调用规范是每个业务集群里只能有一个调用方,如果一个业务节点已经实例化了一个客户端,就无法再实例化另一个。这个规范的目的是让一个集群统一个调用方,方便服务数据的收集、展示、告警等操作。
一个项目有多个集群,多个项目组维护,各个集群都有一个共同特点,需要调用相同的 RPC 服务。如果严格按照上述 RPC 规范的话,每一个集群都需要申请一个自己调用方,每一个调用方都申请相同的 RPC 服务。这样做完全没有问题,只是相同的工作会被各个集群都做一遍,并且生成了多个 RPC 的调用方。
最终方案是将相同的逻辑代码打包成一个公用 jar 包,然后其他集群引入这个包就能解决我们上述的问题。这么做的话就碰到了 RPC 规范中的约束问题,jar 包里的公用逻辑会调用 RPC 服务,那么势必会有一个 RPC 的公用调用方。我们的业务代码里也会有自己业务需要调用的其他 RPC 服务,这个调用方和 jar 包里的调用方就冲突了,只能有一个调用方会被成功初始化,另一个则会报错。这个场景是不是就要实例化两个单例模式的对象呢。
有相关经验的读者可能会想到,能不能把各个集群中相同的工作抽取出来,做成一个类似网关的集群,然后各个集群再来调用这个公用集群,这样同一个工作也不会被做多遍,RPC 的调用方也被整合成了一个。这个方案也是很好的,考虑到一些客观因素,最终并没有选择这种方式。
实例化两个单例类我们假设下述单例类代码是 RPC 的调用 Client:
public class RPCClient { private static BaseClient baseClient; private volatile static RPCClient instance; static { baseClient = BaseClient.getBaseClient(); } private RPCClient() { System.out.println("构造 Client"); } public String callRpc() { return "callRpc success"; } public static RPCClient getClient() { if (instance == null) { synchronized (RPCClient.class) { if (instance == null) { instance = new RPCClient(); } } } return instance; }}public class BaseClient { ... private BaseClient() { System.out.println("构造 BaseClient"); } ...}这个单例 Client 有一点点不同,就是有一个静态属性 baseClient,BaseClient 也是一个简单的单例类,构造方法里有一些打印操作,方便后续观察。baseClient 属性通过静态代码块来赋值。
我们可以想一想,有什么办法可以将这个单例的 Client 类实例化两个对象出来?
无所不能的反射大法最容易想到的就是利用反射获取构造方法,来规避单例类私有化构造方法的约束来实例化:
Constructor> declaredConstructor = RPCClient.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);Object rpcClient = declaredConstructor.newInstance();Method sayHi = rpcClient.getClass().getMethod("callRpc");Object invoke = sayHi.invoke(rpcClient);//执行输出//构造 Client//callBaseRpc successcallRpc success上述代码通过反射来获取私有化的构造方法,然后通过这个构造方法来实例化对象。这样确实能生成单例 RPCClient 的第二个对象。观察代码执行的输出能发现,通过反射生成的这个对象 rpcClient 确实是一个新对象,因为输出里有 RPCClient 构造方法的打印输出。但是并没有打印 BaseClient 这个对象的构造方法里的输出。rpcClient 这个对象里的 baseClient 永远都是只用一个,因为 baseClient 在静态代码块里赋值的,并且 BaseClient 又是一个单例类。这样,我们反射生成的对象与非反射生成的对象就不是完全隔离的。
上述的简单 Demo 里,使用反射好像都不太能够生成两个完全隔离的单例客户端。一个复杂的 RPC Client 类可远没有这么简单,Client 类里还有很多依赖的类,依赖的类里也会依赖其他类,其中不乏各种单例类。通过反射的方法好像行不太通。那还有什么方法能达到目的呢?
自定义类加载器另一个方法是用一个自定义的类加载器来加载 RPCClient 类并实例化。业务代码默认使用的是 AppClassLoader类加载器,这个类加载器来加载 RPCClient 类并实例化第一个 Client 对象,我们自定义的类加载器会加载并实例化第二个 Client 对象。那么在一个 JVM 进程里就存在了两个 RPCClient 对象了。这两个对象会不会存在上述反射中没有完全隔离的问题呢?
答案是不会。类加载是有传递性的,当一个类被加载时,这个类依赖的类如果需要加载,使用的类加载器就是当前类的类加载器。我们使用自定义类加载器加载 RPCClient 时,RPCClient 依赖的类也会被自定义加载器加载。这样依赖类也会被完全隔离,也就没有在上述反射中存在的 baseClient 属性还是同一个对象的情况。
自定义类加载器代码如下:
public class MyClassLoader extends ClassLoader{ @Override public Class> loadClass(String name) { //通过 findLoadedClass 判断是否已经被加载 (下文会补充) Class> loadedClass = findLoadedClass(name); //如果已加载返回已加载的类 if (loadedClass != null) { return loadedClass; } //通过类名获取类文件 String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream resourceAsStream = getClass().getResourceAsStream(fileName); //如果查找不到文件 则委托父类加载器实现 这里的父加载器就是 AppClassLoader if (resourceAsStream == null) { return super.loadClass(name); } //读取文件 并加载类 byte[] bytes = new byte[resourceAsStream.available()]; resourceAsStream.read(bytes); return defineClass(name, bytes, 0, bytes.length); }}测试代码如下:
//实例化自定义类加载器MyClassLoader myClassLoader = new MyClassLoader();//获取当前线程的 ContextClassLoader 备用ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();//设置当前线程的 ContextClassLoader 为实例化的自定义类加载器(这么做的原因下文会补充)Thread.currentThread().setContextClassLoader(myClassLoader);//通过自定义类加载器加载 RPCClientClass> rpcClientCls = myClassLoader.loadClass("com.ppphuang.demo.classloader.single.RPCClient");//将当前线程的 ContextClassLoader 还原为初始的 contextClassLoaderThread.currentThread().setContextClassLoader(contextClassLoader);//通过反射获取该类的 getClient 方法Method getInstance = rpcClientCls.getMethod("getClient");getInstance.setAccessible(true);//调用 getClient 方法获取单例对象Object rpcClient = getInstance.invoke(rpcClientCls);//获取 callRpc 方法Method callRpc = rpcClientCls.getMethod("callRpc");//调用 callRpc 方法Object callRpcMsg = callRpc.invoke(rpcClient);System.out.println(callRpcMsg);//执行输出//构造 BaseClient//构造 Client//callBaseRpc successcallRpc success通过测试代码的输出可以看到,RPCClient BaseClient 这两个类构造方法里的打印都输出了,那就说明通过自定义类加载器实例化的两个对象都执行了构造方法。自然就跟直接调用 RPCClient.getClient() 生成的对象是完全隔离开的。
你可以通过代码注释,来理解一下测试代码的执行过程。
如果看到这里你还有一些疑问的话,我们再巩固一下类加载器相关的知识。
类与类加载器默认类加载在 Java 中有三个默认的类加载器:
BootstrapClassLoader加载 Java 核心库(JAVA_HOME/jre/lib/rt.jar 或 sun.boot.class.path 路径下的内容)。用于提供 JVM 自身需要的类。由 C++ 加载,用如下代码去获取的话会显示为 null:
System.out.println(String.class.getClassLoader());ExtClassLoaderJava 语言编写,从 java.ext.dirs 系统属性所指定的目录中加载类,或从 JDK 的安装目录 jre/lib/ext 子目录下加载类。如果用户创建 的 jar 放在此目录下,也会自动由 ExtClassLoader 加载。
System.out.println(com.sun.crypto.provider.DESedeKeyFactory.class.getClassLoader());AppClassLoader它负责加载环境变量 classpath 或系统属性 java.class.path 指定路径下的类,应用程序中默认是系统类加载器。
System.out.println(ClassLoader.getSystemClassLoader());如果我们没有特殊指定类加载器的话,JVM 进程中所有需要的类都会由上述三个类加载来完成加载。
每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个 ClassLoader 加载的:
class Class { private final ClassLoader classLoader;} 你可以这样来获取某个类的 ClassLoader:
System.out.println(obj.getClass().getClassLoader());不同类加载器的影响两个类相同的前提是类的加载器也相同,不同类加载器加载同一个 Class 也是不一样的 Class,会影响 equals、instanceof 的运算结果。
下面的代码展示了不同类加载器对类判等的影响,为了减少代码篇幅,代码省略了异常处理:
public class ClassLoaderTest { public static void main(String[] args) { ClassLoader myClassLoader = new ClassLoader() { @Override public Class> loadClass(String name) { Class> loadedClass = findLoadedClass(name); if (loadedClass != null) return loadedClass; String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream resourceAsStream = getClass().getResourceAsStream(fileName); if (resourceAsStream == null) { return super.loadClass(name); } byte[] bytes = new byte[resourceAsStream.available()]; resourceAsStream.read(bytes); return defineClass(name, bytes, 0, bytes.length); } }; Object obj = myClassLoader.loadClass("ClassLoaderTest").newInstance(); System.out.println(obj.getClass().getClassLoader()); System.out.println(com.ppphuang.demo.classloader.ClassLoaderTest.class.getClassLoader()); System.out.println(obj instanceof ClassLoaderTest); }}//输出如下://com.ppphuang.demo.classloader.ClassLoaderTest$1@7a07c5b4//sun.misc.Launcher$AppClassLoader@18b4aac2//false上述代码自定义了一个类加载器 myClassLoader,用 myClassLoader 加载的 ClassLoaderTest 类实例化出的对象与 AppClassLoader 加载的 ClassLoaderTest 类做 instanceof 运算,最终输出的接口是 false。由此可以判断出不同加载器加载同一个类,这两个类也是不相同的。
因为不同类加载器的加载的类是不同的,所以我们可以在一个 JVM 里通过自定义类加载器来将一个单例类实例化两次。
ClassLoader 传递性程序在运行过程中,遇到了一个未知的类,它会选择哪个 ClassLoader 来加载它呢?
虚拟机的策略是使用调用者 Class 对象的 ClassLoader 来加载当前未知的类。就是在遇到这个未知的类时,虚拟机肯定正在运行一个方法调用(静态方法或者实例方法),这个方法写在哪个类,那这个类就是调用者 Class 对象。前面我们提到每个 Class 对象里面都有一个 classLoader 属性记录了当前的类是由谁来加载的。
因为 ClassLoader 的传递性,所有延迟加载的类都会由初始调用 main 方法的这个 ClassLoader 全权负责,它就是 AppClassLoader。
ClassLoaderTest classLoaderTest = new ClassLoaderTest();System.out.println(classLoaderTest.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2如果我们使用一个自定义类加载器加载一个类,那么这个类里依赖的类也会由这个类加载来负责加载:
Object obj = myClassLoader.loadClass("com.ppphuang.demo.classloader.ClassLoaderTest").newInstance();因为类加载器的传递性,依赖类的加载器也会使用当前类的加载器,当我们利用自定义类加载器来将一个单例类实例化两次的时候,能保证两个单例对象是完全隔离。
双亲委派模型当一个类加载器需要加载一个类时,自己并不会立即去加载,而是首先委派给父类加载器去加载,父类加载器加载不了再给父类的父类去加载,一层一层向上委托,直到顶层加载器(BootstrapClassLoader),如果父类加载器无法加载那么类加器才会自己去加载。
findLoadedClass当一个类被父加载器加载了,子加载器再次加载这个类的时候,还需要向父加载器委托吗?
我们先把问题细化一下:
AClassLoader 的父加载器为 BClassLoader,BClassLoader 的父加载器为 CClassLoader,当 AClassLoader 调用 loadClass() 加载类,并最终由 CClassLoader 加载的类,到底算谁加载的?
后续 AClassLoader 再加载相同类时,是否能直接从 AClassLoader 的 findLoadedClass0() 中找到该类并返回,还是说再走一次双亲委派最终从 CClassLoader 的 findLoadedClass0() 中找到该类并返回?
JVM 里有一个数据结构叫做 SystemDictonary,这个结构主要就是用来检索我们常说的类信息,其实也就是 private native final Class> findLoadedClass0(String name) 方法的逻辑。
这些类信息对应的结构是 klass,对 SystemDictonary 的理解,可以理解为一个哈希表,key 是类加载器对象 + 类的名字,value是指向 klass 的地址。当我们任意一个类加载器去正常加载类的时候,就会到这个 SystemDictonary 中去查找,看是否有这么一个 klass 可以返回,如果有就返回它,否则就会去创建一个新的并放到结构里。
这里面还涉及两个小概念,初始类加载器、定义类加载器。
上述类加载问题中,AClassLoader 加载类的时候会委托给 BClassLoader 来加载,BClassLoader 加载类的时候会委托给 CClassLoader 来加载,当 AClassLoader 调用 loadClass() 加载类,并最终由 CClassLoader 加载,那么我们称 CClassLoader 为该类的定义类加载器,AClassLoader 和 BClassLoader 为该类的初始类加载器。在这个过程中,AClassLoader、BClassLoader 和 CClassLoader 都会在 SystemDictonary 生成记录。那么后续 C 的子加载器(AClassLoader 和 BClassLoader)加载相同类时,就能在自己 findLoadedClass0() 中找到该类,不必再向上委托。
双亲委派的目的防止重复加载类。在 JVM 中,要唯一确定一个对象,是由类加载器和全类名两者共同确定的,考虑到各层级的类加载器之间仍然由重叠的类资源加载区域,通过向上抛的方式可以避免一个类被多个不同的类加载器加载,从而形成重复加载。
防止系统 API 被篡改。例如读者定义了一个名为 java.lang.Integer 的类,而该类在核心库中也存在,借用双亲委派的机制,我们就能有效防止该自定义的同名类被加载,从而保护了平台的安全性。
JDK 1.2 之后引入双亲委派的方式来实现类加载器的层次调用,以尽可能保证 JDK 的系统 API 不会被用户定义的类加载器所破坏,但一些使用场景会打破这个惯例来实现必要的功能。
破坏双亲委派模型Thread Context ClassLoader在介绍破坏双亲委派模型之前,我们先了解一下 Thread Context ClassLoader(线程上下文类加载器)。
JVM 中经常需要调用由其他厂商实现并部署在应用程序的 ClassPath 下的 JNDI 服务提供者接口 (Servicepovider iotertace, SPD) 的代码,现在问题来了,启动类加载器是绝不可能认识、加载这些代码的,那该怎么办?为了解决这个困境,Java 的设计团队只好引入了一个不太优雅的设计:线程上下文类加裁器 ( Thread Context ClassLoader)。这个类加载器可以通过 java.lang.Thread类的 setContextClassLoader方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是 AppClassLoader。有了线程上下文类加载器,程序就可以做一些 “舞弊”的事情了。JNDI 服务使用这个线程上下文类加载器去加载所需的 SPI 服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器,已经违背了双亲委派模型的一般性原则,但也是无可奈何的事情。Java 中涉及 SPI 的加载基本上都采用这种方式来完成的。
可以通过如下的代码来获取当前线程的 ContextClassLoader :
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();我们在前面测试代码中将 Thread Context ClassLoader 也设置为自定义加载器,目的是避免自定义加载器加载的类里面使用了 Thread Context ClassLoader(默认是 AppClassLoader),导致对象没有完全完全隔离,这也是自定义加载器的常用原则之一。在自定义加载器加载完成之后也要将 Thread Context ClassLoader 复原:
//实例化自定义类加载器MyClassLoader myClassLoader = new MyClassLoader();//获取当前线程的 ContextClassLoader 备用ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();//设置当前线程的 ContextClassLoader 为实例化的自定义类加载器(这么做的原因下文会补充)Thread.currentThread().setContextClassLoader(myClassLoader);//通过自定义类加载器加载 RPCClientClass> rpcClientCls = myClassLoader.loadClass("com.ppphuang.demo.classloader.single.RPCClient");//将当前线程的 ContextClassLoader 还原为初始的 contextClassLoaderThread.currentThread().setContextClassLoader(contextClassLoader);Tomcat类加载模型提到破坏双亲委派模型就必须要提到 Tomcat,部署在一个 Tomcat 中的每个应用程序都会有一个独一无二的 webapp classloader,他们互相隔离不受彼此的影响。除了互相隔离的类加载器,Tomcat 中还有共享的类加载器,大家可以去查看一下相关的文档,还是很值得我们借鉴学习的。
看到这里再回头来理解上文自定义类加载器实例化单例类的代码,应该就很好理解了。
总结本文通过如何将一个单例类实例化两次的案例,用代码实践来引入 Java 类加载器相关的概念与工作机制。理解并熟练掌握相关知识之后可以扩宽解决问题的思路,另辟蹊径,达到目的。
参考https://blog.csdn.net/qq_43369986/article/details/117048340
https://blog.csdn.net/qq_40378034/article/details/119973663
https://blog.csdn.net/J080624/article/details/84835493
公众号:DailyHappy一位后端写码师,一位黑暗料理制造者。
关键词:
不是单例的单例——巧用ClassLoader 本文通过如何将一个单例类实例化两次的案例,用代码实践来引入Java类加载器相关的概念与工作机制。理解并熟
硒化镉 全球看热讯 1、硒化镉,是一种无机化合物,化学式为CdSe,主要用作电子发射器和光谱分析、光导体、半导体、光敏元件等
瑞安市三星包装机械厂_关于瑞安市三星包装机械厂介绍 焦点播报 瑞安市三星包装机械厂,关于瑞安市三星包装机械厂介绍这个很多人还不知道,我们一起来看看!1、瑞安市三星包
标普:确认永利度假村(WYNN.US)及其子公司“B+”发行人信用评级,上调展望至“正面... 标普将WynnResortsLtd 的评级展望从负面调整为正面。同时,标普还确认了WynnResorts及其子公司的“B+”发行
《银河护卫队3》全球票房榜第5名 环球速递 据猫眼专业版数据,《银河护卫队3》上映第2周,全球累计票房突破5亿美元,北美票房2 13亿美元,进入2023年
焦点观察:天津520可以办理结婚登记吗? ➤➤天津520可以办理结婚登记吗?答:可以办理。今年5月20日恰逢周六,全市19个婚姻登记机关为爱加班,可以
不是单例的单例——巧用ClassLoader 本文通过如何将一个单例类实例化两次的案例,用代码实践来引入Java类加载器相关的概念与工作机制。理解并熟
龙井明东边境派出所开展防震减灾宣传活动 为进一步提升群众防震减灾意识,增强自救避险能力,近日,龙井明东边境派出所开展防震减灾宣传活动。活动中
热!北京今天或现今年首个高温日 市气象台11时发布天气预报:今天下午晴,北风一级转南风三四级(阵风五级左右),最高气温35摄氏度;夜间晴
硒化镉 全球看热讯 1、硒化镉,是一种无机化合物,化学式为CdSe,主要用作电子发射器和光谱分析、光导体、半导体、光敏元件等
“冷门歌手”隐退多年人工智能带来新热度 世界今日报 最近,“孙燕姿”翻唱的各种经典曲目成为网友津津乐道的话题。据了解,在B站与“AI孙燕姿”相关的视频已经
翔港科技中标上海烟草集团异型烟包装业务 【翔港科技中标上海烟草集团异型烟包装业务】上海烟草集团5月15日发出开标公告,翔港科技(603499)中标该集
热资讯!国金证券:AI使IP价值进一步凸显 关注拥有优质IP的公司 国金证券指出,AI技术、应用持续迭代中,大模型时代,IP价值进一步凸显,关注拥有优质IP的标的。AI技术有望
银河证券:水泥价格继续下降 熟料库存有所上升 银河证券认为,水泥价格继续下降,熟料库存有所上升。今年旺季需求恢复情况不及预期,预计短期市场需求仍呈
华安证券:碳酸锂价格回升 锂电回收产业景气度修复 华安证券指出,锂盐价格回升,关注优质锂标的,回收产业链景气修复,推荐工艺领先渠道布局企业。中长期看,
瑞可达:公司已于近期发布液冷充电枪产品-热点在线 【瑞可达:公司已于近期发布液冷充电枪产品】瑞可达在互动平台表示,公司在新能源汽车配套市场形成了丰富的
竞争对手的赞美:Oculus创始人盛赞苹果MR“非常棒”
尽管Luckey在后续的回复并未对这一推文进行更多的解释或扩展,也并未表示他是否被告知或亲身体验过了苹果头
瑞安市三星包装机械厂_关于瑞安市三星包装机械厂介绍 焦点播报 瑞安市三星包装机械厂,关于瑞安市三星包装机械厂介绍这个很多人还不知道,我们一起来看看!1、瑞安市三星包
环球热消息:挖呀挖黄老师上电视,真容被嘲嫩牛五方,网友:榜一大哥连夜跑路 在小小的花园里挖呀挖呀挖,这首歌因为黄老师在短视频的重新演绎和翻唱不火过一段时间。黄老师被中国教育台
医美行业市场多大?医美市场的趋势走向分析2023-环球新资讯 随着“颜值经济”兴起,医美市场快速发展,不断满足人们对美的追求。但是在医美行业发展过程中,非法行医、
速递!红茶行业市场发展现状分析2023 近年来,随着国内传统红茶的逐渐恢复,以及新兴红茶的快速发展,市场对红茶的消费需求持续增长,进而推动红
每日视讯:中国乌龙茶进出口市场分析 中国乌龙茶行业市场发展现状分析2023 近年来,在国内乌龙茶种植面积不断扩大以及国内外市场需求不断增加背景下,其产销规模稳步提升,行业发展前
教育物联网行业分析 教育物联网市场前景分析2023 环球热讯 随着互联网的普及,教育的方式也变得丰富起来,特别是“智慧教育”,成为信息时代教育工作开展的重要抓手。
机构策略:A股风险偏好有望迎来企稳 世界热闻 华西证券认为,4月通胀、金融数据低于预期显示当前经济内生动能不强,居民信心偏弱。从这个角度来看,政策
环球速递!商品期货早盘收盘涨跌不一,铁矿石、焦炭、橡胶涨超2%,玻璃跌超4% 金融界5月15日消息国内期货市场早盘收盘,商品期货涨跌不一,铁矿石、焦炭、橡胶涨超2%,焦煤、豆一、热卷
2023长城汽车智慧工厂马拉松圆满落幕 2023年5月14日,作为世界首个跑进工厂的马拉松,2023长城汽车智慧工厂马拉松大赛燃情开跑。长城汽车创造性
4月PC端微软Bing市场份额降至7.14% 不及去年10月数据 滚动 5月15日,据美国第三方数据服务商StatCounter数据,在全球范围内,4月,微软Bing的台式电脑桌面搜索市场份
科技部副部长吴朝晖:开放合作是推动科技进步的内在要求 上证报记者史丽摄上证报中国证券网讯(记者李雁争)科技部副部长吴朝晖15日在国新办发布会上表示,开放合作
新华医疗与京东健康签署战略合作协议 【新华医疗与京东健康签署战略合作协议】据新华医疗消息,5月14日,新华医疗与京东健康于上海第87届中国国
富士胶片参展P&E 2023 数码相机产品展示亿级像素“魔力” 本报记者李雯珊近日,第二十四届中国国际照相机械影像器材与技术博览会(以下简称“P&E2023”)在北京展览
盘中异动 | 天弘中证沪港深云计算产业ETF跌2.03%_世界球精选 天弘中证沪港深云计算产业ETF(517390)5月15日盘中跌2 03%,最新单位净值为0 9150。该基金最近一周跌3 29%
迎小满!在希望的田野上 各地采摘丰收忙→ 再有一周,我国就将迎来小满节气,各地也是一片丰收忙的景象。小麦飘香丰收在望在迈向小满节气的这个过程中
标普:确认永利度假村(WYNN.US)及其子公司“B+”发行人信用评级,上调展望至“正面... 标普将WynnResortsLtd 的评级展望从负面调整为正面。同时,标普还确认了WynnResorts及其子公司的“B+”发行
凯撒文化:游戏《遮天世界》预计第三季度完成测试 凯撒文化所属行业是文化传媒,5月15日凯撒文化在业绩说明会上表示,《遮天世界》已于2023年初获得版号,预
《银河护卫队3》全球票房榜第5名 环球速递 据猫眼专业版数据,《银河护卫队3》上映第2周,全球累计票房突破5亿美元,北美票房2 13亿美元,进入2023年
重庆大学新增1个ESI全球前1%学科!|每日快播 北京时间2023年5月11日ESI最新数据更新,重庆大学药理学与毒理学首次进入ESI世界排名前1%。至此,重庆大学
翔港科技中标上海烟草集团异型烟包装业务 【翔港科技中标上海烟草集团异型烟...
银河证券:水泥价格继续下降 熟料库存有所上升 银河证券认为,水泥价格继续下降,...
华安证券:碳酸锂价格回升 锂电回收产业景气度修复 华安证券指出,锂盐价格回升,关注...
2023长城汽车智慧工厂马拉松圆满落幕 2023年5月14日,作为世界首个跑进...
怎样保存大米一年不坏?真空大米放了3—4年还能吃么? 怎样保存大米一年不坏?1、用锅煮花...
最新消息:河北省40幅摄影作品参加第三十四届华北摄影艺术展览 近日,第三十四届中国华北摄影艺术...
获奖名单公布!第十三届中国艺术节在河北雄安新区落下帷幕 9月15日晚,第十三届中国艺术节在...
2022北京时装周9月15日至22日将在线上线下举办多场活动 一年一度的北京时装盛典即将拉开帷...