4 Spring AOP

目录

AOP 简介

传统开发模式

先来看一个需求

解决方案

AOP 图示

Spring 启用 AspectJ 基于 xml 配置

创建 pom.xml

创建 UserService 借口和 UserServiceImpl实现类

创建 LogAdvice 日志通知

创建 log4j.properties

重点:创建 spring-context-xml.xml 配置

创建 AopTest_Xml 测试类

Spring 启用 AspectJ 基于注解配置

改造实现类 UserServiceImpl ,加上 @Service 注解

改造 LogAdvice 日志通知,加上 @Component 和 @Aspect 注解

创建 spring-context-anno.xml 注解配置

创建 AopTest_Anno 测试类


AOP 简介

  1. AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程) 的补充。
  2. AOP 的主要编程对象是切面(aspect),而切面是模块化的横切关注点。
  3. 在应用 AOP 编程时, 仍然需要定义公共功能,但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。
  4. AOP 的好处:(1) 每个事物逻辑位于一个位置, 代码不分散,便于维护和升级;(2) 业务模块更简洁,只包含核心业务代码。

传统开发模式

先来看一个需求

下面是分别定义了一个接口和它的实现类,功能非常简单,实现正整数的加、减、乘、除操作,代码如下:

CalculateService.java

package com.springdemo.service;

/**
 * Created by qfxl on 2024/05/03.
 */
public interface CalculateService {

    int add(int a, int b);

    int subtract(int a, int b);

    int times(int a, int b);

    int divide(int a, int b);
}

CalculateServiceImpl.java

package com.springdemo.service.impl;

import com.springdemo.service.CalculateService;
import org.springframework.stereotype.Component;

/**
 * Created by qfxl on 2024/05/03.
 */
@Component
public class CalculateServiceImpl implements CalculateService {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }

    @Override
    public int times(int a, int b) {
        return a * b;
    }

    @Override
    public int divide(int a, int b) {
        return a / b;
    }
}

我们的额外需求是:

需求1-做日志:在程序执行期间追踪正在发生的活动
需求2-做验证:希望计算器只能处理正数的运算

如果我们采用传统的方式,只能在每个方法里面先做参数的验证,然后再进行日志记录,这样不仅代码,冗余,而且不利于后期的维护,每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。

以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块(方法)里多次重复相同的日志代码。 如果日志需求发生变化, 必须修改所有模块。

类似于这样:

@Component
public class CalculateServiceImpl implements CalculateService {

    private static final Logger LOGGER = Logger.getLogger(CalculateServiceImpl.class);

    @Override
    public int add(int a, int b) {
    	//先进行参数有效性的验证
    	if(a < 0 || b < 0) {
            System.out.println("参与计算的两个整数有一个为负数: "+a+","+b);
            return -1;
        }
        //做日志
        LOGGER.debug("add操作之前日志...");
        //计算
        int c = a + b;
        LOGGER.debug("add操作之后日志...");
        //返回
        return c;
    }
    //....
}

可以看出,在 add 方法中插入了验证和日志的功能,但是,此方法的核心只是做加法操作,我们在业务实现时还要考虑一些共性的东西,这显然是不好的一种方式,那如何在不改变业务类实现的基础上,动态添加验证、日志功能呢?

解决方案

一种比较好的方式是:使用动态代理解决上述问题
代理设计模式的原理:

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。
任何对原始对象的调用都要通过代理。 代理对象决定是否以及何时将方法调用转到原始对象上。
关于使用程序编写动态代理代码,自己可以去查看相关文档,我们这里不做介绍。
实际上,Spring 中的 AOP 就是基于动态代理技术来实现的,下面我们来详细讲解 AOP 。

AOP 图示

我们把上面的需求以图的方式呈现出来,可以清晰地看到共性的操作,把共性的地方抽取出来,模块化成切面,然后让这个切面按我们的预期进行工作,这就是 AOP 的作用。

1

上面的图示中,我们抽取出来共性,也就是验证和日志功能,但是,光有这个共性,还是不能够模块化成切面的,它还需要其它的辅助元素才能成为一个切面。具体如何使用 AOP 完成上述的需求,Spring 给出了具体的方案,下面让我们来看看详细的案例。

Spring 启用 AspectJ 基于 xml 配置

