JAVA 双亲委派之一

news2024/11/18 17:36:03
JAVA 双亲委派之一

JVM类加载流程

java语言系统内置了众多类加载器,从一定程度上讲,只存在两种不同的类加载器:一种是启动类加载器,此类加载由C++实现,是JVM的一部分;另一种就是所有其他的类加载器,这些类加载器均由java实现,且全部继承自java.lang.ClassLoader

  1. Bootstrap ClassLoader 启动类加载器,最顶层的加载类,由C++实现,负责加载%JAVA_HOME%/lib目录中或-Xbootclasspath中参数指定的路径中的,并且是虚拟机识别的(按名称)类库
  2. Extention ClassLoader 扩展类加载器,由启动类加载器加载,实现为sun.misc.Launcher$ExtClassLoader,负责加载目录%JRE_HOME%/lib/ext目录中或-Djava.ext.dirs中参数指定的路径中的jar包和class文件
  3. Application ClassLoader 应用类加载器,也称为系统类加载器(System ClassLoader,可由java.lang.ClassLoader.getSystemClassLoader()获取),实现为sun.misc.Launcher$AppClassLoader,由启动类加载器加载,负责加载当前应用classpath下的所有类
双亲委派模型

java语言系统有众多类加载器,包括用户自定义类加载器,各加载器之间的加载顺序如何?首先从JVM入口应用sun.misc.Launcher聊起

Launcher

public Launcher() {
    ExtClassLoader localExtClassLoader;
    try {
        // 加载扩展类加载器
        localExtClassLoader = ExtClassLoader.getExtClassLoader();
    } catch (IOException localIOException1) {
        throw new InternalError("Could not create extension class loader", localIOException1);
    }
    try {
        // 加载应用类加载器
        this.loader = AppClassLoader.getAppClassLoader(localExtClassLoader);
    } catch (IOException localIOException2) {
        throw new InternalError("Could not create application class loader", localIOException2);
    }
    // 设置AppClassLoader为线程上下文类加载器
    Thread.currentThread().setContextClassLoader(this.loader);
    // ...
    
    static class ExtClassLoader extends java.net.URLClassLoader
    static class AppClassLoader extends java.net.URLClassLoader
}

Launcher初始化了ExtClassLoader和AppClassLoader,并将AppClassLoader设置为线程上下文类加载器,同时,初始化AppClassLoader时传入了ExtClassLoader实例,WHY? 这里要写一个大大的问号ExtClassLoader和AppClassLoader都继承自URLClassLoader,而最终的父类则为ClassLoader。
在这里插入图片描述

查看源码可以得知,初始化AppClassLoader时传入的ExtClassLoader实例最终设置为了AppClassLoader(ClassLoader)的parent属性,parent属性的作用是什么?

父类加载器
每个类都对应一个加载它的类加载器,我们可以通过程序来验证

public class ClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader());
        System.out.println("DNSNameService's ClassLoader is " + DNSNameService.class.getClassLoader());
        System.out.println("String's ClassLoader is " + String.class.getClassLoader());
    }
}

输出为

ClassLodarDemo's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
DNSNameService's ClassLoader is sun.misc.Launcher$ExtClassLoader@63c12fb0
String's ClassLoader is null

PathClassLoader为我们自己创建的类,其类加载器为AppClassLoader
DNSNameService为%JRE_HOME%/lib/ext目录下的类,其类加载器为ExtClassLoader
String存在于rt.jar中,但其类加载器为null,这里是应为rt.jar由Bootstrap ClassLoader加载,而Bootstrap ClassLoader是由C++编写,属于JVM的一部分

每个类加载器,都有一个父类加载器(parent),同样通过程序验证

public class ClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println("PathClassLoader's ClassLoader is " + PathClassLoader.class.getClassLoader());
        System.out.println("PathClassLoader's ClassLoader is " + PathClassLoader.class.getClassLoader().getParent());
        System.out.println("PathClassLoader's ClassLoader is " + PathClassLoader.class.getClassLoader().getParent().getParent());
    }
}

输出:

PathClassLoader's ClassLoader is sun.misc.Launcher$AppClassLoader@18b4aac2
PathClassLoader's ClassLoader is sun.misc.Launcher$ExtClassLoader@63c12fb0
PathClassLoader's ClassLoader is null

AppClassLoader的父类加载器为ExtClassLoader
ExtClassLoader的父类加载器为null,
源码解析

