切面的魔力:解密Spring AOP 面向切面编程

news2024/12/25 3:41:57

目录

一、AOP简介

1.1 什么是AOP ?

1.2 什么是面向切面编程 ?

1.3 AOP 的特点

二、 AOP的基本概念解读

2.1 AOP的基本概念

2.2 AOP 概念趣事解读

三、代码情景演示

3.1 编写目标对象(超级英雄们正常的行动)

3.2 编写通知类

3.2.1 前置通知

3.2.2 后置通知

3.2.3 异常通知

3.2.4 环绕通知

3.3 spring核心xml文件配置

3.4 测试运行

3.5 配置过滤通知后测试

总结


一、AOP简介

1.1 什么是AOP ?

        AOP(Aspect-Oriented Programming)是一种软件开发技术,旨在通过将横切关注(cross-cutting concerns)从主要业务逻辑中分离出来,提供更好的模块化和可维护性。AOP通过在程序执行过程中动态地将这些关注点织入到代码中,从而实现了代码的解耦和重用。

1.2 什么是面向切面编程 ?

        面向切面编程(Aspect-Oriented Programming)是AOP的一种具体实现方式。它通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,以切面(Aspect)的形式进行模块化。切面定义了在何处和何时应该应用横切关注点。切面通常由切点(Pointcut)和通(Advice)组成。

1.3 AOP 的特点

  1. 横切关注点的模块化:AOP允许将与业务逻辑无关的横切关注点(如日志记录、事务管理、安全性等)从主要业务逻辑中分离出来,以切面的形式进行统一管理。这样可以提高代码的可维护性,使开发人员能够更好地关注核心业务逻辑。

  2. 解耦和重用:通过将横切关注点从主要业务逻辑中分离出来,AOP实现了代码的解耦。这意味着可以更容易地修改、扩展和重用横切关注点,而无需修改主要业务逻辑。这提高了代码的可重用性和可维护性。

  3. 声明式编程:AOP允许开发人员通过声明式的方式将切面应用到目标对象中,而无需在目标对象的代码中显式地编写切面逻辑。这使得代码更加清晰、简洁,并且易于理解和维护。

  4. 动态织入:AOP允许在程序运行时动态地将切面织入到目标对象中。这意味着可以根据需要选择性地应用切面,而无需在编译时或加载时进行硬编码。这提供了更大的灵活性和可配置性。

  5. 提高系统性能:AOP可以将一些通用的横切关注点(如性能监控、缓存管理等)应用到多个目标对象中,从而提高系统的性能和效率。这避免了在每个目标对象中重复编写相同的代码。

        总的来说,AOP的特点包括横切关注点的模块化、解耦和重用、声明式编程、动态织入以及提高系统性能。这些特点使得AOP成为一种强大的技术,可以提高代码的可维护性、可重用性和可测试性,同时降低代码的复杂性和重复性。

二、 AOP的基本概念解读

2.1 AOP的基本概念

  1. 切面(Aspect):切面是一个模块化的单元,它封装了与横切关注点相关的行为。切面可以包含通知和切点。

  2. 连接点(Join Point):连接点是在应用程序执行过程中可以插入切面的点。它可以是方法调用、方法执行、异常抛出等。(也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)

  3. 切点(Pointcut):切点定义了在哪些连接点上应用切面。它使用表达式来匹配连接点,例如指定特定的类、方法、注解等。

  4. 通知(Advice):通知是切面在特定连接点上执行的动作。常见的通知类型包括前置通知(Before)、后置通知(After)、返回通知(After Returning)和异常通知(After Throwing)。

  5. 引入(Introduction):引入允许在现有类中添加新的方法和属性。它允许将新功能引入到现有的类中,而无需修改类的源代码。

  6. 织入(Weaving):织入是将切面应用到目标对象中并创建新的代理对象的过程。织入可以在编译时、类加载时或运行时进行。

  7. AOP代理(Proxy):AOP框架创建的对象,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。(代理=目标+通知)    注:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的

  8. 目标对象(Target): 包含连接点的对象,被通知(被代理)的对象,完成具体的业务逻辑 。

2.2 AOP 概念趣事解读

        AOP最多的就是概念,面对这么概念我们大多数人是记不住的。特别是像博主这样的越看感觉都要长脑子了,所以我准备一个小故事来解读AOP各功能(术语)的解读。