在演示案例前我们先来了解些 AOP 术语:

  • Aspect 中文意思:切面,它只是一个统称,一种名词,它由 Advice 和 PointCut 组成;
  • Advice 中文意思:通知,它是切面的代码载体,可以想象成一个封装好的 JAVA 类,把共性代码封装在这里;
  • PointCut 中文意思:切点,它是用来匹配目标方法的一段配置信息,可以使用注解也可以使用 XML 来配置,它由多个JoinPoint 组成;
  • JoinPoint 中文意思:连接点,它是指匹配到目标方法之后执行 Advice 的时机,共有如下5种:
    • before: 在目标方法执行之前
    • after:在目标方法之后
    • afterThrowing: 在目标方法的 catch 块中
    • afterReturn: 在目标方法的 finally 块中
    • around: 环绕,相当于是上面4种的一个综合体
  • Weave 中文织入:它是一个过程,就是切面织入到目标对象方法的过程,它的原理是基于 代理 来实现的,Spring 目前支持两种代理:
    • 基于 JDK 自带的动态代理类,java.lang.reflect.Proxy,这是默认的情况,但是这个实现有一个前提条件是目标对象必需要有实现的接口。
    • 基于第三方组件 CGLIB 来生成我们的代理类,它不需要目标对象实现任何接口。

创建 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>spring-aop-demo</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <spring.version>5.1.5.RELEASE</spring.version>
        <lombok.version>1.16.20</lombok.version>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <slf4j.version>1.7.25</slf4j.version>
    </properties>

    <dependencies>

        <!-- Spring Begin -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework</groupId>-->
<!--            <artifactId>spring-aop</artifactId>-->
<!--            <version>${spring.version}</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Spring End -->

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>

        <!-- lombok Begin -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- lombok End -->

        <!-- Log Begin -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!-- Log End -->

    </dependencies>

    <build>
        <plugins>
            <!-- Compiler 插件, 设定 JDK 版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

创建 UserService 借口和 UserServiceImpl实现类

package com.example.spring.aop.demo.xml.service;

public interface UserService {

    public void addUser();

    public boolean updateUser();

    public int deleteUser(int id);
}
package com.example.spring.aop.demo.xml.service.impl;

import com.example.spring.aop.demo.xml.service.UserService;

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

    @Override
    public boolean updateUser() {
        System.out.println("updateUser:修改用户!!!");
        return true;
    }

    @Override
    public int deleteUser(int id) {
        System.out.println("deleteUser:删除用户!!!");
        return id;
    }

}

 

创建 LogAdvice 日志通知

package com.example.spring.aop.demo.xml.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;

public class LogAdvice {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAdvice.class);

    public void beforeMethod(JoinPoint joinPoint) {
        LOGGER.trace("前置通知...");
        System.out.println("本次要调用的目标对象:" + joinPoint.getTarget());
        System.out.println("本次要调用的目标方法名:" + joinPoint.getSignature().getName());
        System.out.println("本次要调用的目标方法参数:" + Arrays.toString(joinPoint.getArgs()));
    }

    public void afterReturn(Object obj) {
        LOGGER.trace("目标方法返回后, 返回对象结果是:" + obj);
    }

    public void afterMethod() {
        LOGGER.trace("后置通知...");
    }


    public Object aroundMethod(ProceedingJoinPoint pjp){
        Object result = null;

        LOGGER.warn("前置通知!!!");

        try {
            result = pjp.proceed();
        } catch (Throwable e) {
//            e.printStackTrace();

            LOGGER.warn("异常通知!!!");

            throw new RuntimeException(e);
        }

        LOGGER.warn("后置通知!!!");

        return result;
    }
}

创建 log4j.properties

log4j.rootLogger=INFO, console, file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %p [%c] - %m%n

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=logs/log.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.A3.MaxFileSize=1024KB
log4j.appender.A3.MaxBackupIndex=10
log4j.appender.file.layout.ConversionPattern=%d %p [%c] - %m%n


# 便于控制台日志的识别,所以用TRACE日志级别
# 基于 xml 配置的日志打印
log4j.logger.com.example.spring.aop.demo.xml.advice =TRACE

# 基于注解配置的日志打印
log4j.logger.com.example.spring.aop.demo.anno.advice =TRACE

