返回顶部
首页 > 资讯 > 精选 >Tomcat9中类加载体系是怎么样的
  • 506
分享到

Tomcat9中类加载体系是怎么样的

2023-06-02 13:06:33 506人浏览 独家记忆
摘要

小编给大家分享一下Tomcat9中类加载体系是怎么样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.概要Tomcat作为WEB容器,支持多个应用程序的部署运行

小编给大家分享一下Tomcat9中类加载体系是怎么样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

1.概要

Tomcat作为WEB容器,支持多个应用程序的部署运行,所以会涉及应用程序间类库的隔离和共享,例如:不同应用程序既可以使用某个工具类库的不同版本但互不影响,亦可以共享使用某个类库。

2.JVM双亲委派模型

在JVM中有3种系统提供的类加载器:

  • 启动类加载器Bootstrap ClassLoader:这负载加载存放在

    /lib目录中的类库,启动类加载器无法被Java程序直接引用
  • 扩展类加载器Extension ClassLoader:负载加载

    /lib/ext目录中的类库
  • 应用程序类加载器 Application ClassLoader:负载加载用户类路径CLASSPATH上所指定的类库,ClassLoader.getSysteClassLoader()方法的返回值就是此加载器,一般情况下这个就是程序中默认的类加载器

我们的应用程序都是由这3种类加载器互相配合进行加载的,还可以加入自己定义的类加载器,他们之间的关系如图所示:
Tomcat9中类加载体系是怎么样的
图中展示的类加载器之间的层次关系,成为类加载器的双亲委派模型(Parents Delegation Model),双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应该有父类加载器。这里类加载器之间的父子关系一般不会继承(Inheritance)的强耦合关系来实现,而是使用组合(Composition)的弱耦合关系来复用父加载器的代码。

类加载器的双亲委派模型在jdk1.2期间被引入并被广泛应用于之后的所有Java程序中,但它并不是一个强制的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式,在之后章节中会看到,在Apache Tomcat中就打破了此模型。

双亲委派模型的工作过程是:如果一个类加载器收到加载类的请求,他首先不会自己去加载这个类,而是把这个请求委派给父加载器去完成,每个层次的类加载器都是如此处理,所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时(没有找类),子加载器才会尝试自己加载。

双亲委派模型本质上就是委托代理,虽然简单,却使Java类随着类加载器一起具备了优先级层次,例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器加载这个类,最终都是委派给启动类加载器进行加载,最终Object类在程序中的各种类加载器环境中都是同一个类。也就是说,如果你尝试编写一个与rJDK基础类库中已有类重名的Java类,类似java.lang.Object,你会发现,虽然可以正常编译,但永远无法被加载运行。

3.Tomcat9类加载体系

功能健全的Web容器的类加载器需要支持以下几点:

  • 服务器的类库要与Web应用程序类库互相隔离,互不影响

  • 同一个服务器上两个Web程序所使用的Java类库实现互相独立隔离

  • 同一个服务器上了两个Web应用程序所使用的Java类库可以被共享

  • 支持jsP的热替换(HotSwap),修改JSP文件不需要重启服务器

基于以上需求,下图为Tomcat类加载器的委派关系:Tomcat9中类加载体系是怎么样的

  • Common ClassLoader:Tomcat的基本类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp应用程序访问

  • Catalina ClassLoader:Tomcat容器私有的类加载器,加载路径中的class仅对Tomcat容器本身可见,Webapp应用程序不可访问

  • Shared ClassLoader:各个Webapp应用程序共享的类加载器,加载路径中的class对于所有的webapp可见

  • Webapp ClassLoader:各个 Webapp私有的类加载器,加载路径中的class仅对当前webapp可见

  • JasperLoader:记载方位仅为JSP文件所编译出的一个.class文件,当容器检测到JSP文件被修改时,互替换掉目前的JasperLoader实例,通过新建一个JSP类加载器来实现JSP文件的HotSwap功能

4.源码分析

