零基础学JavaWeb开发(二十)之 spring框架(3)

news2024/11/15 13:38:12

SpringBean的AOP

1、AOP基本的概念

AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。

AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。

简单理解:

Aop面向切面编程,在方法之前和之后实现处理 应用场景在于:日志打印、事务实现、安全、权限控制、自定义注解等。

因为AOP可以解决我们程序上的代码冗余问题

AOP 底层基于 代理设计模式封装

代理设计模式 静态代理与动态代理

动态代理 jdk动态代理与 cglib动态代理

通俗易懂 aop 在我们的目标方法之前和之后 处理的操作

开启事务

目标方法

提交或者回滚事务

提交或者回滚事务

aop 日志打印 事务原理 自定义实现

2、代理模式实现的原理

代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:

抽象主题角色:可以是接口,也可以是抽象类;

委托类角色:真实主题角色,业务逻辑的具体执行者;

代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

2.1、代理模式创建方式

相关测试代码:

package com.mayikt.service;


public interface OrderService {
    /**
     * 添加订单数据
     */
    String addOrder(String orderName);
}
package com.mayikt.service.impl;

import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class OrderServiceImpl implements OrderService {
    @Override
    public String addOrder(String orderName) {
        log.info("<orderName:{}>", orderName);
        return "ok";
    }
}

2.2、静态代理

2.2.1、基于接口实现方式

package com.mayikt.proxy1;

import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class OrderServiceProxy implements OrderService {
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    @Override
    public String addOrder(String orderName) {
        // 目标方法前后处理操作
        log.info("<目标方法之前执行...>");
        String result = orderService.addOrder(orderName);
        log.info("<目标方法之后执行...>");
        return result;
    }
}

package com.mayikt.proxy1;

import com.mayikt.service.impl.OrderServiceImpl;


public class Test01 {
    public static void main(String[] args) {
        OrderServiceProxy orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());
        String result = orderServiceProxy.addOrder("mayikt");
        System.out.println(result);
    }
}

2.2.2、基于继承实现方式

package com.mayikt.proxy2;

import com.mayikt.service.impl.OrderServiceImpl;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class OrderServiceProxy  extends OrderServiceImpl {
    @Override
    public String addOrder(String orderName) {
        // 目标方法前后处理操作
        log.info("<目标方法之前执行...>");
        String result = super.addOrder(orderName);
        log.info("<目标方法之后执行...>");
        return result;
    }
}

package com.mayikt.proxy2;


public class Test01 {
    public static void main(String[] args) {
        OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
        String result = orderServiceProxy.addOrder("mayikt");
        System.out.println(result);
    }
}

2.3、动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

JDK动态代理的一般步骤如下:

1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对

我们的目标方法增强。

2.3.1、JDK API动态代理用法

package com.mayikt.proxy3;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


@Slf4j
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("<jdk动态代理目标方法之前>,args:{}", args);
        Object result = method.invoke(target, args);
        log.info("<jdk动态代理目标方法之后,args:{}", args);
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

package com.mayikt.proxy3;

import com.mayikt.service.OrderService;
import com.mayikt.service.impl.OrderServiceImpl;


public class Test01 {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
        OrderService orderService = jdkInvocationHandler.getProxy();
        orderService.addOrder("mayikt");
    }
}

2.3.2、动态代理与静态代理的区别

动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。

 

2.3.3、JDK动态代理原理分析

1. 获取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import com.mayikt.service.OrderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements OrderService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String addOrder(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
    public static void main(String[] args) {
        $Proxy0 $Proxy0 = new $Proxy0(new JdkInvocationHandler(new OrderServiceImpl()));
        $Proxy0.addOrder("mayikt");
    }
  public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                this);
      
      
 生成代理类时 会传递我们目标对象实现哪些接口
target.getClass().getInterfaces()
 如果我们目标对象没有实现接口 jdk动态代理生成代理类 也没有实现接口

     
 使用jdk动态代理时 注意 让目标对象实现接口, 生成代理类时 实现目标对象的接口
 方便可以使用接口调用目标对象的方法

