Docker部署前后端分离项目——多项目共享环境部署

目录

一、简介

二、文件目录结构

三、前端部署流程(多nginx)

3.1 前端打包

3.2 编写部署文件——项目1(consult-system)

3.3 编写部署文件——项目2(person-system)

3.4 前端部署至linux服务器

3.5 访问方式

四、前端部署流程二(同nginx)

4.1 前端打包

4.2 编写部署文件

4.3 部署上线

五、后端部署流程(共用mysql和redis)

5.1 创建共享网络

5.2 后端打包

5.3 编写部署文件——项目1(consult-system)

5.4 编写部署文件——项目2(person-system)

5.5 后端部署至linux服务器

六、开放端口

七、数据库加载


一、简介

        多项目共享环境部署,指的是在同一台服务器或同一个 Docker 主机上,通过 Docker Compose 等工具,将多个前后端项目的公共基础服务(如数据库、缓存、消息队列等)统一部署为共享服务,各项目通过自定义网络等方式共同使用这些服务,从而实现资源复用、简化运维、提升协作效率的一种部署方式。

        这里有2个项目(即项目1和项目2),将项目部署在linux上,并且项目之间共用redis和mysql

二、文件目录结构

文件目录结构

文件解释

# 多项目的共同环境
shared-services.yml 
# 后端docker编排
consult-compose.yml和person-compose.yml
# 后端jar包
consultingPlatform......jar和personnel.....jar
# 前端build文件
/consult-system/frontend/dist和/person-system/frontend/dist
# 前端docker编排
docker-compose.yml
# nginx配置
nginx.conf

三、前端部署流程(多nginx)

3.1 前端打包

前端利用node命令来打包项目,得到dist文件夹

# 前端通过构建命令,打包项目
npm run build
npm run build:prod # 生产环境
npm run build:dev  # 开发环境

3.2 编写部署文件——项目1(consult-system)

dev-ops/consult-system/frontend/docker-compose.yml文件

# docker-compose -f docker-compose.yml up -d
version: "3"services:consult-nginx:image: nginx:alpineports:- 81:81 # 宿主机端口:容器端口volumes:- ./dist:/usr/share/nginx/html- ./nginx.conf:/etc/nginx/nginx.confprivileged: true #这个必须,解次nginx的文件调用的权限问题

dev-ops/consult-system/frontend/nginx.conf文件

# 添加必要的上下文块
http {# 包含默认的 MIME 类型include       /etc/nginx/mime.types;default_type  application/octet-stream;server {# 端口,需要与docker-compose一致listen 81;server_name localhost;# 静态文件根目录root /usr/share/nginx/html;index index.html;# 开启 gzip 压缩gzip on;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;# 处理前端路由(如 Vue/React 的 history 模式)location / {try_files $uri $uri/ /index.html;}# 禁止访问 .env 等敏感文件location ~ /\.(?!well-known).* {deny all;}
}
}# 添加 events 块(可选但推荐)
events {worker_connections 1024;
}

3.3 编写部署文件——项目2(person-system)

dev-ops/person-system/frontend/docker-compose.yml文件

# docker-compose -f docker-compose.yml up -d
version: "3"services:person-nginx:image: nginx:alpineports:- 82:82 # 宿主机端口:容器端口volumes:- ./dist:/usr/share/nginx/html- ./nginx.conf:/etc/nginx/nginx.confprivileged: true #这个必须,解次nginx的文件调用的权限问题

dev-ops/person-system/frontend/nginx.conf文件

# 添加必要的上下文块
http {# 包含默认的 MIME 类型include       /etc/nginx/mime.types;default_type  application/octet-stream;server {# 端口,需要与docker-compose一致listen 82;server_name localhost;# 静态文件根目录root /usr/share/nginx/html;index index.html;# 开启 gzip 压缩gzip on;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;# 处理前端路由(如 Vue/React 的 history 模式)location / {try_files $uri $uri/ /index.html;}# 禁止访问 .env 等敏感文件location ~ /\.(?!well-known).* {deny all;}
}
}# 添加 events 块(可选但推荐)
events {worker_connections 1024;
}

