Spring中AOP的使用以及举例

news2024/10/1 5:35:33

1.AOP的简介

1.1 什么是AOP?

  • AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。
    • OOP(Object Oriented Programming)面向对象编程

我们都知道OOP是一种编程思想,那么AOP也是一种编程思想,编程思想主要的内容就是指导程序员该如何编写程序,所以它们两个是不同的编程范式

1.2 AOP作用

  • 作用:在不惊动原始设计的基础上为其进行功能增强,前面咱们有技术就可以实现这样的功能即代理模式。

前面咱们有技术就可以实现这样的功能即代理模式

  • 简而言之 AOP就是在触发一个方法的同时 可以在此方法触发前或者触发后运行其他的方法 多数使用环绕通知,即围绕触发方法运行。

2.AOP的使用

2.1使用步骤

步骤1:添加依赖

pom.xml

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSYObAxz-1677416538545)(assets/1630146885493.png)]

  • 因为spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop
  • 导入AspectJ的jar包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以我们直接采用Spring整合ApsectJ的方式进行AOP开发。

步骤2:定义接口与实现类

环境准备的时候,BookDaoImpl已经准备好,不需要做任何修改

步骤3:定义通知类和通知

通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。

public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

类名和方法名没有要求,可以任意。

步骤4:定义切入点

BookDaoImpl中有两个方法,分别是save和update,我们要增强的是update方法,该如何定义呢?

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

说明:

  • 切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。
  • execution及后面编写的内容,后面会有章节专门去学习。

步骤5:制作切面

切面是用来描述通知和切入点之间的关系,如何进行关系的绑定?

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ky3AhsSX-1677416538546)(assets/1630148447689.png)]

说明:@Before翻译过来是之前,也就是说通知会在切入点方法执行之前执行,除此之前还有其他四种类型,后面会讲。

步骤6:将通知类配给容器并标识其为切面类

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

步骤7:开启注解格式AOP功能

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

步骤8:运行程序

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}

看到在执行update方法之前打印了系统时间戳,说明对原始方法进行了增强,AOP编程成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPV2xMJD-1677416538546)(assets/1630147945888.png)]

知识点1:@EnableAspectJAutoProxy

名称@EnableAspectJAutoProxy
类型配置类注解
位置配置类定义上方
作用开启注解格式AOP功能

知识点2:@Aspect

名称@Aspect
类型类注解
位置切面类定义上方
作用设置当前类为AOP切面类

知识点3:@Pointcut

名称@Pointcut
类型方法注解
位置切入点方法定义上方
作用设置切入点方法
属性value(默认):切入点表达式

知识点4:@Before

名称@Before
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行

3实战举例(2)

3.1 实战一:业务层接口执行效率

3.1.1 需求分析

这个需求也比较简单,前面我们在介绍AOP的时候已经演示过:

  • 需求:任意业务层接口执行均可显示其执行效率(执行时长)

这个案例的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化。

具体实现的思路:

(1) 开始执行方法之前记录一个时间

(2) 执行方法

(3) 执行完方法之后记录一个时间

(4) 用后一个时间减去前一个时间的差值,就是我们需要的结果。

所以要在方法执行的前后添加业务,经过分析我们将采用环绕通知

**说明:**原始方法如果只执行一次,时间太快,两个时间差可能为0,所以我们要执行万次来计算时间差。