1.执行到我们 代理类中$Proxy0.addOrder()

2.执行到我们MayiktInvocationHandler.invoke

  public final String addOrder(String var1) {
        try {
            return (String) super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

3.执行到我们MayiktInvocationHandler.invoke 在根据java反射机制 传递目标对象 调用目标方法

2.3.4、mybatis mapper接口分析

在mybatis mapper 是一个接口为何可以调用的呢?底层其实就是基于JDK动态代理实现。

相关代码:

package com.mayikt.mybatis.ext;

import org.springframework.stereotype.Indexed;

import java.lang.annotation.*;


@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MayiktInsert {
    String value();
}


package com.mayikt.mybatis;

import com.mayikt.mybatis.ext.MayiktInsert;
import com.mayikt.utils.MayiktJdbcUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;


public class MybatisJdkInvocationHandler implements InvocationHandler {
    private Class mapperClass;

    public MybatisJdkInvocationHandler(Class mapperClass) {
        this.mapperClass = mapperClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 使用java反射技术获取该方法上的注解
        MayiktInsert declaredAnnotation = method.getDeclaredAnnotation(MayiktInsert.class);
        String insertSql = declaredAnnotation.value();
        if (StringUtils.isEmpty(insertSql)) {
            return null;
        }
        // 执行该sql语句
        Connection connection = MayiktJdbcUtils.getConnection();
        PreparedStatement preparedStatement = connection.prepareStatement(insertSql);
        int result = preparedStatement.executeUpdate();
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, this);
    }
}

package com.mayikt.mybatis;

import com.mayikt.mybatis.ext.MayiktInsert;

public interface UserMapper {
    @MayiktInsert("INSERT INTO `mayikt`.`mayikt_users` (`id`, `name`, `age`) VALUES (null, 'wangmazi', NULL);")
    int addUser();
}

package com.mayikt.mybatis;


public class Test01 {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
        int result = userMapper.addUser();
        System.out.println(result);
    }
}

package com.mayikt.mybatis;


public class MapperProxy {
    public static UserMapper getUserMapper(Class mapperClass) {
        return new MybatisInvocationHandler(mapperClass).getProxy();
    }

    public static void main(String[] args) {
        // 将jdk动态生成好的 class 存放本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
        int i = userMapper.addUser();
        System.out.println(i);
    }
}

📎mayikt-designmode.rar

2.3.5、什么是CGLIB 动态代理

Cglib 底层基于asm实现

Cglib 与jdk动态代理到底有哪些区别呢?

jdk动态代理底层基于反射方式调用目标方法 jdk7之前效率是非常低后期优化。

Cglib 底层是反射调用目标方法效率非常低 ,直接采用建立fastclass 索引的方式

调用目标方法。

jdk7之前Cglib动态代理效率是比我们jdk动态代理效率高非常多

jdk7开始jdk动态代理是我们Cglib动态代理效率高。

Cglib动态代理生成的代理类 直接 继承 被代理类(实现继承方式代理)

public class OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c extends OrderServiceImpl implements Factory {

jdk动态代理生成代理类 实现 被代理实现的接口 (实现接口方式代理)

Cglib底层生成代理类是通过 asm 生成字节码 class

1.Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。

2.运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。

3.jdk7开始 jdk动态代理效率比cglib要高

spring底层采用 cglib代理类?还是jdk动态代理

判断 被代理类 实现接口 使用jdk动态代理

如果被代理类 没有实现接口则使用cglib代理类

2.3.6、cglib基本用法

    <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>
    </dependencies>

实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。

intercept方法的具体参数有

obj 目标类的实例

1. method 目标方法实例(通过反射获取的目标方法实例)

2. args 目标方法的参数

3. proxy 代理类的实例

该方法的返回值就是目标方法的返回值。

package com.mayikt.service;


public interface OrderService {
    /**
    * 添加订单数据
    */
    String addOrder(String orderName);
}
package com.mayikt.service.impl;

import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class OrderServiceImpl implements OrderService {
//    @Override
    public String addOrder(String orderName) {
        log.info("<orderName:{}>", orderName);
        // addorder相关的事情
        return "ok";
    }
}
package com.mayikt.cglib;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;


@Slf4j
public class MayiktCglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        log.info("<目标方法之前开始执行....>");
//        Object result = method.invoke(obj, args);
        Object result = proxy.invokeSuper(obj, args);
        log.info("<目标方法之后开始执行....>");
        return result;
    }
}
package com.mayikt.cglib;

