[笔记]Spring AOP

news2024/9/29 23:30:26

Spring AOPAspect Oriented Programming

  • AOP将应用程序分为核心业务和非核心的公共功能,AOP的关注点是系统中的非核心的公共功能;

  • AOP可以通过预编译或者运行期动态代理的方式,为横跨多个对象(没有继承关系..)的业务逻辑添加统一的功能横切关注点Cross Cutting Concern:事务、日志记录、权限检查、异常处理等)

  • 使用AOP可以实现在不修改核心业务源代码的情况下为核心业务添加统一的功能,实现了核心业务和非核心的公共功能之间的解耦,提高了代码的重用性;

基于注解的AOP

在Spring Boot中,AOP主要通过注解来实现,以简化配置和提升代码的可读性。

@Aspect注解
  • 作用:将一个类定义为切面类。

  • 用法:与@Component@Service等注解结合使用,以便Spring容器管理这个切面。

定义切点(Pointcut)
  • @Pointcut:定义了何处应用AOP(比如哪些方法)。

  • 表达式:使用execution表达式指定方法模式。

@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {
}
定义通知(Advice)
  • @Before:在方法执行之前执行。

  • @AfterReturning:方法成功执行后执行。

  • @AfterThrowing:方法抛出异常后执行。

  • @After:方法执行后执行,无论其结果如何。

  • @Around:在方法执行前后执行,提供了最大的灵活性。

@Before("serviceLayer()")
public void logBeforeService(JoinPoint joinPoint) {
    // 实现逻辑
}
实际应用示例
  • 切面类:结合@Aspect@Component定义切面。

  • 通知方法:使用@Before@After等注解定义不同类型的通知。

@Aspect
@Component
public class LoggingAspect {
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        // 日志逻辑
    }

    // 其他通知定义
}
在Spring Boot中的使用
  • 依赖:确保已添加AOP相关依赖(如spring-boot-starter-aop)。

  • 注解:直接在类和方法上使用@Aspect和通知相关的注解。

  • 无需XML:不需要XML配置,Spring Boot会自动处理这些注解。

静态代理 vs 动态代理

  1. 静态代理

    1. 实现方式:

      • 通过继承或实现接口。

      • 需要为每个目标类创建一个代理类。

    2. 缺点:

      • 高耦合度:代理类和目标类紧密绑定。

      • 可扩展性差:每个新类都需要新的代理类。

  2. 动态代理

    1. 在运行时动态创建代理类,不需要显式地为每个目标类编写代理类。

    2. 分为两种类型:

      • JDK动态代理:

        • 只能代理实现了接口的类。

        • 使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。

      • CGLIB动态代理:

        • 可以代理没有实现接口的类。

        • 通过继承目标类来实现代理。

        • 使用net.sf.cglib.proxy.Enhancer类和net.sf.cglib.proxy.MethodInterceptor接口实现。

补充示例:静态代理与动态代理

静态代理示例:

在Spring框架中,静态代理通常不通过XML配置来实现,因为静态代理涉及到手动创建代理类,而不是在运行时动态生成代理对象。在静态代理中,你会直接编写一个代理类,它实现与目标对象相同的接口,并在代理类中显式调用目标对象的方法。

由于这种方式并不涉及Spring的AOP特性,所以没有特定的XML配置来声明静态代理。

public interface UserService {
    void addUser();
    void deleteUser();
}

public class UserServiceImpl implements UserService {
    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }
}

public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public UserServiceProxy(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void addUser() {
        System.out.println("执行前置逻辑");
        userService.addUser();
        System.out.println("执行后置逻辑");
    }

    public void deleteUser() {
        System.out.println("执行前置逻辑");
        userService.deleteUser();
        System.out.println("执行后置逻辑");
    }
}
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">

    <!-- 声明原始的UserService实现 -->
    <bean id="userService" class="com.example.service.impl.UserServiceImpl"/>

    <!-- 声明UserService的静态代理类 -->
    <bean id="userServiceProxy" class="com.example.service.impl.UserServiceProxy">
        <!-- 注入原始的UserService -->
        <constructor-arg ref="userService"/>
    </bean>

</beans>
动态代理示例:
  JDK动态代理

  JDK动态代理,也称为Java动态代理,是基于接口的代理方式,使用Java自带的代理机制来实现。

  • 原理: 通过实现目标对象的接口并在调用处理器(InvocationHandler)中定义拦截逻辑。

  • 使用场景: 适用于目标对象实现了接口的情况。

  • 示例代码:

public interface Teacher {
    void teach();
}
public class Wang implements Teacher {
    @Override
    public void teach() {
        System.out.println("老师讲课");
    }
}
/**
 * 代理对象的执行逻辑
 */
public class MyHandler implements InvocationHandler {

    Teacher teacher;

    public MyHandler(Teacher teacher) {
        this.teacher = teacher;
    }

    /**
     * Object proxy: 代理对象
     * Method method: 目标方法
     * Object[] args: 方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*
           // 前置 公共的功能
           System.out.println("上课前 - 检查手机");
           // 核心业务  -  执行目标对象的业务
           teacher.teach();
           // 后置 公共的功能
           System.out.println("放学后 - 解决问题");
         */
        System.out.println("上课前 - 检查手机");

        // 执行代理对象的业务逻辑
        Object result = method.invoke(teacher, args);

        System.out.println("放学后 - 解决问题");

        // 代理对象的方法返回值
        return result;
    }
}

 

XML配置

使用JDK动态代理时,通常不需要特别指定,因为这是Spring的默认行为。

但可以通过设置[aop:aspectj-autoproxy](aop:aspectj-autoproxy)标签的proxy-target-class属性为false来明确指示使用JDK动态代理。

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

    <!-- 管理目标类 -->
    <bean id="userService" class="com.example.service.impl.UserServiceImpl"/>

    <!-- 管理切面类 -->
    <bean id="myAspect" class="com.example.aspect.MyAspect"/>

    <!-- AOP配置 -->
    <aop:config>
        <aop:aspect id="aspect" ref="myAspect">
            <!-- 配置通知和切点 -->
            <!-- 示例: 前置通知 -->
            <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
            <!-- 其他通知配置 -->
        </aop:aspect>
    </aop:config>

    <!-- 明确指定使用JDK动态代理 -->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>
CGLIB动态代理:

CGLIB(Code Generation Library)代理是一种基于类的代理方式,能够代理没有实现接口的类。

  • 原理: 通过继承目标类并在方法拦截器(MethodInterceptor)中定义拦截逻辑。

  • 使用场景: 适用于目标对象没有实现任何接口的情况。

  • 示例代码:

/**
 * 目标对象 - 被代理对象
 */
public class JayZhou {

    public void teach() {
        System.out.println("老师讲课!");
    }

}
public class MyHandler implements MethodInterceptor {