《 这是一个好故事 》

        小猪侠是一个勇敢而机智的超级英雄,他的使命是保护城市的和平与安全。他有一个特殊的能力,可以通过棒棒糖来赋予他人额外的力量和技能。这个棒棒糖就是它的 " 切面(Aspect)"。

        小猪侠的切面是一个模块化的单元,它封装了与超级英雄相关的行为。他的切面包含了一些 通知(Advice),这些通知定义了在特定的情境(连接点)下给其他超级英雄赋予的额外力量和技能。连接点(Join Point)可以是超级英雄的战斗、救援行动、危机处理等。

        为了将它的切面应用到超级英雄身上,小猪侠需要创建一个 代理(Proxy)。代理是一个中间人,它将超级英雄包装起来,并在必要时调用切面中定义的通知。代理使得超级英雄在执行任务时能够自动获得额外的力量和技能。

        超级英雄们在执行任务时,并不知道小猪侠的切面和代理的存在。他们只需要按照正常的方式进行战斗和救援,而不需要关注切面的实现细节。这就是 目标对象(Target)的作用。目标对象是超级英雄们正常的行动,而切面和代理则为他们提供了额外的力量和技能。

        一天,小猪侠遇到了一个危机,城市中的一座大桥即将坍塌。他需要帮助其他超级英雄一起救援。小猪侠使用他的切面技能,创建了一个前置通知,它会在超级英雄们展开救援行动之前被触发。

        小猪侠将他的切面应用到超级英雄们身上,通过代理来调用他们的救援行动。当超级英雄们准备展开救援行动时,切面中的前置通知被触发,小猪侠使用棒棒糖赋予他们额外的力量和技能,使得他们能够成功救援并修复大桥。

        在这个故事中,小猪侠的切面代表了它的棒棒糖,连接点是超级英雄们展开救援行动的位置,代理是将切面应用到超级英雄们身上的中间人,通知是切面中定义的赋予额外力量和技能的动作,目标对象是超级英雄们正常的行动,织入是将切面应用到目标对象中的过程。

三、代码情景演示

3.1 编写目标对象(超级英雄们正常的行动)

1. 为了降低代码耦合性,首先编写一个动作行为的接口定义发动技能和飞行的方法。

package com.ycxw.aop.biz;

/**
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:08
 */
public interface Behavior {
	// 飞行
	public boolean fly(String name);

	// 发动技能
	public void skill(String name, String skills);
}

2. 编写实现接口的类

package com.ycxw.aop.biz.impl;

import com.ycxw.aop.biz.Behavior;
/**
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:15
 */
public class BehaviorImpl implements Behavior {

	public BehaviorImpl() {
		super();
	}


	@Override
	public boolean fly(String name) {
		System.out.println("超级英雄:"+name);
		return true;
	}

	@Override
	public void skill(String name, String skills) {
		System.out.println("超级英雄:"+name+" 发动了"+skills);
	}
}

3.2 编写通知类

通知:是切面中定义的赋予额外力量和技能的动作

3.2.1 前置通知

在目标对象使用前执行:

package com.ycxw.aop.advice;

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 前置通知
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:20
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        //在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
        String target = arg2.getClass().getName();
        String methodName = arg0.getName();
        String args = Arrays.toString(arg1);
        System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+") 被调用了");

    }
}

3.2.2 后置通知

在目标对象使用完后执行:

package com.ycxw.aop.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 后置通知
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:48
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
        String target = arg3.getClass().getName();
        String methodName = arg1.getName();
        String args = Arrays.toString(arg2);
        System.out.println("【后置通知:】:"+target+"."+methodName+"("+args+") 被调用了,"+"该方法被调用后的返回值为:"+arg0);

    }
}

3.2.3 异常通知

当运行发生异常执行该通知:

package com.ycxw.aop.advice;

import com.ycxw.aop.exception.PriceException;
import org.springframework.aop.ThrowsAdvice;

/**
 * 异常通知
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 17:00
 */
public class MyThrowsAdvice implements ThrowsAdvice {
    public void afterThrowing(PriceException ex) {
        System.out.println("【异常通知】:当技能施展发生异常,那么执行此处代码块!!!");
    }

}

3.2.4 环绕通知