org.apache.catalina.startup.Bootstrap.init()
       public void init() throws Exception {        initClassLoaders(); //初始化类加载器        Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程类加载器为catalinaLoader        SecurityClassLoad.securityClassLoad(catalinaLoader); //使用Java SecurityManager初始化        // Load our startup class and call its process() method        if (log.isDebugEnabled())            log.debug("Loading startup class");        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");        Object startupInstance = startupClass.getConstructor().newInstance();        // Set the shared extensions class loader        if (log.isDebugEnabled())            log.debug("Setting startup class properties");        String methodName = "setParentClassLoader";        Class<?> paramTypes[] = new Class[1];        paramTypes[0] = Class.forName("java.lang.ClassLoader");        Object paramValues[] = new Object[1];        paramValues[0] = sharedLoader; //WebappLoader的parentLoader        Method method =            startupInstance.getClass().getMethod(methodName, paramTypes);        method.invoke(startupInstance, paramValues);        catalinaDaemon = startupInstance;    }

commLoader、catalinaLoader和sharedLoader的初始化是在Tomcat容器运行的最初进行的,调用的方法为org.apache.catalina.startup.Bootstrap.init()

  • initClassLoaders初始化commLoader、catalinaLoader和sharedLoader

  • 设置当前线程的类加载器为catalinaLoader,实现catalinaLoader为容器自身的私有类加载的目的

  • 当使用Java SecurityManager时进行一些初始化工作

  • 将sharedLoader向后传递,以作为webappLoader的parent

org.apache.catalina.startup.Bootstrap.initClassLoaders()
 private void initClassLoaders() {        try {            commonLoader = createClassLoader("common", null);            if (commonLoader == null) {                // no config file, default to this loader - we might be in a 'single' env.                commonLoader = this.getClass().getClassLoader();            }            catalinaLoader = createClassLoader("server", commonLoader);            sharedLoader = createClassLoader("shared", commonLoader);        } catch (Throwable t) {            handleThrowable(t);            log.error("Class loader creation threw exception", t);            System.exit(1);        }    }

initClassLoaders方法中,对commLoader、catalinaLoader和sharedLoader进行初始化

  • commLoader、catalinaLoader和sharedLoader均通过createClassLoader创建

  • createClassLoader方法的第二参数为父类加载器,所以catalinaLoader、sharedLoader的父类加载器为commLoader

org.apache.catalina.startup.Bootstrap.createClassLoader()
private ClassLoader createClassLoader(String name, ClassLoader parent)        throws Exception {        String value = CatalinaProperties.getProperty(name + ".loader");        if ((value == null) || (value.equals("")))            return parent;        value = replace(value);        List<Repository> repositories = new ArrayList<>();        String[] repositoryPaths = getPaths(value);        for (String repository : repositoryPaths) {            // Check for a JAR URL repository            try {                @SuppressWarnings("unused")                URL url = new URL(repository);                repositories.add(new Repository(repository, RepositoryType.URL));                continue;            } catch (MalfORMedURLException e) {                // Ignore            }            // Local repository            if (repository.endsWith("*.jar")) {                repository = repository.substring                    (0, repository.length() - "*.jar".length());                repositories.add(new Repository(repository, RepositoryType.GLOB));            } else if (repository.endsWith(".jar")) {                repositories.add(new Repository(repository, RepositoryType.JAR));            } else {                repositories.add(new Repository(repository, RepositoryType.DIR));            }        }        return ClassLoaderFactory.createClassLoader(repositories, parent);    }
  • 获取类加载器相应的加载路径,路径的配置信息默认在conf/catalina.properties中,也可以通过环境变量catalina.config指定配置文件

  • 类加载路径支持:.jar、*.jar和目录三种形式

  • 当没有配置类加载器对应的加载路径时,则直接返回父类加载器。Tomcat9中catalinaLoader和sharedLoader均没有进行配置,故这两个加载器均为commonLoader
    以下为Tomat9的conf/catalina.properties默认的加载路径配置:

    common.loader="${catalina.base}/lib","${catalina.base}/lib    @Override    protected synchronized void startInternal() throws LifecycleException {        ...省略其他代码...        if (getLoader() == null) {            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());            webappLoader.setDelegate(getDelegate());            setLoader(webappLoader);        }        ...省略其他代码...    }

    webappLoader的初始化在StandardContext中进行,这里创建WebappLoader

    org.apache.catalina.loader.WebappLoader
        //重新实现ClassLoader    private String loaderClass = ParallelWebappClassLoader.class.getName();    @Override    protected void startInternal() throws LifecycleException {        ...省略其他代码...        // Construct a class loader based on our current repositories list        try {            classLoader = createClassLoader(); //创建webappLoader            classLoader.setResources(context.getResources());            classLoader.setDelegate(this.delegate); //是否双亲委派         ...省略其他代码...    }        private WebappClassLoaderBase createClassLoader()        throws Exception {        Class<?> clazz = Class.forName(loaderClass);        WebappClassLoaderBase classLoader = null;        if (parentClassLoader == null) {            parentClassLoader = context.getParentClassLoader();        }        Class<?>[] argTypes = { ClassLoader.class };        Object[] args = { parentClassLoader };        Constructor<?> constr = clazz.getConstructor(argTypes);        classLoader = (WebappClassLoaderBase) constr.newInstance(args); //反射创建类加载器        return classLoader;    }
    • 在WebappLoader的startInternal()方法调用createClassLoader()创建webappLoader

    • createClassLoader()通过反射创建ParallelWebappClassLoader

    org.apache.catalina.loader.ParallelWebappClassLoader
    public class ParallelWebappClassLoader extends WebappClassLoaderBase{    ...省略其他代码...}public abstract class WebappClassLoaderBase extends URLClassLoader        implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {    ...省略其他代码...    @Override    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {        synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {            if (log.isDebugEnabled())                log.debug("loadClass(" + name + ", " + resolve + ")");            Class<?> clazz = null;            // Log access to stopped class loader            checkStateForClassLoading(name);            // (0)检查之前加载过的本地缓存,如果缓存存在,则取缓存中的类            clazz = findLoadedClass0(name);            if (clazz != null) {                if (log.isDebugEnabled())                    log.debug("  Returning class from cache");                if (resolve)                    resolveClass(clazz);                return clazz;            }            // (0.1)检查之前加载过的本地缓存,如果缓存存在,则取缓存中的类            // GraalVM直接返回null,查询缓存时,会校验name的合法性            clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);            if (clazz != null) {                if (log.isDebugEnabled())                    log.debug("  Returning class from cache");                if (resolve)                    resolveClass(clazz);                return clazz;            }            //(0.2) 通过系统加载器加载类,放置webapp中覆写Java SE的基础类            //此处的类加载器应为Bootstrap ClassLoader            String resourceName = binaryNameToPath(name, false);            ClassLoader javaseLoader = getJavaseClassLoader();            boolean tryLoadingFromJavaseLoader;            try {                URL url;                if (securityManager != null) {                    PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);                    url = AccessController.doPrivileged(dp);                } else {                    url = javaseLoader.getResource(resourceName);                }                tryLoadingFromJavaseLoader = (url != null);            } catch (Throwable t) {                // Swallow all exceptions apart from those that must be re-thrown                ExceptionUtils.handleThrowable(t);                // The getResource() trick won't work for this class. We have to                // try loading it directly and accept that we might get a                // ClassNotFoundException.                tryLoadingFromJavaseLoader = true;            }            if (tryLoadingFromJavaseLoader) {                try {                    clazz = javaseLoader.loadClass(name);                    if (clazz != null) {                        if (resolve)                            resolveClass(clazz);                        return clazz;                    }                } catch (ClassNotFoundException e) {                    // Ignore                }            }            // (0.5) SecurityManager安全检查            if (securityManager != null) {                int i = name.lastIndexOf('.');                if (i >= 0) {                    try {                        securityManager.checkPackageAccess(name.substring(0,i));                    } catch (SecurityException se) {                        String error = sm.getString("webappClassLoader.restrictedPackage", name);                        log.info(error, se);                        throw new ClassNotFoundException(error, se);                    }                }            }            boolean delegateLoad = delegate || filter(name, true);            // (1) 当设置双亲委派或者加载类的名称为Tomcat容器内部的类,则委托给父类加载器加载            if (delegateLoad) {                if (log.isDebugEnabled())                    log.debug("  Delegating to parent classloader1 " + parent);                try {                    clazz = Class.forName(name, false, parent);                    if (clazz != null) {                        if (log.isDebugEnabled())                            log.debug("  Loading class from parent");                        if (resolve)                            resolveClass(clazz);                        return clazz;                    }                } catch (ClassNotFoundException e) {                    // Ignore                }            }            // (2) 在应用类加载路径进行加载            if (log.isDebugEnabled())                log.debug("  Searching local repositories");            try {                clazz = findClass(name);                if (clazz != null) {                    if (log.isDebugEnabled())                        log.debug("  Loading class from local repository");                    if (resolve)                        resolveClass(clazz);                    return clazz;                }            } catch (ClassNotFoundException e) {                // Ignore            }            // (3) 委托父类加载器加载            if (!delegateLoad) {                if (log.isDebugEnabled())                    log.debug("  Delegating to parent classloader at end: " + parent);                try {                    clazz = Class.forName(name, false, parent);                    if (clazz != null) {                        if (log.isDebugEnabled())                            log.debug("  Loading class from parent");                        if (resolve)                            resolveClass(clazz);                        return clazz;                    }                } catch (ClassNotFoundException e) {                    // Ignore                }            }        }        throw new ClassNotFoundException(name);    }    ...省略其他代码...}
    • ParallelWebappClassLoader继承了WebappClassLoaderBase,而WebappClassLoaderBase继承了URLClassLoader,并覆写了loadClass()方法,相当于实现了自己的类加载器

    • loadClass()方法显示,Tomcat类加载体系打破了双亲委派模型,其加载过程为

      • 从缓存中查询,如果已加载过,则直接返回缓存中的class

      • 通过系统类加载器加载(Bootstrap ClassLoader),已避免应用程序中覆写JavaSE基础类

      • 判断是否设置为双亲委派,或者classname为特定路径下的,则委托给父类加载器夹杂

      • 通过应用程序类加载路径加载,加载通过WebResourceRoot进行

      • 当双亲委派标记为假时,最终委托给父类加载器加载

    其中必须通过父类加载加载的类名称通过filter()方法判断:

     protected boolean filter(String name, boolean isClassName) {        if (name == null)            return false;        char ch;        if (name.startsWith("javax")) {                        if (name.length() == 5) {                return false;            }            ch = name.charAt(5);            if (isClassName && ch == '.') {                                if (name.startsWith("servlet.jsp.jstl.", 6)) {                    return false;                }                if (name.startsWith("el.", 6) ||                    name.startsWith("servlet.", 6) ||                    name.startsWith("websocket.", 6) ||                    name.startsWith("security.auth.message.", 6)) {                    return true;                }            } else if (!isClassName && ch == '/') {                                if (name.startsWith("servlet/jsp/jstl/", 6)) {                    return false;                }                if (name.startsWith("el/", 6) ||                    name.startsWith("servlet/", 6) ||                    name.startsWith("webSocket/", 6) ||                    name.startsWith("security/auth/message/", 6)) {                    return true;                }            }        } else if (name.startsWith("org")) {                        if (name.length() == 3) {                return false;            }            ch = name.charAt(3);            if (isClassName && ch == '.') {                                if (name.startsWith("apache.", 4)) {                                        if (name.startsWith("tomcat.jdbc.", 11)) {                        return false;                    }                    if (name.startsWith("el.", 11) ||                        name.startsWith("catalina.", 11) ||                        name.startsWith("jasper.", 11) ||                        name.startsWith("juli.", 11) ||                        name.startsWith("tomcat.", 11) ||                        name.startsWith("naming.", 11) ||                        name.startsWith("coyote.", 11)) {                        return true;                    }                }            } else if (!isClassName && ch == '/') {                                if (name.startsWith("apache/", 4)) {                                        if (name.startsWith("tomcat/jdbc/", 11)) {                        return false;                    }                    if (name.startsWith("el/", 11) ||                        name.startsWith("catalina/", 11) ||                        name.startsWith("jasper/", 11) ||                        name.startsWith("juli/", 11) ||                        name.startsWith("tomcat/", 11) ||                        name.startsWith("naming/", 11) ||                        name.startsWith("coyote/", 11)) {                        return true;                    }                }            }        }        return false;    }
    org.apache.catalina.webresources.StandardRoot
    public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {    ...省略其他代码...    private final List<WebResourceSet> preResources = new ArrayList<>();    private WebResourceSet main;    private final List<WebResourceSet> classResources = new ArrayList<>();    private final List<WebResourceSet> jarResources = new ArrayList<>(); //对应WEB-INF/lib    private final List<WebResourceSet> postResources = new ArrayList<>();    private final List<WebResourceSet> mainResources = new ArrayList<>(); //对应应用webapp路径    private final List<List<WebResourceSet>> allResources = new ArrayList<>();    {        allResources.add(preResources);        allResources.add(mainResources);        allResources.add(classResources);        allResources.add(jarResources);        allResources.add(postResources);    }    ...省略其他代码...}

    StandardRoot 在容器创建时进行初始化,其实现了接口WebResourceRoot ,用于加载Webapp类库。
    以上代码可以看出,在Tomcat中Webapp类加载顺序为:

    preResources->mainResources->classResources->jarResources->postResources

    其中主要用到的资源路径为:

    • mainResources:对应应用目录下WEB-INF/classes

    • classResources:对应应用目录下WEB-INF/lib

    所以在开发时,可以在src目录下覆盖lib包中的类,因为WEB-INF/classes会有限WEB-INF/lib进行加载。通常我们可以将依赖的第三方类库的源代码复制到src目录经,通过Tomcat运行进行debug,这对于排查第三方类库的问题很有帮助。

    org.apache.jasper.JspCompilationContext
    public ClassLoader getJspLoader() {        if( jspLoader == null ) {            jspLoader = new JasperLoader                    (new URL[] {baseUrl},                            getClassLoader(),                            rctxt.getPermissionCollection());        }        return jspLoader;    }public void clearJspLoader() {        jspLoader = null;    }
    • 在Tomcat的conf/web.xml中,指定了处理JSP的Servlet:org.apache.jasper.servlet.JspServlet,在处理JSP页面的请求时,Tomcat会检测相应的JSP文件是否发生的修改,如果修改则会清理JSP的编译文件,然后先生成java文件,在编译为class文件

    • jspLoader为加载JSP的转化成的Servlet的类加载器,在Tomcat检测到jsp文件发生变化时都会重新生成

    • jspLoader的父类加载器为webapp的classloader,父类加载器的初始在org.apache.jasper.compiler.JspRuntimeContext的构造方法中,详见如下代码

      public JspRuntimeContext(ServletContext context, Options options) {      ..省略其他代码      // Get the parent class loader      ClassLoader loader = Thread.currentThread().getContextClassLoader();      if (loader == null) {          loader = this.getClass().getClassLoader();      }      parentClassLoader =  loader;        ...省略其他代码...  }

    以上是“Tomcat9中类加载体系是怎么样的”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: Tomcat9中类加载体系是怎么样的

本文链接: https://lsjlt.com/news/230241.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Tomcat9中类加载体系是怎么样的
    小编给大家分享一下Tomcat9中类加载体系是怎么样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.概要Tomcat作为Web容器,支持多个应用程序的部署运行...
    99+
    2023-06-02
  • JVM类加载过程是怎样的
    小编给大家分享一下JVM类加载过程是怎样的,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!类加载过程Class 文件需要加载到虚拟机中之后才能运行和使用,那么虚拟机...
    99+
    2023-06-02
  • Java类的加载过程是怎样的
    这篇文章主要讲解了“Java类的加载过程是怎样的”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java类的加载过程是怎样的”吧!  一、加载  在加载阶段,虚拟机主要完成三件事:  1.通过...
    99+
    2023-06-02
  • oracle中体系结构是怎么样的
    这篇文章主要为大家展示了“oracle中体系结构是怎么样的”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“oracle中体系结构是怎么样的”这篇文章吧。oracl...
    99+
    2024-04-02
  • java 类是怎么加载的
    类加载,就是读取 .class 文件到内存中,放入方法区,并在堆区创建一个 java.lang.Class 类型的对象,这个对象封装了类在方法区内的数据结构。类加载发生在何时?一般在类被使用时发生,包括下面 6 种情况:– 使用 new 关...
    99+
    2019-06-01
    java教程 java 加载
  • oracle体系结构是怎么样的
    本篇内容介绍了“oracle体系结构是怎么样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!oracle ...
    99+
    2024-04-02
  • PG体系结构是怎样的
    本篇内容介绍了“PG体系结构是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!##pg 结构...
    99+
    2024-04-02
  • Java架构体系是怎样的
    这篇文章主要讲解了“Java架构体系是怎样的”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java架构体系是怎样的”吧!一 。性能优化深入内核,直击故障,拒绝蒙圈二。应用框架 源码解读站在巨...
    99+
    2023-06-02
  • MySQL架构体系是怎样的
    本篇内容主要讲解“MySQL架构体系是怎样的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL架构体系是怎样的”吧!一 : 数据库和数据库实例 在MySQL的学习研究中,存在两个...
    99+
    2023-06-05
  • Java中的类加载是什么意思
    本篇内容主要讲解“Java中的类加载是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中的类加载是什么意思”吧!目录类加载<1>.父子类执行的顺序<2>类加...
    99+
    2023-06-20
  • Java中类的加载顺序是什么
    本篇文章为大家展示了Java中类的加载顺序是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。具体如下:public class Parent { public static int a = pa...
    99+
    2023-05-31
    java 中类 ava
  • Kafka的体系架构是怎样的
    这期内容当中小编将会给大家带来有关Kafka的体系架构是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、什么是Kafka?数据工程中最具挑战性的部分之一是如何从不同点收集和传输大量数据到分布式系统...
    99+
    2023-06-02
  • ubuntu加载中文字体的方法是什么
    这篇“ubuntu加载中文字体的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“ubuntu加载中文字体的方法是什么...
    99+
    2023-07-04
  • vba怎么判断窗体是否加载
    在VBA中,可以使用下列代码来判断窗体是否加载:```vbaFunction IsFormLoaded(formName As St...
    99+
    2023-09-15
    vba
  • Java类加载器包括几种以及它们之间的关系是怎样的
    这篇文章主要讲解了“ Java类加载器包括几种以及它们之间的关系是怎样的”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ Java类加载器包括几种以及它们之间的关系是怎样的”吧! 为什么说Ja...
    99+
    2023-06-02
  • spring怎么加载类的
    在Spring中,可以通过以下几种方式来加载类: 使用XML配置文件加载类:可以通过编写XML配置文件来定义Spring容器,并...
    99+
    2023-10-25
    spring
  • Final关键字对JVM类加载器的影响是怎样的
    这篇文章将为大家详细讲解有关Final关键字对JVM类加载器的影响是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。当一个类中有声明为static final的变量,这样的变量对类的加载...
    99+
    2023-06-17
  • 什么是JVM的类加载器
    本篇内容主要讲解“什么是JVM的类加载器”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是JVM的类加载器”吧!1. 什么是JVM既然是学习关于JVM的相关理论知识,我们当然得知道什么是JVM...
    99+
    2023-06-16
  • jvm中怎么加载一个类
    这篇文章将为大家详细讲解有关jvm中怎么加载一个类,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。  第一步:加载,双亲委派:启动类加载器(jre/lib),系统扩展类加载器(ext/lib)...
    99+
    2023-06-02
  • Kubernetes中Nginx配置热加载的过程是怎样的
    Kubernetes中Nginx配置热加载的过程是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。前言Nginx本身是支持热更新的,通过nginx -s r...
    99+
    2023-06-26
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作