3.1.2 环境准备

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

    <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.4</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.47</version>
        </dependency>
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.16</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.6</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.0</version>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
  • 添加AccountService、AccountServiceImpl、AccountDao与Account类

    public interface AccountService {
        void save(Account account);
        void delete(Integer id);
        void update(Account account);
        List<Account> findAll();
        Account findById(Integer id);
    }
    
    @Service
    public class AccountServiceImpl implements AccountService {
    
        @Autowired
        private AccountDao accountDao;
    
        public void save(Account account) {
            accountDao.save(account);
        }
    
        public void update(Account account){
            accountDao.update(account);
        }
    
        public void delete(Integer id) {
            accountDao.delete(id);
        }
    
        public Account findById(Integer id) {
            return accountDao.findById(id);
        }
    
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    }
    public interface AccountDao {
    
        @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
        void save(Account account);
    
        @Delete("delete from tbl_account where id = #{id} ")
        void delete(Integer id);
    
        @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
        void update(Account account);
    
        @Select("select * from tbl_account")
        List<Account> findAll();
    
        @Select("select * from tbl_account where id = #{id} ")
        Account findById(Integer id);
    }
    
    public class Account implements Serializable {
    
        private Integer id;
        private String name;
        private Double money;
        //setter..getter..toString方法省略
    }
    
  • resources下提供一个jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
    jdbc.username=root
    jdbc.password=root
    
  • 创建相关配置类

    //Spring配置类:SpringConfig
    @Configuration
    @ComponentScan("com.itheima")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    public class SpringConfig {
    }
    //JdbcConfig配置类
    public class JdbcConfig {
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String userName;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean
        public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(userName);
            ds.setPassword(password);
            return ds;
        }
    }
    //MybatisConfig配置类
    public class MybatisConfig {
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
            SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
            ssfb.setTypeAliasesPackage("com.itheima.domain");
            ssfb.setDataSource(dataSource);
            return ssfb;
        }
    
        @Bean
        public MapperScannerConfigurer mapperScannerConfigurer(){
            MapperScannerConfigurer msc = new MapperScannerConfigurer();
            msc.setBasePackage("com.itheima.dao");
            return msc;
        }
    }
    
    
  • 编写Spring整合Junit的测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class AccountServiceTestCase {
        @Autowired
        private AccountService accountService;
    
        @Test
        public void testFindById(){
            Account ac = accountService.findById(2);
        }
    
        @Test
        public void testFindAll(){
            List<Account> all = accountService.findAll();
        }
    
    }
    

最终创建好的项目结构如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfWCsAat-1677416831895)(assets/1630214631112.png)]

3.1.3 功能开发

步骤1:开启SpringAOP的注解功能

在Spring的主配置文件SpringConfig类中添加注解

@EnableAspectJAutoProxy

步骤2:创建AOP的通知类

  • 该类要被Spring管理,需要添加@Component

  • 要标识该类是一个AOP的切面类,需要添加@Aspect

  • 配置切入点表达式,需要添加一个方法,并添加@Pointcut

@Component
@Aspect
public class ProjectAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}
    
    public void runSpeed(){
        
    } 
}

步骤3:添加环绕通知

在runSpeed()方法上添加@Around

@Component
@Aspect
public class ProjectAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}
    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    @Around("servicePt()")
    public Object runSpeed(ProceedingJoinPoint pjp){
        Object ret = pjp.proceed();
        return ret;
    } 
}

**注意:**目前并没有做任何增强

步骤4:完成核心业务,记录万次执行的时间

@Component
@Aspect
public class ProjectAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}
    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp){
        
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
           pjp.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("业务层接口万次执行时间: "+(end-start)+"ms");
    } 
}

步骤5:运行单元测试类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Be4m6Cz-1677416831896)(assets/1630215355776.png)]

**注意:**因为程序每次执行的时长是不一样的,所以运行多次最终的结果是不一样的。

步骤6:程序优化

目前程序所面临的问题是,多个方法一起执行测试的时候,控制台都打印的是:

业务层接口万次执行时间:xxxms

我们没有办法区分到底是哪个接口的哪个方法执行的具体时间,具体如何优化?

@Component
@Aspect
public class ProjectAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}
    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp){
        //获取执行签名信息
        Signature signature = pjp.getSignature();
        //通过签名获取执行操作名称(接口名)
        String className = signature.getDeclaringTypeName();
        //通过签名获取执行操作名称(方法名)
        String methodName = signature.getName();
        
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
           pjp.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    } 
}