重点:创建 spring-context-xml.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" 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">
    
    <!-- 1.创建目标类 -->
    <bean id="userService" class="com.example.spring.aop.demo.xml.service.impl.UserServiceImpl"></bean>

    <!-- 2.创建切面类(通知对象) -->
    <bean id="logAdvice" class="com.example.spring.aop.demo.xml.advice.LogAdvice"></bean>

    <!-- 3.aop 编程 -->
    <aop:config>
        <!--
        <aop:aspect>:把切面类声明成 "切面",从而获取到通知(方法)
            ref:切面类引用
         -->
        <aop:aspect id="logAdviceAapect" ref="logAdvice">

            <!--
            <aop:pointcut>:声明一个切入点,所有的通知都可以使用
                id:名称,供所有通知引用;
                expression:切入点表达式
             -->
            <aop:pointcut id="log_pc"
                          expression="execution(* com.example.spring.aop.demo.xml.service.impl.UserServiceImpl.*(..))"/>

            <!--
             <aop:before>:前置通知,在目标方法执行前执行
                method:通知,方法名
                pointcut:切入点表达式,此表达式只能当前通知使用
                pointcut-ref:切入点引用,可以与其他通知共享切入点
             -->
            <aop:before method="beforeMethod" pointcut-ref="log_pc"/>

            <!--
            <aop:after-returning>:返回通知,返回通知在方法返回结果之后执行
                returning:通知方法返回参数的名称
             -->
            <aop:after-returning method="afterReturn" pointcut-ref="log_pc" returning="obj"/>

            <!--
            <aop:after>:后置通知,在目标方法执行后(无论是否发生异常),执行的通知
             -->
            <aop:after method="afterMethod" pointcut-ref="log_pc"/>

            <!--
            <aop:around>:环绕通知,环绕目标方法执行
                注意:
                    通知方法格式:public Object aroundMethod(ProceedingJoinPoint pjp){...}
                        返回值类型:"Object" ,必须要有返回值;返回目标方法执行之后的结果即调用 "pjp.proceed()" 的结果,否则会出现空指针异常
                        参数类型:"org.aspectj.lang.ProceedingJoinPoint",其是 "JoinPoint" 的子类
                        执行目标方法:"Object result = pjp.proceed();" ,并抛出异常
             -->
            <aop:around method="aroundMethod" pointcut-ref="log_pc" />
        </aop:aspect>
    </aop:config>

</beans>

创建 AopTest_Xml 测试类

package com.example.spring.aop.demo;

import com.example.spring.aop.demo.xml.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest_Xml {

    @Test
    public void xmlTest(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-context-xml.xml");
        UserService userService = (UserService) ac.getBean("userService");
        userService.addUser();
        System.out.println();

        boolean b = userService.updateUser();
        System.out.println();

        int i = userService.deleteUser(10);
        System.out.println();
    }

}

测试结果:

2

Spring 启用 AspectJ 基于注解配置

改造实现类 UserServiceImpl ,加上 @Service 注解

package com.example.spring.aop.demo.anno.service.impl;

import com.example.spring.aop.demo.xml.service.UserService;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("addUser:添加用户!!!");
    }

    @Override
    public boolean updateUser() {
        System.out.println("updateUser:修改用户!!!");
        return true;
    }

    @Override
    public int deleteUser(int id) {
        System.out.println("deleteUser:删除用户!!!");
        return id;
    }
}

改造 LogAdvice 日志通知,加上 @Component 和 @Aspect 注解

package com.example.spring.aop.demo.anno.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;