import com.mayikt.service.impl.OrderServiceImpl;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;


public class Test01 {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code\\cglib");
        MayiktCglibMethodInterceptor mayiktCglibMethodInterceptor = new MayiktCglibMethodInterceptor();
        Enhancer enhancer = new Enhancer();
        // 设置代理
        enhancer.setSuperclass(OrderServiceImpl.class);
        // 设置cglib 回调类
        enhancer.setCallback(mayiktCglibMethodInterceptor);
        // 创建cglib代理类
        OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
        String result = orderServiceImpl.addOrder("mayikt");
        System.out.println(result);
    }
}

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

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

相关文章

二叉树知识锦囊(三)

作者&#xff1a;爱塔居 专栏&#xff1a;数据结构​​​​​​ 作者简介&#xff1a;大三学生&#xff0c;希望和大家一起进步&#xff01; 目录 前言 1. 检查两棵树是否相同。 2. 另一颗树的子树。 3. 翻转二叉树。 4. 判断一颗二叉树是否是平衡二叉树。 5. 对称二叉树。 前…

【Python】Python数据结构之布尔类型(bool)

目录&#xff1a;Python数据结构之布尔类型&#xff08;bool&#xff09;一、布尔说明二、判定三、布尔运算&#xff1a; and, or, not一、布尔说明 Python 中布尔值使用常量True 和 False来表示&#xff1b;注意大小写。比较运算符< > 等返回的类型就是bool类型&#…

C++虚继承,虚基表 ,菱形继承以及解决方法

目录菱形继承形成原因出现二义性变量的内存布局应对方案虚继承 vitrual解决二义性变量内存布局--虚基表感悟关于代码复用等的另一种关系-组合菱形继承形成原因 多继承&#xff0c;呈菱形状 菱形继承代码: class A { public:A() {}int _a ; }; class B :public A { public…

分享131个ASP源码,总有一款适合您

ASP源码 分享131个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 131个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/17vXlBvqeYPM5-XUlu5zaAg?pwd3zzi 提取码&#x…

【Qt】如何使用QtCreator向工程添加文件

文章目录一、导读二、盘一盘文件模板&#xff08;2-1&#xff09;添加C/C文件&#xff08;2-2&#xff09;添加Modeling文件&#xff08;2-3&#xff09;添加Qt相关文件&#xff08;2-4&#xff09;添加GLSL相关文件&#xff08;2-5&#xff09;添加其他文件三、总结一、导读 …

【JavaSE专栏3】JDK安装、IntelliJ IDEA安装、配置环境变量

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

【苹果相册推】Xcode项目,我们将其命名为mypushchat,以及调试的iOS设备

推荐内容IMESSGAE相关 作者✈️IMEAX推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者✈️IMEAX推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者✈️IMEAX推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者✈️IMEAX推荐内容3.日历推 *** …

第二个程序——客户端ClientUI

简介 在我的上一篇文章中&#xff0c;我已经介绍了如何实现“在线聊天室”中的服务器端ServerUI&#xff0c;服务器端作为整个聊天系统的“中继系统”&#xff0c;负责转发用户的信息到聊天室&#xff0c;可以转发给聊天室中的每一个人&#xff08;即&#xff0c;群聊&#xf…

一期Go群问答-并发控制-数据竞争-错误与异常

每周更新Go技术交流群的群问答内容&#xff0c;有需要可发我Go加群讨论学习。 并发控制 waitGroup.done()不是必须写在main方法中吗? 为什么我的协程没有成功等待&#xff1f; 熊&#xff1a;如果用了wait group&#xff0c;请求就直接卡住了&#xff0c;如果只有一个gorou…

Linux C编程一站式学习笔记5