    /**
     * @param proxy 代理对象
     * @param method 被拦截的方法
     * @param args argument 方法参数
     * @param methodProxy 用于执行父类的方法
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("上课前检查手机");

        // 执行目标对象的方法   super.method
        Object result = methodProxy.invokeSuper(proxy, args);

        System.out.println("放学后解决问题");

        return result;
    }
}
XML配置

要使用CGLIB代理,您需要将[aop:aspectj-autoproxy](aop:aspectj-autoproxy)标签的

proxy-target-class属性设置为true

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

    <!-- 管理目标类 -->
    <bean id="userService" class="com.example.service.impl.UserServiceImpl"/>

    <!-- 管理切面类 -->
    <bean id="myAspect" class="com.example.aspect.MyAspect"/>

    <!-- AOP配置 -->
    <aop:config>
        <aop:aspect id="aspect" ref="myAspect">
            <!-- 配置通知和切点 -->
            <!-- 示例: 前置通知 -->
            <aop:before method="beforeAdvice" pointcut="execution(* com.example.service.*.*(..))"/>
            <!-- 其他通知配置 -->
        </aop:aspect>
    </aop:config>

    <!-- 明确指定使用CGLIB代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

JDK代理和CGLIB代理的比较

  • 应用场景:

    • JDK代理只适用于接口方法。

    • CGLIB可以代理类方法,无需接口。

  • 性能:

    • CGLIB通常性能更优,直接操作字节码。

    • JDK代理使用反射,性能略逊。

  • 实现方式:

    • JDK代理使用Java原生反射。

    • CGLIB通过字节码生成。

无论是使用JDK代理还是CGLIB代理,Spring的配置方式基本相同,主要区别在于[aop:aspectj-autoproxy](aop:aspectj-autoproxy)标签的proxy-target-class属性的设置。


AOP的用途

  1. 横切关注点

    1. AOP允许将应用程序中跨越多个点的功能(如日志、事务管理、安全等)模块化为特殊的类,称为"切面"(Aspects)。

    2. 这些横切关注点通常与业务逻辑无关,但对多个模块都有影响。

  2. 解耦

    1. 通过将非业务代码(如日志和安全)从业务代码中分离,AOP有助于降低模块间的耦合度。

    2. 提高了代码的可维护性和可重用性。

Spring AOP 关键术语和概念

  1. JoinPoint(连接点): 指那些可能被拦截到的点。在Spring AOP中,这通常指的是方法的执行。

  2. PointCut(切点): 指明哪些JoinPoint(方法)需要被拦截的规则集合。切点的表达式决定了哪些方法会被增强。

  3. Advice(通知/增强): 绑定到特定JoinPoint(通过PointCut选择)上的动作。常见的类型有:

    1. Before(前置通知)

    2. After Returning(后置通知)

    3. After Throwing(异常通知)

    4. After(最终通知)

    5. Around(环绕通知)

  4. Target(目标对象): 包含JoinPoint的对象。它是被代理的对象。

  5. Introduction(引介): 用于给类添加新的方法和属性的AOP概念。

  6. Proxy(代理): 为Target对象提供的代理,用于拦截对Target对象的调用。

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

  8. Aspect(切面): 通知(Advice)和切点(PointCut)的结合。切面定义了何时(切点)和如何(通知)进行跨越应用程序多个点的行为。

依赖:

        <!--aspectJ的依赖-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

Spring AOP与AspectJ密切相关,但在实际使用中,Spring AOP通常被认为是AspectJ的简化版,专注于方法拦截。

xml演示:

<!--管理目标类-->
    <bean id="userService" class="com.xq.service.impl.UserServiceImpl"></bean>

    <!--管理切面类-->
    <bean id="myAspect" class="com.xq.aspect.MyAspect"></bean>

    <!--
      配置目标类和切面类
    -->
    <aop:config>
        <!--
           配置切点
           aop:pointcut  配置切点的标签
                id: 切点的名称 自定义的 唯一即可
                expression: 切点表达式
                     execution: 切点表达式的固定写法  切点方法的形参类型 包名+类名+方法名(参数类型)
                                * 切点方法的返回值任意
                                (..) 代表的是切点方法的参数任意
        -->
        <aop:pointcut id="p1" expression="execution(* com.xq.service.impl.UserServiceImpl.deleteUser(..))"></aop:pointcut>
        <aop:pointcut id="p2" expression="execution(* com.xq.service.impl.UserServiceImpl.findAll(..))"></aop:pointcut>
        <aop:pointcut id="p3" expression="execution(* com.xq.service.impl.UserServiceImpl.updateUser(..))"></aop:pointcut>
        <aop:pointcut id="p4" expression="execution(* com.xq.service.impl.UserServiceImpl.addUser(..))"></aop:pointcut>
        <!--
          配置代理对象 将通知应用到切点方法上
          aop:aspect标签  配置代理对象
               ref: 引用的就是切面类的id
        -->
        <aop:aspect ref="myAspect">
            <!--
               aop:before 前置通知的配置
                   method: 增强的方法的名称
                   pointcut-ref:引用哪一个切点
            -->
            <aop:before method="checkPrivilege" pointcut-ref="p1"></aop:before>
            <!--
               后置通知的配置
               aop:after-returning: 描述后置通知的标签
            -->
            <aop:after-returning method="printLog" pointcut-ref="p2"></aop:after-returning>

            <!--
               配置环绕通知
               aop:around  环绕通知的标签
            -->
            <aop:around method="around" pointcut-ref="p3"></aop:around>

            <!--
              抛出异常通知
              aop:after-throwing 抛出异常通知的标签
            -->
            <aop:after-throwing method="throwing" pointcut-ref="p4"></aop:after-throwing>

            <!--
               最终通知的标签 aop:after
            -->
            <aop:after method="after" pointcut-ref="p4"></aop:after>
        </aop:aspect>
    </aop:config>

注解:

  别忘了先对切面类扛注释

  @Aspect注解标记了一个类作为切面。

  @Before, @AfterReturning, @Around, @AfterThrowing, 和 @After 注解分别用于定义不同类型的通知。

@Component
@Aspect //标识当前类是一个切面类

  逐条注解:



/**
 * 切面类
 */
@Component
@Aspect //标识当前类是一个切面类
public class MyAspect {

   

    //权限校验的方法  @Before注解: 前置通知的注解
    @Before(value = "execution(* com.xq.service.impl.UserServiceImpl.deleteUser(..))")
    public void checkPrivilege(){
        System.out.println("开启了权限校验......");
    }

    //打印日志的方法 @AfterReturning注解  后置通知的注解
    @AfterReturning(value = "execution(* com.xq.service.impl.UserServiceImpl.findAll(..))")
    public void printLog(){
        System.out.println("开启了日志打印功能");
    }