步骤7:运行单元测试类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQ2ixzaG-1677416831896)(assets/1630215743444.png)]

补充说明

当前测试的接口执行效率仅仅是一个理论值,并不是一次完整的执行过程。

这块只是通过该案例把AOP的使用进行了学习,具体的实际值是有很多因素共同决定的。

3.2 实现二:百度网盘密码数据兼容处理

3.2.1需求分析

  • 当我们在百度网盘链接输入密码的时候 有时候可能会复制到前后的空格 这时候就会显示密码错误 而这时我们需要使用环绕通知的功能在所复制的密码前后自动除去空格时期配对成功

3.2.2 环境准备

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

    <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.4</version>
        </dependency>
      </dependencies>
    
  • 添加ResourcesService,ResourcesServiceImpl,ResourcesDao和ResourcesDaoImpl类

    public interface ResourcesDao {
        boolean readResources(String url, String password);
    }
    @Repository
    public class ResourcesDaoImpl implements ResourcesDao {
        public boolean readResources(String url, String password) {
            //模拟校验
            return password.equals("root");
        }
    }
    public interface ResourcesService {
        public boolean openURL(String url ,String password);
    }
    @Service
    public class ResourcesServiceImpl implements ResourcesService {
        @Autowired
        private ResourcesDao resourcesDao;
    
        public boolean openURL(String url, String password) {
            return resourcesDao.readResources(url,password);
        }
    }
    
    
  • 创建Spring的配置类

    @Configuration
    @ComponentScan("com.itheima")
    public class SpringConfig {
    }
    
  • 编写App运行类

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
            boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root");
            System.out.println(flag);
        }
    }
    

最终创建好的项目结构如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZMqVNyJF-1677417246834)(assets/1630241681697.png)]

现在项目的效果是,当输入密码为"root"控制台打印为true,如果密码改为"root "控制台打印的是false

需求是使用AOP将参数进行统一处理,不管输入的密码root前后包含多少个空格,最终控制台打印的都是true。

3.2.3 具体实现

步骤1:开启SpringAOP的注解功能

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

步骤2:编写通知类

@Component
@Aspect
public class DataAdvice {
    @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")
    private void servicePt(){}
    
}

步骤3:添加环绕通知

@Component
@Aspect
public class DataAdvice {
    @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")
    private void servicePt(){}
    
    @Around("DataAdvice.servicePt()")
    // @Around("servicePt()")这两种写法都对
    public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = pjp.proceed();
        return ret;
    }
    
}

步骤4:完成核心业务,处理参数中的空格

@Component
@Aspect
public class DataAdvice {
    @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")
    private void servicePt(){}
    
    @Around("DataAdvice.servicePt()")
    // @Around("servicePt()")这两种写法都对
    public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
        //获取原始方法的参数
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++) {
            //判断参数是不是字符串
            if(args[i].getClass().equals(String.class)){
                args[i] = args[i].toString().trim();
            }
        }
        //将修改后的参数传入到原始方法的执行中
        Object ret = pjp.proceed(args);
        return ret;
    }
    
}

步骤5:运行程序

不管密码root前后是否加空格,最终控制台打印的都是true

步骤6:优化测试

为了能更好的看出AOP已经生效,我们可以修改ResourcesImpl类,在方法中将密码的长度进行打印

@Repository
public class ResourcesDaoImpl implements ResourcesDao {
    public boolean readResources(String url, String password) {
        System.out.println(password.length());
        //模拟校验
        return password.equals("root");
    }
}

再次运行成功,就可以根据最终打印的长度来看看,字符串的空格有没有被去除掉。

注意:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-36zPPyFe-1677417246835)(assets/1630242491831.png)]

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

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

相关文章

模块电源DC/DC直流隔离升压稳压HRB系列5v12v24v转50v100v110v200v220v250v300v400v