这个在平常是用的最多的,比较便捷,相当于结合前置和后置的通知功能。

package com.ycxw.aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.util.Arrays;

/**
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:54
 */
public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        String target = arg0.getThis().getClass().getName();
        String methodName = arg0.getMethod().getName();
        String args = Arrays.toString(arg0.getArguments());
        System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了");
//		arg0.proceed()就是目标对象的方法
        Object proceed = arg0.proceed();
        System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed);
        return proceed;

    }
}

3.3 spring核心xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  目标对象  -->
    <bean class="com.ycxw.aop.biz.impl.BehaviorImpl" id="behavior"></bean>
<!-- 通知  -->
    <bean class="com.ycxw.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
    <bean class="com.ycxw.aop.advice.MyAfterReturningAdvice" id="afterAdvice"></bean>
    <bean class="com.ycxw.aop.advice.MyMethodInterceptor" id="interceptor"></bean>
    <bean class="com.ycxw.aop.advice.MyThrowsAdvice" id="throwsAdvice"></bean>
   
<!--  代理  ProxyFactoryBean类似于工厂模式(直接配置)-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="behaviorProxy">
    <!--   配置目标对象   -->
        <property name="target" ref="behavior"></property>
        <!--配置代理目标对象的接口 cglid动态代理-->
        <property name="proxyInterfaces">
            <list>
                <value>com.ycxw.aop.biz.Behavior</value>
            </list>
        </property>
        <!--配置通知-->
        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>
                <value>afterAdvice</value>
                <value>interceptor</value>
                <value>throwsAdvice</value>
            </list>
        </property>
    </bean>
</beans>

3.4 测试运行

1. 编写测试类

package com.ycxw.aop.demo;

import com.ycxw.aop.biz.Behavior;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 云村小威
 * @site blog.csdn.net/Justw320
 * @create 2023-08-17 16:08
 */
public class demo1 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        Behavior bean = (Behavior) context.getBean("behaviorProxy");
        bean.fly("小威");
        bean.skill("小威","变大技能");
    }
}

2. 运行结果

 由此可见,应证了前面的通知。可看到环绕通知跟前置和、后置通知处于等效功能,而环绕通知更为简便。

3.5 配置过滤通知后测试

这里过滤了后置通知:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--AOP-->
    <!--  目标对象  -->
    <bean class="com.ycxw.aop.biz.impl.BehaviorImpl" id="behavior"></bean>
<!-- 通知  -->
    <bean class="com.ycxw.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
    <bean class="com.ycxw.aop.advice.MyAfterReturningAdvice" id="afterAdvice"></bean>
    <bean class="com.ycxw.aop.advice.MyMethodInterceptor" id="interceptor"></bean>
    <bean class="com.ycxw.aop.advice.MyThrowsAdvice" id="throwsAdvice"></bean>
    <!--过滤-->
    <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" id="regexpAdvisor">
        <!--配置需要过滤的通知-->
        <property name="advice" ref="afterAdvice"></property>
        <!--通过正则过滤指定方法-->
        <property name="pattern" value=".*skill"></property>
    </bean>

<!--  代理  ProxyFactoryBean类似于工厂模式(直接配置)-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="behaviorProxy">
    <!--   配置目标对象   -->
        <property name="target" ref="behavior"></property>
        <!--配置代理目标对象的接口 cglid动态代理-->
        <property name="proxyInterfaces">
            <list>
                <value>com.ycxw.aop.biz.Behavior</value>
            </list>
        </property>
        <!--配置通知-->
        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>
                <value>regexpAdvisor</value>
                <value>throwsAdvice</value>
            </list>
        </property>
    </bean>
</beans>

由运行结果可见,等代码执行完后才执行后置通知,在前面的fly方法后才过滤的后置通知。