    //开启环绕通知的方法 环绕通知:就是在目标方法前后都执行的方法  @Around 环绕通知的注解
    @Around(value = "execution(* com.xq.service.impl.UserServiceImpl.updateUser(..))")
    public void around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("开启了环绕通知1");
            joinPoint.proceed();
            System.out.println("开启了环绕通知2");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    //抛出异常通知 只有目标方法出现异常之后,才会执行的增强方法 @AfterThrowing 抛出异常通知的注解
    @AfterThrowing(value = "execution(* com.xq.service.impl.UserServiceImpl.addUser(..))")
    public void throwing(){
        System.out.println("抛出了运行时异常....");
    }

    //最终通知 不管目标方法有没有出现异常 都会执行该增强方法
    @After(value = "execution(* com.xq.service.impl.UserServiceImpl.addUser(..))")
    public void after(){
        System.out.println("最终通知的方法执行了.....");
    }
}

  抽象方法注解:

  • @Pointcut 注解用于定义切点表达式。

  • 其他注解(如 @Before, @Around 等)则用于将方法标记为特定类型的通知,并通过它们的value属性指向对应的切点。



/**
 * 切面类
 */
@Component
@Aspect //标识当前类是一个切面类
public class MyAspect {

    //定义切点
    @Pointcut(value = "execution(* com.xq.service.impl.UserServiceImpl.deleteUser(..))")
    public void pointCut1(){

    }

    //权限校验的方法  @Before注解: 前置通知的注解
    //@Before(value = "execution(* com.xq.service.impl.UserServiceImpl.deleteUser(..))")
    @Before(value = "pointCut1()")
    public void checkPrivilege(){
        System.out.println("开启了权限校验......");
    }

}

applicationContext.xml配置:

applicationContext.xml文件中的配置启用了基于注解的AOP。这样,Spring可以自动检测带有@Aspect注解的类,并根据其中定义的注解来配置AOP。

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

    <!--
       开启包扫描
    -->
    <context:component-scan base-package="com.xq"></context:component-scan>