public ExtClassLoader(File[] var1) throws IOException {
        super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
        SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }
    
 // 父类URLClassLoader方法
public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory)

super方法中传参就null

null并不代表ExtClassLoader没有父类加载器,而是Bootstrap ClassLoader

双亲委派

在这里插入图片描述

类加载器在加载类或者其他资源时,使用的是如上图所示的双亲委派模型,这种模型要求除了顶层的BootStrap ClassLoader外,其余的类加载器都应当有自己的父类加载器(父类加载器不是父类继承),如果一个类加载器收到了类加载请求,首先会把这个请求委派给父类加载器加载,只有父类加载器无法完成类加载请求时,子类加载器才会尝试自己去加载。

要理解双亲委派,可以查看ClassLoader.loadClass方法

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 检查是否已经加载过
        Class<?> c = findLoadedClass(name);
        if (c == null) { // 没有被加载过
            long t0 = System.nanoTime();
            // 首先委派给父类加载器加载
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // 如果父类加载器无法加载,才尝试加载
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}
破坏双亲委派模型

双亲委派模型并不是一个强制性的约束模型,在java的世界中大部分的类加载器都遵循这个模型,但也有例外,一个典型的例子便是JNDI服务。

JNDI存放于rt.jar,由启动类加载器加载,JNDI的目的是对资源进行集中管理和查找,它需要调用由各厂商实现并部署在应用程序ClassPath下的JNDI接口实现的代码,如此一来,在双亲委派模型下,启动类加载器根本无法加载这些代码

针对此问题,java设计团队引入了线程上下文类加载器(Thread Context ClassLoader),这个类加载器可以通过Thread类的setContextClassLoader进行设置,默认继承父线程类加载器
通过线程上下文类加载器,JNDI服务通过此类加载器,由父类加载器请求子类加载器完成类加载动作

破坏双亲委派模型的例子还有很多,如tomcat服务、osgi、jigsaw等等,是否破坏双亲委派模型并没有对与错,只是不同场景下的具体应用而已

自定义ClassLoader

不论是AppClassLoader还是ExtClassLoader还是启动类加载器,其加载类的路径都是固定的,如果我们需要加载外部类或者资源,如某路径下或网络上,这样便需要自定义类加载器
自定义类加载器,只需要继承ClassLoader类,复写findClass方法,在findClass方法中调用defineClass方法即可

一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader
如果需要制定一个ClassLoader的父类加载器为启动类加载器,只需要将其parent指定为null即可

首先,编写一个测试用的类文件

package com.lvyuanj.standard.test;

public class MyDefinedClass {
    public void say() {
        System.out.println("I'm MyDefinedClass by " + this.getClass().getClassLoader());
    }
}

将其编译,放入D:\TestXf\classloader目录下接下来,

编写MyDefinedClassLoader

package com.lvyuanj.standard.test;

import java.net.URL;
import java.net.URLClassLoader;

public class MyDefinedClassLoader extends URLClassLoader {

    public MyDefinedClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public MyDefinedClassLoader(URL[] urls) {
        super(urls);
    }
}

这里直接继承URLClassLoader类,该类findClass的实现如下