总结

        AOP是面向切面编程,它通过在程序执行过程中动态地将横切关注点织入到代码中来实现。当程序执行到目标对象的目标方法时,AOP可以在方法调用前、后或异常抛出时执行相应的通知。

        具体来说,如果连接点上有前置通知,AOP会先执行前置通知,然后再执行目标方法。前置通知可以在目标方法执行之前执行一些预处理操作,如日志记录、参数验证等。

        如果没有前置通知,AOP会直接执行目标方法。然后,AOP会检查目标方法上是否有后置通知。如果有后置通知,AOP会在目标方法执行后执行后置通知。后置通知可以在目标方法执行之后执行一些后处理操作,如结果处理、资源释放等。

        除了前置和后置通知,AOP还支持异常通知和环绕通知。异常通知可以在目标方法抛出异常时执行一些处理逻辑。环绕通知是最强大的通知类型,它可以完全控制目标方法的执行过程,包括在方法调用前后执行自定义的逻辑。

        需要注意的是,虽然通知代码通常是非业务核心代码,如日志记录和事务管理,但并不是所有的通知都是非业务核心代码。有些通知可能涉及到业务逻辑,例如在目标方法执行前进行权限检查。AOP的灵活性和可配置性使得开发人员可以根据具体需求来定义和应用切面,以实现代码的解耦和重用,提高代码的可维护性和可测试性。

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

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

相关文章

史上最全HBase面试题,高薪必备,架构必备

说在前面 本文《尼恩 大数据 面试宝典》 是 《尼恩Java面试宝典》姊妹篇。 这里特别说明一下&#xff1a;《尼恩Java面试宝典》41个专题 PDF 自首次发布以来&#xff0c; 已经汇集了 好几千题&#xff0c;大量的大厂面试干货、正货 &#xff0c;足足4800多页&#xff0c;帮助…

【脚踢数据结构】常见排序算法

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的…

keil遇到“Error:CreateProcess failed,Command:“怎么办

解决办法&#xff1a; 第一步&#xff1a;卸载原有的compiler 5; 第二步&#xff1a;安装compiler 5到指定目录&#xff1a;这个目录地址是&#xff1a; 安装目录地址“ARM安装目录” / ARMARM_Compiler_5.06u7 例如我的安装目录是C:\Keil_v5\ARM\ARM_Compiler_5.06u7 第三…

苹果电脑清理软件——CleanMyMac快速清理电脑垃圾

大家在用苹果电脑卸载软件时是不是很苦恼&#xff1f;遇到要清理的垃圾软件太多&#xff0c;总是清理不干净。这时可以借助苹果电脑清理软件——CleanMyMac快速清理&#xff0c;接下来的文章就教教大家快速清理电脑垃圾软件&#xff0c;怎么快速清理电脑垃圾软件。 CleanMyMac …

LangChain手记 Evalutation评估

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Evaluation&#xff08;源代码可见&#xff09; 基于LLM的应用如何做评估是一个难点&#xff0c;本节介绍了一些思路和工具。 “从传统开发转换到基于prompt的开发&#xff0c;开发使用LLM的应用&#xff0c;整个工…

云农场种植模式:数字化农业的崭新未来

在现代科技的映衬下&#xff0c;农业正在经历着前所未有的变革。云农场种植模式作为数字化农业的一个重要组成部分&#xff0c;为农业注入了新的活力和可能性。这种创新模式不仅提升了农业效率&#xff0c;还为农民、消费者以及环境带来了许多积极影响。 云农场种植模式玩法说明…

vue实现可缩放拖拽盒子(亲测可用)

特征 没有依赖 使用可拖动&#xff0c;可调整大小或两者兼备定义用于调整大小的句柄限制大小和移动到父元素或自定义选择器将元素捕捉到自定义网格将拖动限制为垂直或水平轴保持纵横比启用触控功能使用自己的样式为句柄提供自己的样式 安装和基本用法 npm install --save vue-d…

解决“warning: #223-D: function “xPortSysTickHandler“ declared implicitly“告警提示

继上篇文章发布已有时隔两个月之久&#xff0c;今天就把这两个月遇到的一些问题解决分享一下&#xff0c;首先&#xff0c;我们来看今天分享的这个关于我在学习freertos遇到的一个告警。如图所示&#xff1a; 告警提示原句为&#xff1a; warning: #223-D: function "xP…

DNDC模型建模方法及土壤碳储量、温室气体排放、农田减排、土地变化、气候变化应用

DNDC&#xff08;Denitrification-Decomposition&#xff0c;反硝化-分解模型&#xff09;是目前国际上最为成功的模拟生物地球化学循环的模型之一&#xff0c;自开发以来&#xff0c;经过不断完善和改进&#xff0c;从模拟简单的农田生态系统发展成为可以模拟几乎所有陆地生态…