    <!--
      开启spring对aop的注解支持
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

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

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

相关文章

四、Flask学习之JavaScript

四、Flask学习之JavaScript JavaScript&#xff0c;作为一种前端脚本语言&#xff0c;赋予网页生动的交互性和动态性。通过它&#xff0c;开发者能够操作DOM&#xff08;文档对象模型&#xff09;实现页面元素的动态改变、响应用户事件&#xff0c;并借助AJAX技术实现异步数据…

omron adept控制器维修SmartController EX

欧姆龙机器人adept运动控制器维修SmartController EX 19300-000 维修范围&#xff1a;姆龙机器人&#xff1b;码垛机器人&#xff1b;搬运机器人&#xff1b;焊机机器人&#xff1b;变位机等。 Adept Viper s650/s850用于装配、物料搬运、包装和机械装卸&#xff0c;循环周期短…

基于YOLOv8深度学习的102种花卉智能识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

dom-to-image-more 使用

与网上不同的使用方式&#xff1a; 官网 dom-to-image-more - npm 这里不会出现两行缩略不行的bug yarn add dom-to-image-more 下面 生成图片并下载图片 const picture ref() const dom2img () > {var node picture.valuedomtoimage.toPng(node, { cacheBust: t…

iou的cpu和gpu源码实现

本专栏主要是深度学习/自动驾驶相关的源码实现,获取全套代码请参考 简介 IoU&#xff08;Intersection over Union&#xff09;是一种测量在特定数据集中检测相应物体准确度的一个标准&#xff0c;通常用于目标检测中预测框&#xff08;bounding box&#xff09;之间准确度的…

Arduino U8g2库:图形界面库的强大利器,

Arduino U8g2库&#xff1a;图形界面库的强大利器 介绍 在Arduino世界中&#xff0c;图形界面的显示通常是一项关键的任务。为了简化这个过程&#xff0c;提高开发效率&#xff0c;许多库被开发出来&#xff0c;其中U8g2库就是其中之一。U8g2库是一个功能强大的图形库&#x…

uniapp复选框 实现排他选项

选择了排他选项之后 复选框其他选项不可以选择 <view class"reportData" v-for"(val, index) in obj" :key"index"> <view v-if"val.type 3" ><u-checkbox-group v-model"optionValue" placement"colu…

web系统服务器监控检查

一、检查操作系统是否存在增减文件&#xff0c;是否有shell被上传 要检查操作系统是否存在增减文件或是否有shell被上传&#xff0c;您可以按照以下步骤进行操作&#xff1a; 文件完整性检查&#xff1a; 使用文件系统的完整性检查工具&#xff0c;例如fsck&#xff08;对于ext…

项目一:踏上Java开发之旅

文章目录 一、实战概述二、实战步骤任务1&#xff1a;安装配置JDK并开发第一个Java程序步骤一&#xff1a;安装JDK步骤二&#xff1a;配置JDK环境变量步骤三&#xff1a;开发第一个Java程序 课堂练习任务1、打印个人信息任务2、打印直角三角形任务3、打印一颗爱心任务4、打印史…

git:使用git rebase合并多次commit为一个

git log&#xff1a;找到需要合并的最早 commit 的父级 git rebase -i 73a5cd8597除第一个 pick 外&#xff0c;将其它改成 s&#xff0c;改完后保存退出 保存完后弹出 commit message 合并提示&#xff0c;根据这次合并的目的&#xff0c;重写commit message&#xff0c;改完后…

软考复习之软件工程篇

软件生命周期 问题定义&#xff1a;要示系统分析员与用户进行交流&#xff0c;弄清”用户需要计算机解决什么问题”然后提出关于“系统目标与范围的说明”&#xff0c;提交用户审查和确认 可行性研究&#xff1a;一方面在于把待开发的系统的目标以明确的语言描述出来&#xf…

httpClient忽略https的证书认证

忽略https证书认证代码: /*** 创建模拟客户端&#xff08;针对 https 客户端禁用 SSL 验证&#xff09;* return* throws Exception*/public static CloseableHttpClient createHttpClientWithNoSsl() throws Exception {// Create a trust manager that does not validate cer…

【C++】初步认识基于C的优化

C祖师爷在使用C语言时感觉到了不方便的一些点&#xff0c;于是一步一步改进优化&#xff0c;最后形成了C 本文将盘点一下基于C的语法优化 目录 命名空间&#xff1a;命名空间定义&#xff1a;命名空间使用&#xff1a; C输入&输出&#xff1a;cout&#xff1a;endl&#…

司铭宇老师:门店服装销售技巧培训:卖衣服销售方法和技巧

门店服装销售技巧培训&#xff1a;卖衣服销售方法和技巧 在服装零售行业&#xff0c;销售方法和技巧对于提升销售业绩和增强顾客满意度至关重要。一个成功的销售人员需要掌握如何吸引顾客、如何展示商品、如何促成交易等多方面的技能。以下是关于卖衣服的销售方法和技巧的详细…

ai智能写作软件有分享吗?分享4款解放双手的软件!

随着人工智能技术的不断发展&#xff0c;AI智能写作软件逐渐成为内容创作者们的新宠。这些软件不仅能够帮助我们快速生成高质量的文本内容&#xff0c;还能在优化搜索引擎排名等方面发挥重要作用。本文将为大家介绍几款常用的AI智能写作软件&#xff0c;让您轻松提升内容创作效…

如何在飞书创建企业ChatGPT智能问答助手应用并实现公网远程访问(1)

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…

Unity | 渡鸦避难所-8 | URP 中利用 Shader 实现角色受击闪白动画

1. 效果预览 当角色受到攻击时&#xff0c;为了增加游戏的视觉效果和反馈&#xff0c;可以添加粒子等动画&#xff0c;也可以使用 Shader 实现受击闪白动画&#xff1a;受到攻击时变为白色&#xff0c;逐渐恢复为正常颜色 本游戏中设定英雄受击时播放粒子效果&#xff0c;怪物…

pytorch实战-6手写数字加法机-迁移学习

1 概述 迁移学习概念&#xff1a;将已经训练好的识别某些信息的网络拿去经过训练识别另外不同类别的信息 优越性&#xff1a;提高了训练模型利用率&#xff0c;解决了数据缺失的问题&#xff08;对于新的预测场景&#xff0c;不需要大量的数据&#xff0c;只需要少量数据即可…

IP代理可以保护信息安全吗?

“随着互联网的普及和发展&#xff0c;网络安全问题已经成为众多企业和个人所面临的严峻挑战。保护信息安全已成为企业的核心竞争力之一&#xff0c;而IP代理正成为实现这一目标的有效手段。” 一、IP代理真的可以保护用户信息安全吗&#xff1f; IP代理作为一种网络工具&…

CSS基本知识总结

目录 一、CSS语法 二、CSS选择器 三、CSS样式表 1.外部样式表 2.内部样式表 3.内联样式 四、CSS背景 1.背景颜色&#xff1a;background-color 2.背景图片&#xff1a;background-image 3.背景大小&#xff1a;background-size 4.背景图片是否重复&#xff1a;backg…