protected Class<?> findClass(final String name) throws ClassNotFoundException {
    final Class<?> result;
    try {
        result = AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class<?>>() {
                public Class<?> run() throws ClassNotFoundException {
                    // 类文件全路径
                    String path = name.replace('.', '/').concat(".class");
                    // 指定资源目录下查找
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            // 调用defineClass生成类
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        return null;
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
        throw new ClassNotFoundException(name);
    }
    return result;
}

编写测试程序

package com.lvyuanj.standard.test;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;

public class MyDefinedClassLoaderDemo {
    public static void main(String[] args) throws Exception {
        URL path = new File("D:\\TestXf\\classloader").toURI().toURL();
        URL[] urls = new URL[1];
        urls[0] = path;

        MyDefinedClassLoader myDefinedClassLoaderA = new MyDefinedClassLoader(urls);
        Class<?> aClass = myDefinedClassLoaderA.loadClass("com.lvyuanj.standard.test.MyDefinedClass");
        Method sayA = aClass.getMethod("say");
        Object instanceA = aClass.newInstance();
        sayA.invoke(instanceA);
        System.out.println(aClass.getClassLoader().toString());
        System.out.println("aClass@"+aClass.hashCode());

        System.out.println("====================");

        MyDefinedClassLoader myDefinedClassLoaderB = new MyDefinedClassLoader(urls, myDefinedClassLoaderA);
        Class<?> bClass = myDefinedClassLoaderB.loadClass("com.lvyuanj.standard.test.MyDefinedClass");
        Method sayB = bClass.getMethod("say");
        Object instanceB = bClass.newInstance();
        sayB.invoke(instanceB);
        System.out.println(myDefinedClassLoaderB);
        System.out.println("bClass@"+bClass.hashCode());

        System.out.println("====================");

        MyDefinedClassLoader myDefinedClassLoaderC = new MyDefinedClassLoader(urls);
        Class<?> cClass = myDefinedClassLoaderC.loadClass("com.lvyuanj.standard.test.MyDefinedClass");
        Method sayC = cClass.getMethod("say");
        Object instanceC = cClass.newInstance();
        sayC.invoke(instanceC);
        System.out.println(myDefinedClassLoaderC);
        System.out.println("cClass@"+cClass.hashCode());

        System.out.println("====================");
        System.out.println("aClass == bClass :"+ (aClass == bClass));
        System.out.println("cClass == bClass :"+ (cClass == bClass));
    }
}

输出为

I'm MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader@619a5dff
com.lvyuanj.standard.test.MyDefinedClassLoader@619a5dff
aClass@2117255219
====================
I'm MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader@619a5dff
com.lvyuanj.standard.test.MyDefinedClassLoader@7ab2bfe1
bClass@2117255219
====================
I'm MyDefinedClass by com.lvyuanj.standard.test.MyDefinedClassLoader@497470ed
com.lvyuanj.standard.test.MyDefinedClassLoader@497470ed
cClass@186276003
====================
aClass == bClass :true
cClass == bClass :false

这里我们定义了3个ClassLoader,类搜索路径均为D:\TestXf\classloader,其中myDefinedClassLoaderB的父类加载器为myDefinedClassLoaderA
aClass bClass cClass 分别由 myDefinedClassLoaderA myDefinedClassLoaderB myDefinedClassLoaderC 加载
instanceA instanceB instanceC 分别由 aClass bClass cClass 创建

从输出可以看出,instanceA及instanceB所对应的class均由myDefinedClassLoaderA加载
虽然myDefinedClassLoaderA与myDefinedClassLoaderB为两个不同的类加载器,但由于myDefinedClassLoaderB的父类加载器为myDefinedClassLoaderA,从输出结果可以看出,aClass与bClass完全相同(包括内存地址),这也说明aClass与bClass均由同一类加载器加载
而instanceC所对应的class,cClass却是由myDefinedClassLoaderC加载

这个例子,能更好的帮助理解双亲委派模型

**注意:如果使用IDEA编辑器的话,一定不要创建MyDefinedClass 这个类,否则sun.misc.Launcher A p p C l a s s L o a d e r 默认的加载器。自定义 C l a s s L o a d e r 加载器,继承 C l a s s L o a d e r ,重写 f i n d C l a s s 指定路径,加载的类一定不要放到本工程目录对应的包下面,否则 f i n d C l a s s 都不会执行,如果 l o a d C l a s s ( " 包名 + 类名 " ),此类会被 s u n . m i s c . L a u n c h e r AppClassLoader默认的加载器。 自定义ClassLoader加载器,继承ClassLoader,重写findClass 指定路径,加载的类一定不要放到本工程目录对应的包下面,否则findClass都不会执行,如果loadClass("包名+类名"),此类会被sun.misc.Launcher AppClassLoader默认的加载器。自定义ClassLoader加载器,继承ClassLoader,重写findClass指定路径,加载的类一定不要放到本工程目录对应的包下面,否则findClass都不会执行,如果loadClass"包名+类名"),此类会被sun.misc.LauncherAppClassLoader默认加载器加载,是可以直接找到此类,导致自定义的findClass不执行。 使用loadClass(“类名”), 找不到才会再执行findClass方法。正好次方法验证了类加载向父类查找建议:需要加载的类,使用IDEA 写好之后,然后把文件copy到其它目录,然后删除本工程下面的的文件
**

类的唯一性

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间如上例,虽然classA(classB)与classC的类路径相同,但由于被不同的类加载器加载,其却属于两个不同的类名称空间

手动打包jar
package com.lvyuanj.standard.model;

public class StudentA {

    private String name;

    private Integer age;

    private String address;

    public StudentA() {
        System.out.println("--------------->StudentA constructor-----------");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void print(){
        System.out.println("---------------------> excute print method");
    }
}
package com.lvyuanj.standard.model;

public class StuMain {

    public static void main(String[] args) {
        StudentA studentA = new StudentA();
        studentA.print();
    }
}
手动编码
  1. 执行: javac -encoding utf-8 -d classes com/lvyuanj/standard/model/*.java
打jar包
  1. 创建MANIFEST.MF文件
Manifest-Version: 1.0
Main-Class: com.lvyuanj.standard.model.StuMain

请务必在文件的最后一行,至少加一个回车换行
Manifest-Version 版本号默认为1.0
Main-Class 入口的主类,也就是main方法所在的类
Class-Path 引用外部类的路径

2.打包
在该文件夹下执行:jar -cvfm stu.jar MANIFEST.MF com
jar命令是jdk自带的,可在%JAVA_HOME%\bin中找到
c 创建新档案
v 在标准输出中生成详细输出,有输出信息
f 指定档案文件名
m 包含指定清单文件中的清单信息,也就是指定我们的MANIFEST.MF 如果没有这个参数,会生成一个默认的MANIFEST.MF文件
stu.jar 指定生成jar包的名称

MANIFEST.MF com commons-lang3-3.4.jar 要打包的文件
成功之后会在当前文件夹下生成一个stu.jar
在这里插入图片描述

  1. 运行:
    执行命令: java -jar stu.jar
    输出结果:
--------------->StudentA constructor-----------
---------------------> excute print method
验证ExtClassLoader加载

把以上打包好的stu.jar 包放到 jre/lib/ext 目录下面

public static void main(String args[]) {
    Class<?> classz = Class.forName("com.lvyuanj.standard.model.StudentA");
    System.out.println("classload :"+classz.getClassLoader());
}

结果:

classload:sun.misc.Launcher$ExtClassLoader@63c12fb0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1662287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《软件方法(下)》8.3.2.2 警惕拼凑泛化(202405更新)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 8.3 建模步骤C-2 识别类的关系 8.3.2 识别泛化关系 8.3.2.1 识别泛化的思路 &#xff08;3&#xff09;自上而下&#xff08;从一般到特殊&#xff09; 如图8-92所示&#xff0c;这…

西湖大学英语听力考试音频无线发射系统-英语听力发射系统浅析

西湖大学英语听力考试音频无线发射系统-英语听力发射系统浅析 由北京海特伟业科技任洪卓发布于2024年5月10日 西湖大学&#xff0c;这所矗立于时代前沿的高等学府&#xff0c;始终秉持着创新精神和追求卓越的坚定信念&#xff0c;不断致力于教学质量的提升与学术研究的深化。其…

verilog中含有无关项的序列检测

编写一个序列检测模块&#xff0c;检测输入信号a是否满足011XXX110序列&#xff08;长度为9位数据&#xff0c;前三位是011&#xff0c;后三位是110&#xff0c;中间三位不做要求&#xff09;&#xff0c;当信号满足该序列&#xff0c;给出指示信号match。 程序的接口信号图如…

能聚合各站热点的DailyHot

什么是 DailyHot ? 今日热榜&#xff08;DailyHot&#xff09;是一个获取各大热门网站热门头条的聚合网站&#xff0c;能追踪全网热点、实现简单高效阅读。项目分为前、后端&#xff0c;其中后端提供了一个聚合热门数据的 API 接口。 &#x1f6a9; 后端 API 特性 极快响应&a…

Gradient发布支持100万token的Lllama3,上下文长度从8K扩展到1048K

前言 近日Gradient公司在Crusoe Energy公司的算力支持下&#xff0c;开发了一款基于Llama-3的大型语言模型。这款新模型在原Llama-3 8B的基础上&#xff0c;将上下文长度从8000 token大幅扩展到超过104万token。 这一创新性突破&#xff0c;展现了当前SOTA大语言模型在长上下…

kali安装及替换源

一、安装及简单配置 1.安装&#xff1a;地址就不贴了&#xff0c;自己打一下就好 2.虚拟机中打开kali 3.替换包源 (1)使用指令打开/etc/apt/sources.list mousepad /etc/apt/sources.list (2)将内容替换成阿里云源 deb http://mirrors.aliyun.com/kali kali-rolling main n…

DeepMind的首席执行官Demis Hassabis表示,未来一到两年内,能够独立完成复杂任务的AI代理将成为现实

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

JDK1.8的安装及环境变量的配置(超详细图文)

0.JDK 简介 JDK&#xff0c;全称Java Development Kit&#xff0c;是Java语言的软件开发工具包&#xff0c;主要用于Java程序的开发。 1.首先下载JDK安装包 下载安装jdk1.8或jdk17(可以去官方下载) 这里提供一份网盘下载地址&#xff0c;大家按需自取&#xff1a;点击这里下…

【JavaScript】内置对象 - 数组对象 ③ ( 数组反转 - reverse 方法 | 数组排序 - sort 方法 | 自定义数组排序规则 )

文章目录 一、数组排序1、翻转数组元素 - reverse()2、数组元素排序 - sort() 默认从小到大排序3、数组元素排序 - sort() 自定义排序规则4、数组元素排序 - sort() 自定义降序排序简化写法 Array 数组对象参考文档 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript…

Vue2 组件通信方式

props/emit props 作用&#xff1a;父组件通过 props 向子组件传递数据parent.vue <template><div><Son :msg"msg" :pfn"pFn"></Son></div> </template><script> import Son from ./son export default {name: …

Web前端开发 小实训(三) 商品秒杀小练习

学生能够在本次实训中完成商品秒杀页面的基本逻辑 任务要求 能够实现某一个商品的秒杀&#xff0c;在倒计时结束后不再进行秒杀。 操作步骤 1、打开预设好的页面 <html><head><meta charset"utf-8"><title>秒杀</title><link …

DataLab-数据分析的Ai辅助工具

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;DataLab是一个由DataCamp提供的强大在线数据分析平台&#xff0c;它通过AI技术简化了数据处理流程&#xff0c;使得用户无需编程或数据分析的高级技能即可快速获取数据洞察。它支持多种数据源&#xff0c;包…

AJAX概述和基本使用

01 【AJAX概述和基本使用】 1.AJAX简介 AJAX 全称为Asynchronous JavaScript And XML&#xff0c;就是异步的JS 和XML 通过AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据 AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准…

RF Plasma gernerator-系列(RF-5KW Adtec)说明书TX06-9001-00

RF Plasma gernerator-系列(RF-5KW Adtec)说明书TX06-9001-00

C语言 [力扣]详解环形链表和环形链表II

各位友友们&#xff0c;好久不见呀&#xff01;又到了我们相遇的时候&#xff0c;每次相遇都是一种缘分。但我更加希望我的文章可以帮助到大家。下面就来具体看看今天所要讲的题目。 文章目录 1.环形链表2.环形链表II 1.环形链表 题目描述:https://leetcode.cn/problems/link…

亲测有效!关键点检测——COCO格式转YOLO格式代码!!!

话不多收&#xff0c;直接上代码&#xff0c;这个我也是找了好久的&#xff0c;分享不易&#xff0c;给个鼓励&#xff01;&#xff08;记得点赞收藏&#xff09; 大家可以直接使用此代码转换你自己的数据集&#xff0c;路径换成你自己的就行了&#xff0c;注意路径格式&#x…

【经验总结】超参数对模型训练的影响

1. 学习率对模型训练的影响 python 代码&#xff1a; import numpy as np from keras.models import Sequential from keras.layers import Dense from keras.optimizers import Adam import matplotlib.pyplot as plt# 生成随机数据集 np.random.seed(0) X_train np.random…

2024年最新趋势跨境电商平台开发需了解的新技术

随着数字化技术的不断演进和全球市场的日益融合&#xff0c;跨境电商平台开发将面临前所未有的挑战和机遇。为了更好地适应并引领这一发展&#xff0c;开发者需要密切关注2024年最新的技术趋势&#xff0c;以确保他们的平台能够在竞争激烈的市场中脱颖而出。本文将对跨境电商平…

TCP UDP

传输层 端口号 tcp udp 网络层 IP地址 IP TCP&#xff0c;UDP 1&#xff0c;TCP是面向链接的协议&#xff0c;而UDP是无连接的协议; 2&#xff0c;TCP协议的传输是可靠的&#xff0c;而UDP协议的传输“尽力而为” 3&#xff0c;TCP可以实现流控&#xff0c;但UDP不行;…

通用型产品发布解决方案(后端环境搭建)

文章目录 后端renren脚手架配置1.解压后放到项目目录下2.新建商品模块1.创建一个新模块 sunliving-commodity2.删除两个不必要的文件3.pom.xml 引入依赖 3.maven进行聚合管理1.将刚才配置的pom.xml文件复制到父项目下并进行修改2.手动将这个pom.xml加入项目&#xff08;如果右下…