3.4 前端部署至linux服务器

step1:通过xftp将dev-ops整个文件夹上传至linux根目录

step2:对项目1(consult-system)部署

cd /dev-ops/consult-system/frontend
docker-compose -f docker-compose.yml up -d
# 查看是否启动
docker psCONTAINER ID   IMAGE                            COMMAND                  CREATED          STATUS                    PORTS                                        NAMES
...
e6753c3329b0   nginx:alpine                     "/docker-entrypoint.…"   28 minutes ago   Up 28 minutes             80/tcp, 0.0.0.0:81->81/tcp                   frontend-consult-nginx-1
...

step3:对项目2(person-system)部署

cd /dev-ops/person-system/frontend
docker-compose -f docker-compose.yml up -d
# 查看是否启动
docker psCONTAINER ID   IMAGE                            COMMAND                  CREATED          STATUS                    PORTS                                        NAMES
...
e6753c3329b0   nginx:alpine                     "/docker-entrypoint.…"   28 minutes ago   Up 28 minutes             80/tcp, 0.0.0.0:82->82/tcp                   frontend-person-nginx-1
...

3.5 访问方式

通过ip+端口的形式进行访问

项目1:http://xxx.xxx.xxx.xxx:81

项目2:http://xxx.xxx.xxx.xxx:82

四、前端部署流程二(同nginx)

4.1 前端打包