特点效率高达80%以上1*1英寸标准封装单电压输出稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上应用HRB 0.2~10W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、9~18V、及18~36VDC标准&#…

中小企业数字化自动化转型的方法

自动化是我们国内未来的趋势。智能制造的实现主要依托两个基础能力&#xff0c;一个是工业制造技术&#xff0c;另一个就是工业互联网。而自动化是工业制造技术的重要组成部分&#xff0c;是高度智能制造装备的核心部分&#xff0c;与承接着制造单元与工业互联网这两大核心。懂…

Polkadot 基础

Polkadot Polkadot联合并保护了一个不断增长的专业区块链生态系统&#xff0c;称为parachains。Polkadot上的应用程序和服务可以安全地跨链通信&#xff0c;形成真正可互操作的去中心化网络的基础。 真正的互操作性 Polkadot支持跨区块链传输任何类型的数据或资产&#xff0c;…

基于51单片机和proteus的智能调速风扇设计

此智能风扇是基于51单片机和proteus的仿真设计&#xff0c;功能如下&#xff1a; 1. Timer0 PWM控制电机转速 2. DHT11采集温湿度 3. LCD1602显示温湿度及电机状态 4. 按键控制电机加减速启停等 5. 串口控制电机加减速启停等 功能框图如下&#xff1a; Proteus仿真界面如下…

EasyExcel使用与详细说明,EasyExcel工具类

文章目录1.Apache POI1.1 学习使用成本较高1.2 POI的内存消耗较大1.3 特点2. 初识EasyExcel2.1 重写了POI对07版Excel的解析2.2 特点3.快速入门3.1 导入依赖坐标3.2 最简单的读3.2.1 需求、准备工作3.2.2 编写导出数据的实体3.2.3 读取Excel的监听器&#xff0c;用于处理读取产…

rabbitmq在linux系统下安装步骤

第一步&#xff1a;登录官网 官网地址&#xff1a;www.rabbitmq.com,点击Get Started 重要信息&#xff1a;RabbitMQ Tutorials手册&#xff0c;描述了工作模式 第二步&#xff1a;点击Download Installation下载 重要信息&#xff1a;rabbitmq是用erlang语言开发的&#xff0…

C++类与对象(上)【详析】

目录1.面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1访问限定符4.2封装5.类的作用域6.类的实例化7.类对象模型7.1 如何计算类对象的大小8.this关键字如果说我们对C的初步认识&#xff0c;是觉得C是对C语言不足之处的进行修补&#xff0c;在认识完类…

Vue-router 3.x 版本中路由守卫钩子函数解析

目录概念&#xff1a;分类全局前置守卫 &#xff08; router.beforeEach &#xff09;全局解析守卫 &#xff08; router.beforeResolve &#xff09;全局后置守卫 &#xff08; router.afterEach &#xff09;路由独享守卫 &#xff08; beforeEnter &#xff09;组件内的守卫…

有哪些办法可以降低 Redis 的内存使用情况

在逛知乎时&#xff0c;看到这样一个问题&#xff0c;觉得挺不错的&#xff0c;将自己个人的见解分享给大家。问题是:有哪些办法可以降低 Redis 的内存使用情况&#xff1f;个人也对Redis做了一个比较全面的问题汇总&#xff0c;希望对大家有所帮助。Redis面试题汇总要降低内存…

Git同时配置Github和Gitlab

电脑的git需要同时管理Github上自己的代码仓库和Gitlab的公司的代码仓库&#xff0c;所以记录同时配置两者的步骤。 第一步、清除已有的全局配置&#xff08;我之前只有github的配置&#xff09; git config --global --unset user.name git config --global --unset user.em…

【论文阅读总结】Mask R-CNN翻译总结