@Component
@Aspect
public class LogAdvice {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAdvice.class);

    /*
     * 声明一个公共切入点,所有的通知都可以使用
     *      @Pointcut(value = "execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))")
     *      等同于 xml 配置里的:
     *      <aop:pointcut id="log_pc" expression="execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))"/>
     */
    @Pointcut(value = "execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))")
    public void log_pc() {

    }

    @Before("log_pc()")
    public void beforeMethod(JoinPoint joinPoint) {
        LOGGER.trace("前置通知...");
        System.out.println("本次要调用的目标对象:" + joinPoint.getTarget());
        System.out.println("本次要调用的目标方法名:" + joinPoint.getSignature().getName());
        System.out.println("本次要调用的目标方法参数:" + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(value = "log_pc()", returning = "obj")
    public void afterReturn(Object obj) {
        LOGGER.trace("目标方法返回后, 返回对象结果是:" + obj);
    }

    @After("log_pc()")
    public void afterMethod() {
        LOGGER.trace("后置通知...");
    }


    /*
     * 声明一个切入点表达式,此表达式只能当前通知使用
     *      @Around("execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))")
     *      等同于 xml 配置里的:
     *      <aop:around method="aroundMethod" pointcut="execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))" />
     */
    @Around("execution(* com.example.spring.aop.demo.anno.service.impl.UserServiceImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object result = null;

        LOGGER.warn("前置通知!!!");

        try {
            result = pjp.proceed();
        } catch (Throwable e) {
//            e.printStackTrace();

            LOGGER.warn("异常通知!!!");

            throw new RuntimeException(e);
        }

        LOGGER.warn("后置通知!!!");

        return result;
    }
}

创建 spring-context-anno.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" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描全部注解 -->
    <context:component-scan base-package="com.example.spring.aop.demo.anno"/>

    <!-- 开启aop动态代理 -->
    <aop:aspectj-autoproxy/>

</beans>

创建 AopTest_Anno 测试类

package com.example.spring.aop.demo;

import com.example.spring.aop.demo.xml.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context-anno.xml")
public class AopTest_Anno {

    @Autowired
    private UserService userService;

    @Test
    public void annoTest() {
        userService.addUser();
        System.out.println();

        boolean b = userService.updateUser();
        System.out.println();

        int i = userService.deleteUser(10);
        System.out.println();
    }
}

测试结果:

3

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】第二门课 改善深层神经网络 Week 2 3 优化算法、超参数调试和BN及其框架

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

Go语言基础语法(一):变量的声明与赋值

四种声明 变量声明 Go语言是静态类型语言&#xff0c;因此变量&#xff08;variable&#xff09;是有明确类型的&#xff0c;编译器也会检查变量类型的正确性。在数学概念中&#xff0c;变量表示没有固定值且可改变的数。但从计算机系统实现角度来看&#xff0c;变量是一段或…

Simulink|【免费】虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型

主要内容 该模型为simulink仿真模型&#xff0c;主要实现的内容如下&#xff1a; 随着风力发电、光伏发电等新能源发电渗透率增加&#xff0c;电力系统的等效惯量和等效阻尼逐渐减小&#xff0c;其稳定性问题变得越来越严峻。虚拟同步发电机&#xff08;VSG&#xff09;技…

win10部署本地大模型langchain+ollama

一、环境 windows10、Python 3.9.18、langchain0.1.9 二、ollama下载 Download Ollama on Windows 0.1.33版本链接https://objects.githubusercontent.com/github-production-release-asset-2e65be/658928958/35e38c8d-b7f6-48ed-8a9c-f053d04b01a9?X-Amz-AlgorithmAWS4-H…

ubuntu搭建node私库Verdaccio

ubuntu搭建node私库Verdaccio Verdaccio 是一个轻量级的私有 npm 代理注册服务器&#xff0c;它是开源的&#xff0c;可以帮助你设置和维护企业内部的 npm 包的存储库。使用 Verdaccio 可以让你完全控制包的发布流程、依赖关系以及访问策略。这篇文章将指导你如何在 Ubuntu 系…

SQL注入漏洞扫描---sqlmap

what SQLMap是一款先进的自动执行SQL注入的审计工具。当给定一个URL时&#xff0c;SQLMap会执行以下操作&#xff1a; 判断可注入的参数。判断可以用哪种SQL注入技术来注入。识别出目标使用哪种数据库。根据用户的选择&#xff0c;读取哪些数据库中的数据。 更详细语法请参考…

领域驱动设计(DDD)笔记(三)后端工程架构

文章链接 领域驱动设计(DDD)笔记(一)基本概念-CSDN博客领域驱动设计(DDD)笔记(二)代码组织原则-CSDN博客领域驱动设计(DDD)笔记(三)后端工程架构-CSDN博客前导 领域驱动设计(Domain Driven Design,简称DDD)是业内主导的业务工程理论。它在各中权威人士被广泛讨论…

leetcode_43.字符串相乘

43. 字符串相乘 题目描述&#xff1a;给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 &q…

好用的AI工具推荐与案例分析

你用过最好用的AI工具有哪些&#xff1f; 简介&#xff1a;探讨人们在使用AI工具时&#xff0c;最喜欢的和认为最好用的工具是哪些&#xff0c;展示AI技术的实际应用和影响。 方向一&#xff1a;常用AI工具 在选择常用AI工具时&#xff0c;可以根据不同的应用场景和需求来挑选…

【1小时掌握速通深度学习面试3】RNN循环神经网络

目录 12.描述循环神经网络的结构及参数更新方式&#xff0c;如何使用神经网络对序列数据建模? 13.循环神经网络为什么容易出现长期依赖问题? 14.LSTM 是如何实现长短期记忆功能的? 15.在循环神经网络中如何使用 Dropout ? 16.如何用循环神经网络实现 Seg2Seq 映射? …

手撕vector的模拟实现

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

Vitis HLS 学习笔记--HLS眼中的完美循环嵌套

目录 1. 简介 2. 示例 2.1 不完美循环 2.2 完美循环 2.3 HLS 眼中的循环 3. 总结 1. 简介 在处理嵌套循环时&#xff08;HDL或HLS工具中&#xff09;&#xff0c;优化循环结构对于实现最佳性能至关重要。嵌套循环的性能优化直接影响着计算的时延和资源利用率。创建完美嵌…

光头强:IBM收购HashCorp (Terraform)有多大意义?

StrongBear公司在光头强强总以及合伙人熊大熊二的艰苦努力下&#xff0c;最近公司进了一次扩容。甚至将原来一些甲方的研发人员也拉入旗下&#xff0c;其中就包括与熊二共事多年的小玲子以及小强同学。 光头强也注意到最近在IT软件领域&#xff0c;频频发生一些并购事件。比如…

SAP_SD模块-销售批次策略应用记录

一、销售批次查找策略的重要性 批次查找策略允许企业在销售过程中根据预定义的规则自动选择最适合的产品批次。这种策略的实施&#xff0c;对企业尤其是那些涉及到严格产品质量与安全标准的行业&#xff08;如食品、药品及化工产品&#xff09;具有以下几方面的重要意义&#x…

不尝试一下?计算机领域两大赛事来了!!

前言 最近&#xff0c;熊二新来的同事小强比较关注国内的一些赛事信息。这不&#xff0c;近期有两大赛事。这两大赛事&#xff0c;主要还是面向高校学生的。一个是搞网络安全方向的: 第二届京麒CTF挑战赛&#xff0c;另一个是搞数据库方向的: 2024年全国大学生计算机系统能力大…

【大数据】学习笔记

文章目录 [toc]NAT配置IP配置SecureCRT配置PropertiesTerminal Java安装环境变量配置 Hadoop安装修改配置文件hadoop-env.shyarn-env.shslavescore-site.xmlhdfs-site.xmlmapred-site.xmlyarn-site.xml 环境变量配置 IP与主机名映射关系配置hostname配置映射关系配置 关闭防火墙…

基于Springboot的校运会管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校运会管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&a…

第2章 WebServer进阶

2.1 使用多线程处理多用户请求 2.1.1 多线程Socket通信 在上一章的案例中&#xff0c;服务端显然只能处理一次浏览器请求&#xff0c;请求一次浏览器端就结束程序。如何解决这个问题呢&#xff1f;可以采用多线程Socket通信技术&#xff0c;解决多用户并发请求。 在多线程Sock…

十四、网络编程

目录 一、二、网络通讯要素三、IP和端口号四、网络协议1、网络通信协议2、TCP/IP协议簇1&#xff09;TCP协议2&#xff09;UDP 3、Socket 五、TCP网络编程1、基于Socket的TCP编程1&#xff09;客户端创建socket对象2&#xff09; 服务器端建立 ServerSocket对象 2、UDP网络通信…

理想二极管LM74700QDBVRQ1

LM74700QDBVRQ1 防反接专用芯片 器件手册 应用参考&#xff08;下图是另外一个理想二极管应用电路图&#xff09; 这两款芯片的区别主要是工作电压范围不同&#xff08;实际应用是&#xff09; 电源远端电压补偿-CSDN博客https://blog.csdn.net/anlog/article/details/1338627…