项目配置静态资源路径——(后续与nginx配合使用

  • vue项目
// 项目一
// vue.config.js
module.exports = {publicPath: process.env.NODE_ENV === 'production' ? '/consult/' // 生产环境路径: '/' // 开发环境路径
}// 项目二
// vue.config.js
module.exports = {publicPath: process.env.NODE_ENV === 'production' ? '/person/' // 生产环境路径: '/' // 开发环境路径
}
  • react项目
// 项目一
// package.json
{"homepage": "/consult/", // 生产环境路径
}// 项目二
// package.json
{"homepage": "/person/", // 生产环境路径
}
  • umi(react)项目
// .umirc.ts
export default defineConfig({define: {"process.env.UMI_ENV": process.env.UMI_ENV,},// 静态资源路径,根据process.env.UMI_ENV判断publicPath: process.env.UMI_ENV === "prod" ? "/person/" : "/",
});
// package.json"scripts": {"build:prod": "cross-env UMI_ENV=prod umi build",},

 前端利用node命令来打包项目,得到dist文件夹

# 前端通过构建命令,打包项目
npm run build
npm run build:prod # 生产环境
npm run build:dev  # 开发环境

4.2 编写部署文件

docker-compose-common-nginx.yml

# docker-compose -f docker-compose-common-nginx.yml up -d
version: "3"services:consult-nginx:image: nginx:alpineports:- 80:80 # 宿主机端口:容器端口volumes:# 挂载consult-system- ./consult-system/frontend/dist:/usr/share/nginx/html/consult-system# 挂载person-system- ./person-system/frontend/dist:/usr/share/nginx/html/person-system- ./common-nginx.conf:/etc/nginx/nginx.confprivileged: true #这个必须,解次nginx的文件调用的权限问题

common-nginx.conf

events {worker_connections 1024;
}http {include       /etc/nginx/mime.types;default_type  application/octet-stream;# 全局开启 gzip 压缩gzip on;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;server {listen 80;server_name localhost;# 禁止访问敏感文件(全局生效)location ~ /\.(?!well-known).* {deny all;}# ======================# 配置 consult-system 应用# 访问路径:http://localhost/consult/# ======================location /consult/ {alias /usr/share/nginx/html/consult-system/;index index.html;try_files $uri $uri/ /consult/index.html;}# ======================# 配置 person-system 应用# 访问路径:http://localhost/person/# ======================location /person/ {# 指向 person-system 的静态文件目录alias /usr/share/nginx/html/person-system/;index index.html;# 处理前端路由 (history 模式)try_files $uri $uri/ /person/index.html;}# ======================# 可选:默认重定向# ======================location = / {# 可重定向到其中一个应用return 302 /consult/;# 或显示欢迎页面# root /usr/share/nginx/html;# index welcome.html;}location = /person {return 301 /person/;}location = /consult {return 301 /consult/;}}
}

4.3 部署上线

cd /dev-opsdocker-compose -f docker-compose-common-nginx.yml up -d# 查看,是否存在 nginx80端口容器
docker ps 

五、后端部署流程(共用mysql和redis)

5.1 创建共享网络

dev-ops/shared-services.yml

# docker-compose -f shared-services.yml up -d# -f:指定使用的 Docker Compose 配置文件(默认文件名是 docker-compose.yml)
# up:启动所有服务(包括构建镜像)
# -d:在后台运行容器
version: "3"networks:my-network:driver: bridge# 添加网络名称确保唯一性name: shared-networkservices:# 共享的 MySQL(两个项目共用)mysql:image: mysql:5.7# 添加容器名称确保可寻址container_name: shared-mysqlports:- 13306:3306environment:TZ: Asia/ShanghaiMYSQL_ROOT_PASSWORD: 123456networks:- my-networkhealthcheck:test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]interval: 5stimeout: 10sretries: 10start_period: 15s# 共享的 Redis(两个项目共用)redis:image: redis:6.2container_name: shared-redisrestart: alwaysprivileged: trueports:- 16379:6379networks:- my-networkvolumes:- ./redis/redis.conf:/usr/local/etc/redis/redis.confcommand: redis-server /usr/local/etc/redis/redis.confhealthcheck:test: ["CMD", "redis-cli", "ping"]interval: 10stimeout: 5sretries: 3

  通过xftp将dev-ops整个文件夹上传至linux根目录,并构建共享网络

cd /dev-ops/
docker-compose -f shared-services.yml up -d# 查看自定义网络,列表中有shared-network
docker network ls
# 查看网络信息,container下有mysql和redis
docker network inspect <网络名称/id>

5.2 后端打包

进入后端项目的pom.xml添加构建打包代码,然后在命令行执行 nvm clean package,将会在target文件夹下生成jar包,注意:记得修改主函数,也就是后端启动类

<build><!-- 1. 最终生成的JAR文件名 --><finalName>${project.artifactId}-${project.version}</finalName><!-- 2. 资源文件处理配置 --><resources><resource><directory>src/main/resources</directory><filtering>true</filtering> <!-- 启用资源过滤,替换占位符 --><includes><include>**/*.properties</include><include>**/*.xml</include><include>**/*.yml</include><include>**/*.yaml</include></includes></resource><!-- 包含静态资源 --><resource><directory>src/main/resources</directory><filtering>false</filtering><includes><include>static/**</include><include>templates/**</include><include>public/**</include></includes></resource></resources><!-- 3. 测试资源处理 --><testResources><testResource><directory>src/test/resources</directory><filtering>true</filtering></testResource></testResources><!-- 4. 插件配置 --><plugins><!-- 编译器插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version> <!-- 更新到最新版本 --><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding><compilerArgs><arg>-parameters</arg> <!-- 保留方法参数名,方便MyBatis使用 --></compilerArgs></configuration></plugin><!-- 跳过测试 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.1.2</version><configuration><skipTests>true</skipTests> <!-- 构建时跳过测试 --></configuration></plugin><!-- Spring Boot打包插件 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>xxx.xxx.xxx.xxx</mainClass><skip>false</skip> <!-- 确保执行打包 --><executable>true</executable> <!-- 生成可执行JAR --><layers><enabled>true</enabled> <!-- 启用分层打包,优化Docker镜像构建 --></layers></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal> <!-- 重新打包包含所有依赖 --></goals></execution></executions></plugin><!-- 资源过滤插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.3.1</version><configuration><delimiters><delimiter>@</delimiter> <!-- 使用@作为占位符分隔符,避免与Spring冲突 --></delimiters><useDefaultDelimiters>false</useDefaultDelimiters></configuration></plugin></plugins></build>

5.3 编写部署文件——项目1(consult-system)

dev-ops/consult-system/consult-compose.yml文件

# docker-compose -f consult-compose.yml up -d
# -f:指定使用的 Docker Compose 配置文件(默认文件名是 docker-compose.yml)
# up:启动所有服务(包括构建镜像)
# -d:在后台运行容器
version: "3"networks:shared-network: # 使用共享网络与共享服务在同一网络下,是shared-services.yml中定义的网络external: trueservices:consult-system-behind:image: consult-system-behind:latestbuild: . # 在同一级下寻找Dockerfile并构建,默认是寻找Dockerfileports: # 暴露端口- 8877:8877# 记得jdbc:mysql://{mysql容器名}:3306/person_system?environment:- TZ=PRC- SERVER_PORT=8877- APP_CONFIG_API_VERSION=v1- APP_CONFIG_CROSS_ORIGIN=*- SPRING_DATASOURCE_USERNAME=root- SPRING_DATASOURCE_PASSWORD=123456- SPRING_DATASOURCE_URL=jdbc:mysql://shared-mysql:3306/consult_platform?serverTimezone=UTC&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai- SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver- SPRING_HIKARI_POOL_NAME=Retail_HikariCP- REDIS_SDK_CONFIG_HOST=shared-redis- REDIS_SDK_CONFIG_PORT=6379# 使用共享网络networks:- shared-network

dev-ops/consult-system/Dockfile文件

FROM openjdk:8-jre-slim# 作者
MAINTAINER yeqc# 添加应用
ADD consultingPlatform-0.0.1-SNAPSHOT.jar app.jarENTRYPOINT ["java","-jar","/app.jar"]

5.4 编写部署文件——项目2(person-system)

dev-ops/person-system/person-compose.yml文件

# docker-compose -f person-compose.yml up -d
# -f:指定使用的 Docker Compose 配置文件(默认文件名是 docker-compose.yml)
# up:启动所有服务(包括构建镜像)
# -d:在后台运行容器
version: "3"networks:shared-network: # 使用共享网络与共享服务在同一网络下,是shared-services.yml中定义的网络external: trueservices:person-system-behind:image: person-system-behind:latestbuild: . # 在同一级下寻找Dockerfile并构建,默认是寻找Dockerfileports: # 暴露端口- 8082:8082# 记得jdbc:mysql://{mysql容器名}:3306/person_system?environment:- TZ=PRC- SERVER_PORT=8082- APP_CONFIG_API_VERSION=v1- APP_CONFIG_CROSS_ORIGIN=*- SPRING_DATASOURCE_USERNAME=root- SPRING_DATASOURCE_PASSWORD=123456- SPRING_DATASOURCE_URL=jdbc:mysql://shared-mysql:3306/person_system?serverTimezone=UTC&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai- SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver- SPRING_HIKARI_POOL_NAME=Retail_HikariCP- REDIS_SDK_CONFIG_HOST=shared-redis- REDIS_SDK_CONFIG_PORT=6379# 把服务加入共享网络networks:- shared-network

dev-ops/person-system/Dockerfile文件

FROM openjdk:8-jre-slim# 作者
MAINTAINER yeqc# 添加应用
ADD personnel-management-system-behind-0.0.1-SNAPSHOT.jar app.jarENTRYPOINT ["java","-jar","/app.jar"]

5.5 后端部署至linux服务器

 step1:通过xftp将dev-ops整个文件夹上传至linux根目录

step2:对项目1(consult-system)部署

cd /dev-ops/consult-system
docker-compose -f consult-compose.yml up -d
# 同理,利用docker ps查看是否存在consult-system-behind:latest

step3:对项目2(person-system)部署

cd /dev-ops/person-system
docker-compose -f consult-compose.yml up -d
# 同理,利用docker ps查看是否存在person-system-behind:latest

六、开放端口

需要在云服务器中开放对应的端口,根据自己设置的端口来开放,我这里的端口如下:

项目1:前端(81)后端(8877)

项目2:前端(82)后端(8082)

七、数据库加载

通过数据库可视化工具,输入自己的云服务器ip、账号、密码、端口(13306),进行登录,执行sql脚本,导入数据

注意:若登录不上,可能是没有开放13306端口,需要在云服务器中开放

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

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

相关文章

学习笔记(39):结合生活案例,介绍 10 种常见模型

学习笔记(39):结合生活案例&#xff0c;介绍 10 种常见模型线性回归只是机器学习的 “冰山一角”&#xff01;根据不同的任务场景&#xff08;分类、回归、聚类等&#xff09;&#xff0c;还有许多强大的模型可以选择。下面我用最通俗易懂的语言&#xff0c;结合生活案例&#…

BabyAGI 是一个用于自构建自主代理的实验框架

这个最新的 BabyAGI 是一个用于自构建自主代理的实验框架 核心是一个新的函数框架 &#xff08;functionz&#xff09;&#xff0c;用于存储、管理和执行数据库中的函数。它提供了一个基于图形的结构&#xff0c;用于跟踪导入、依赖函数和身份验证密钥&#xff0c;并具有自动加…

商业秘密视域下计算机软件的多重保护困境

作者&#xff1a;邱戈龙、柯坚豪重庆商业秘密律师广东长昊律师事务所引言&#xff1a;计算机软件保护的复杂性 在商业秘密保护的宏大版图中&#xff0c;计算机软件因其技术密集性和创新性占据着特殊地位。软件的真正价值不仅在于其代码本身&#xff0c;更在于其背后的流程、逻…

深入理解 Spring Boot 自动配置原理

Spring Boot 之所以能“开箱即用”&#xff0c;其核心就在于 自动配置机制&#xff08;Auto Configuration&#xff09;。本文将深入剖析 Spring Boot 自动配置的工作原理&#xff0c;从注解入手&#xff0c;再到底层的源码机制&#xff0c;揭开 Spring Boot 背后的“魔法”。 …

Ubuntu18.04开机启动执行脚本

#!/bin/bash # 运行 .NET Core 应用程序 dotnet /home/bruce/atg/SmartConsole.dll &# 打开浏览器 firefox 给文件权限sudo chmod 777 start.sh运行gnome-session-properties打开系统自带的一个启动程序

c语言进阶 字符函数和字符串函数

字符函数和字符串函数字符函数和字符串函数1. strlenstrlen 函数详解模拟实现1.计数器方式2.不能创建临时变量计数器&#xff08;递归&#xff09;3.指针-指针的方式2. strcpystrcpy 函数详解模拟实现3. strcatstrcat 函数详解模拟实现4. strcmpstrcmp 函数详解模拟实现5. strn…

(LeetCode 每日一题) 1233. 删除子文件夹 (排序)

题目&#xff1a;1233. 删除子文件夹 思路&#xff1a;排序&#xff0c;时间复杂度0(L*nlogn)。 文件夹a的子文件b&#xff0c;b字符串字典序列一定是大于a的&#xff0c;所以直接将字符串数组folder升序排序。每次只需判断当前字符串&#xff0c;是否是父文件夹数组v最后一个…

集成算法学习bagging,boosting,stacking

baggibg(rf随机森林) adaboostibg 用来展示 Project Jupyter | Home 展示源码 Eclipse IDE | The Eclipse Foundation Eclipse 下载 |Eclipse 基金会 教程8-Adaboost决策边界效果_哔哩哔哩_bilibili (23 封私信) 图解机器学习神器&#xff1a;Scikit-Learn - 知乎 Baggi…

HOOPS SDK赋能PLM:打造全生命周期3D数据管理与协作能力

在制造业和工业领域&#xff0c;产品全生命周期管理&#xff08;PLM&#xff09; 已成为驱动企业数字化转型、提升创新力与运营效率的核心引擎。一个高效的PLM平台不仅需要管理海量的设计数据&#xff0c;还必须在设计、制造、供应链、销售和服务等多个环节之间无缝流转信息&am…

解决 Selenium 页面跳转过快导致的内容获取问题:从原理到实践

在使用 Selenium 进行网页自动化操作时&#xff0c;很多开发者都会遇到一个头疼的问题&#xff1a;页面还没加载完&#xff0c;代码就已经执行到下一句了。结果要么是元素找不到&#xff0c;要么是获取的内容不完整&#xff0c;甚至直接抛出异常。今天我们就来聊聊如何优雅地解…

【Python练习】051. 编写一个函数,实现简单的定时器功能

051. 编写一个函数,实现简单的定时器功能 051. 编写一个函数,实现简单的定时器功能 代码说明: 示例运行: 扩展功能 代码说明: 实现Python定时器的几种方法 051. 编写一个函数,实现简单的定时器功能 以下是一个简单的Python函数,用于实现定时器功能。这个定时器可以设置…

springboot基础-demo

1.创建学生信息表 create table stu(id int unsigned primary key auto_increment comment ID,name varchar(100) comment 姓名,age tinyint unsigned comment 年龄,gender tinyint unsigned comment 性别, 1:男, 2:女,score double(5,2) comment 成绩,phone varchar(11) comme…

关于transformer的一些疑点总结

残差连接的作用 Transformer中的残差连接&#xff08;Residual Connection&#xff09;是其深层架构能稳定训练的核心设计之一&#xff0c;主要通过以下机制发挥作用&#xff1a; 1. 缓解梯度消失&#xff0c;支持深层训练 梯度保护机制&#xff1a;在反向传播时&#xff0c;…

【终极指南】解决 Windows 11 更新后 Docker 连接 localhost 奇慢(卡顿、超时十几秒)的通用方案

聪明人能看得出这是 ai 写的&#xff0c;但也是我亲身实践的&#xff0c;最后让 ai 总结写了一篇&#xff0c;放心食用 一、 结论先行&#xff08;直接用&#xff09;问题现象&#xff1a; 升级到某个 Windows 11 版本后&#xff0c;在本地访问 Docker 容器中部署的任何服务&am…

Stream API

Java 8 引入的 Stream API 是处理集合数据的强大工具&#xff0c;它允许你以声明式方式处理数据集合&#xff0c;支持各种聚合操作和并行处理。以下是 Stream API 的核心知识点及具体代码示例&#xff1a; 1. Stream 概述 Stream 是数据渠道&#xff0c;用于操作数据源&#xf…

相机参数的格式与作用

在计算机视觉中&#xff0c;相机标定是非常重要的一步&#xff0c;主要目的是从图像中恢复出物体的三维信息。为了做到这一点&#xff0c;我们需要了解和使用一系列的数学工具&#xff0c;这些工具描述了相机的成像过程&#xff0c;包括相机的内参、外参、畸变系数、投影矩阵和…

【jvm|基本原理】第四天

摘要&#xff1a;本文简单分析了Java虚拟机的核心运行机制。首先介绍了基本数据类型在32位和64位虚拟机中的存储差异&#xff0c;说明slot槽设计以空间换时间的优化思路。其次详细解析了对象在堆内存中的存储结构&#xff0c;包括对象头、对象数据和对齐填充机制。然后探讨了方…

Git高级操作与最佳实践详解

前言 熟练掌握Git的高级操作可以显著提高开发效率&#xff0c;优化工作流程&#xff0c;解决复杂问题。本文将详细介绍Git的高级操作技巧与最佳实践&#xff0c;帮助开发者更加高效地管理代码和协作开发。 1. 提交历史管理 1.1 修改最近的提交 # 修改最近的提交信息 git co…

ElasticSearch:商品SKU+SPU实现join查询,设计及优化

文章目录一、SPUSKU1、商品SPU和SKU2、SPU和SKU的关系3、实现SPUSKU父子嵌套查询1. **嵌套对象&#xff08;Nested Objects&#xff09;**2. **父子关系&#xff08;Parent-Child&#xff09;**3. **应用层关联&#xff08;Application-Side Join&#xff09;**&#xff08;推荐…

Objective-c 初阶 —— Runtime(方法交换 消息传递)

一、消息传递1、什么是消息[a func1];我们会把这种用方括号来调函数的方式称为发消息。对于这个例子&#xff0c;就相当于我们给 a 这个对象发了个 func1 的消息&#xff08;个人认为指令更好理解&#xff09;。2、什么是 selectorselector 就是一个函数区分器。它只会给这个方…