Mask R-CNN1.摘要Mask R-CNN相关介绍与优点2.引言3.文献综述3.1 R-CNN3.2 Instance Segmentation【实例分割】4. Mask R-CNN介绍4.1 Faster R-CNN(相关细节请看相关文章)4.2 Mask R-CNN4.3 Mask Representation【遮罩表示法】4.4 RoIAlign【感兴趣区域对齐】4.4.1 RoIPool【感兴…

软件测试面试问答

笔试 笔试的话我们需要揣测具体会考什么内容&#xff0c;我们可以通过招聘信息去了解该公司需要什么样的技能&#xff0c;以此来准备笔试。一般必考的内容会有编程&#xff0c;测试用例设计&#xff0c;工作流程&#xff0c;逻辑思维等内容&#xff0c;除此之外每个公司可能还会…

【设计模式】我终于读懂了模板方法模式。。。

&#x1f34e;豆浆制作问题 编写制作豆浆的程序&#xff0c;说明如下: 1.制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎 2)通过添加不同的配料&#xff0c;可以制作出不同口味的豆浆 3)选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样…

二、IA-32系列处理器 通用寄存器介绍

IA-32系列处理器 通用寄存器介绍 寄存器处理器内的特殊存储单元处理器内有多种不同用途的寄存器寄存器分别有各自的名称,以便表示及访问通用寄存器IA-32系列CPU有8个32位的通用寄存器(General-Purpose Registers)通用寄存器不仅能存储数据,而且能参与算术逻辑运算,还能给出…

OpenCV 图像轮廓检测

本文是OpenCV图像视觉入门之路的第15篇文章&#xff0c;本文详细的介绍了图像轮廓检测的各种操作&#xff0c;例如&#xff1a;轮廓检索模式、轮廓逼近算子等操作。 图像轮廓是具有相同颜色或灰度的连续点的曲线&#xff0c;轮廓在形状分析和物体的检测和识别中很有用。图像轮廓…

预训练BERT

与PTB数据集相比&#xff0c;WikiText-2数据集保留了原来的标点符号、大小写和数字&#xff0c;并且比PTB数据集大了两倍多。 我们可以任意访问从WikiText-2语料库中的一对句子生成的预训练&#xff08;遮蔽语言模型和下一句预测&#xff09;样本。 原始的BERT有两个版本&…

《Python机器学习》基础代码

1&#xff0c;要学习Python机器学习,第一步就是读入数据,这里我们以读入excel的数据为例,利用jupyter notebook来编码,具体教程看这个视频 推荐先上传到jupyter notebook,再用名字.xlsx来导入 Jupyter notebook导入Excel数据的两种方法介绍_哔哩哔哩_bilibili 2&#xff0c;…

MyBatis - 06 - MyBatis获取参数值的两种方式(重要)

文章目录1、回顾JDBC原生的获取参数值的方式2、MyBatis获取参数值的两种方式3、MyBatis获取参数值的五种情况情况1: Mapper接口方法的参数为单个字面量类型的参数ParameterMapper接口代码测试类代码ParameterMapper.xml配置方式1&#xff1a;${}ParameterMapper.xml配置方式2:#…

[8]云计算概念、技术与架构Thomas Erl-第九章 云管理机制|week9|10月29日

9.0引言 基于云的IT资源需要被建立、配置、维护和监控。 远程管理系统 资源管理系统 SLA管理系统 计费管理系统 这些系统通常会提供整合的API&#xff0c;并能够以个别产品、定制应用或者各种组合产品套装和多功能应用的形式提供给用户。 9.1 远程管理系统 远程管理系统…

「回顾RKDC2023」飞凌嵌入式受邀亮相第七届瑞芯微开发者大会

2023年2月23-24日&#xff0c;第七届瑞芯微开发者大会&#xff08;RKDC2023&#xff09;在福州隆重举行&#xff0c;飞凌嵌入式作为瑞芯微生态合作伙伴受邀参会&#xff0c;并与数千名开发者科技公司代表及行业领袖共同聚焦行业新兴产品需求&#xff0c;探讨新硬件发展趋势&…