Linux C编程一站式学习笔记 chap5 深入理解函数 文章目录Linux C编程一站式学习笔记 chap5 深入理解函数一.return语句习题二.增量式开发三.递归我猜有递归可视化工具&#xff0c;一搜果真有收获习题GCD(Greatest Common Divisor) 最大公约数Fibonacci相关资源、参考资料嘶&…

在linux中安排mysql

linux安装mysql 检测当前系统中是否安装Mysql数据库 rpm -qa rpm -qa|grep mysql rpm -qa|grep mariadb没有输出就是没有安装 我的这里显示mariadb是安装了的&#xff08;会与mysql冲突&#xff09; 卸载已经安装的软件 rpm -e --nodeps 软件名称 rpm -e --nodeps mariadb-li…

什么是执行董事

一、什么是执行董事执行董事&#xff0c;是指参与经营的董事。作为法定意义上的执行董事&#xff0c;是指规模较小的有限公司在不设立董事会的情况下设立的负责公司经营管理的职务。作为上市公司意义上的执行董事&#xff0c;执行董事并没有明确的法规依据。执行董事和非执行董…

偷偷理解Java和Scala中==和equals()的区别

君霸王&#xff0c;社稷定&#xff0c;君不霸王&#xff0c;社稷不定&#x1f97d; 目录 Java总结 Scala总结 Java中和equals() ---------------------------------------------------------------------------------------------------------------------------------------…

【人工智能原理自学】卷积神经网络:图像识别实战

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文讲解卷积神经网络&#xff1a;图像识别实战&#xff0c;一起卷起来叭&#xff01; 目录一、“卷”二、LeNet-5网络一、“卷” 这节课我们来看如何把卷积运算融入到神经网络…

【青训营】Go语言的基本语法

一、 配置Go语言及其开发环境 Mac配置&#xff1a;http://t.zoukankan.com/zsy-p-6685889.html https://wenku.baidu.com/view/8aeec92b15fc700abb68a98271fe910ef12daeaf.html?wkts1673764660043&bdQuery%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AEgopathmac 二、基础语法 p…

避免用Apache Beanutils进行属性的copy。why?让我们一起一探究竟。

在实际的项目开发中&#xff0c;对象间赋值普遍存在&#xff0c;随着双十一、秒杀等电商过程愈加复杂&#xff0c;数据量也在不断攀升&#xff0c;效率问题&#xff0c;浮出水面。 问&#xff1a;如果是你来写对象间赋值的代码&#xff0c;你会怎么做&#xff1f; 答&#xf…

05 |「链表」刷题

前言 前言&#xff1a;链表面试高频题。 文章目录前言一. 基础回顾二. 高频考题1. 例题1&#xff09;题目链接&#xff08;LeetCode 206 反转链表&#xff09;2&#xff09; 算法思路3&#xff09;源码剖析4&#xff09;时间复杂度2. 习题一. 基础回顾 参考上一讲&#xff1a; …

线性代数[向量]

系列文章目录 第一章 线性代数[初等变换(一)] 第二章 线性代数[初等变换(二)] 第三章 线性代数[初等变换(三)] 第四章 线性代数[矩阵的秩] 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一.引入 二.向…

计网必会:运输层概述、网络层介绍、TCP、UDP、多路复用、多路分解

文章目录运输层概念回顾TCP 和UDP概述介绍网络层TCP UDP网络层的联系多路复用和多路分解多路复用和多路分解的中国话理解TCP的多路复用和多路分解与UDP的区别HTTP 会话Web服务器和TCPUDP的多路复用和多路分解源端口号的用处的中国话理解UDP套接字无连接运输UDP 的优势运输层概念…

【C语言进阶】指针进阶(详细版)

目录 一、字符指针 二、指针数组 三、数组指针 1、数组指针的定义 2、&数组名和数组名的区别 3、数组指针的使用 四、数组传参和指针传参 1、一维数组传参 2、一级指针传参 3、二维数组传参 4、二级指针传参 五、函数指针 1、函数指针的定义 2、函数指针的使用 六、…