BAT测试专家对web测试和APP测试的总结

单纯从功能测试的层面上来讲的话&#xff0c;App 测试、Web 测试在流程和功能测试上是没有区别的&#xff0c;但由于系统结构方面存在差异&#xff08;web 项目&#xff0c;b/s 架构&#xff1b;app 项目&#xff0c;c/s 结构&#xff09;在测试中还是有不同的侧重点内容&#…

SOLIDWORKS 弹簧扣特征-塑料制品的福音

很多从事塑料制品产业的工程师&#xff0c;每天都在面对如何提高配合精度的问题&#xff0c;特别是扣合的精度。通常情况下扣合类结构采用分开建立扣与槽的形式&#xff0c;该方式已经无法满足当前环境下设计的需要&#xff0c;当然在SOLIDWORKS中此类问题很早之前就已经被考虑…

扁线电机定子转子工艺及自动化装备

售&#xff1a;扁线电机 电驱对标样件 需要请联&#xff1a;shbinzer &#xff08;拆车邦&#xff09; 新能源车电机路线大趋势&#xff0c;自动化装配产线需求迫切永磁同步电机是新能源车驱动电机的主要技术路线。目前新能源车上最广泛应用的类型为永磁同步电机&#xff0c…

数组的详述(2)

2、二维数组的创建和初始化 可以把二维数组理解为一维数组的数组。 行 列 //行可省略&#xff0c;列不能省。 二维数组的使用&#xff08;<而不是<&#xff0c;因为数组下标第一个是0&#xff09; 二维数组在内存中的储存​​​​&#xf…

day4 驱动开发

【ioctl函数的使用】 1.概述 linux有意将对设备的功能选择和设置以及硬件数据的读写分成不同的函数来实现。让read/write函数专注于数据的读写&#xff0c;而硬件功能的设备和选择通过ioctl函数来选择 2.ioctl函数分析 int ioctl(int fd,unsigned long request) 通过&…

Spring-aop特点,专业术语及案例演示

一.aop简介 AOP&#xff08;Aspect-Oriented Programming&#xff09;是Spring框架的一个重要特性&#xff0c;它通过将横切关注点&#xff08;cross-cutting concerns&#xff09;从核心业务逻辑中分离出来&#xff0c;以模块化的方式在整个应用程序中重复使用。以下是关于AOP…

udp与can通信的选择与比较

UDP&#xff08;用户数据报协议&#xff09;和CAN&#xff08;控制器局域网&#xff09;是两种不同的通信协议&#xff0c;它们在实时传递性上有一些区别。 UDP是一种无连接的传输协议&#xff0c;它提供了简单的、不可靠的数据传输。UDP不提供可靠性保证、流控制或重传机制。…

图解二叉树,拿下拿下!

图文详解二叉树 一、树形结构概念特性二、树形结构基本概念术语三、树的存储结构四、二叉树 概念与特性五、特殊的二叉树六、二叉树的性质七、二叉树的存储结构八、二叉树的基本操作1、二叉树的遍历&#xff08;1&#xff09;前中后序遍历&#xff08;2&#xff09;经典找序列&…

R语言APSIM模型进阶应用与参数优化、批量模拟实践技术

随着数字农业和智慧农业的发展&#xff0c;基于过程的农业生产系统模型在模拟作物对气候变化的响应与适应、农田管理优化、作物品种和株型筛选、农田固碳和温室气体排放等领域扮演着越来越重要的作用。APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生…

探索性测试及基本用例

1 测试决策5要素 测试目标&#xff1a;所有的重要任务都完成了&#xff0c;而剩下没做的事情是比较次要的&#xff0c;我们做到这一点就可以尽早尽可能地降低发布风险。 测试方法&#xff1a;测试是一个不断抉择的过程&#xff0c;测试人员必须理解运行测试用例时和分析现有信…

自定义表格组件:实现表格中有固定列的功能逻辑

目录 1&#xff0c;效果图2&#xff0c;实现思路3&#xff0c;实现方式 1&#xff0c;效果图 可以拖动纵向滑块&#xff0c;最左边一列固定住。 以同样的道理&#xff0c;可以在右面固定一列 2&#xff0c;实现思路 作为一个table组件&#xff0c;要接受父组件中的对table的…