spring笔记下(AOP、事务管理)

news2024/11/28 19:02:15

一、AOP概述


1. AOP介绍


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

AOP作用:在不惊动原始设计的基础上为其进行功能增强(简单的说就是在不改变方法源代码的基础上对方法进行功能增强)。

Spring理念:无入侵式/无侵入式。(没有修改源代码,可插拔)


AOP中的核心概念

在这里插入图片描述

连接点(JoinPoint):在spring中可以认为是类中的方法,例如:update()、delete()等。

切入点(Pointcut):进行功能增强的方法,例如:update()、delete()方法,select()方法没有被增强所以不是切入点,但是是连接点。

  • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法:

    • 一个具体方法:com.baidou.dao包下的BookDao接口中的无形参无返回值的save方法。

    • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法。

通知(Advice):在切入点前后执行的操作,也就是增强的共性功能。

  • 在SpringAOP中,功能最终以方法的形式呈现。

通知类:通知方法所在的类叫做通知类。

切面(Aspect):描述通知与切入点的对应关系,也就是哪些通知方法对应哪些切入点方法。

在这里插入图片描述

//底层动态代理


2. AOP快速入门


案例:在执行某个方法之前输出当前系统时间。(基于注解实现,xml配置没有注解直观)

1、导入依赖坐标(pom.xml)

<dependencies>
    <!--spring核心依赖,会将spring-aop传递进来-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
    <!--aop框架,简化aop开发-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

在这里插入图片描述


2、定义dao接口和实现类

package com.baidou.dao;

public interface BookDao {
    public void save();
    public void update();
}
package com.baidou.dao.impl;

import com.baidou.dao.BookDao;

@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println(System.currentTimeMillis());//手动添加一个时间输出
        System.out.println("book dao save ...");
    }

    public void update() { //一会对此方法进行增强
        System.out.println("book dao update ...");
    }
}

3、定义通知类,制作通知方法

package com.baidou.aop;

import org.springframework.stereotype.Component;

//通知类必须配置成Spring管理的bean
@Component
public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

4、定义切入点表达式、配置切面(绑定切入点与通知关系)

@Component //通知类必须配置成Spring管理的bean
@Aspect //设置当前类为切面类
public class MyAdvice {

    //设置切入点,@Pointcut注解要求配置在方法上方
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")//切入点表达式
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

在这里插入图片描述


5、在spring配置类中进行Spring注解包扫描、以及开启AOP功能。

@Configuration
@ComponentScan("com.baidou")
@EnableAspectJAutoProxy //开启注解开发AOP功能
public class SpringConfig {
}

6、测试

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

在这里插入图片描述

3. AOP工作流程


底层使用动态代理实现。

1、Spring容器启动。

2、读取所有切面配置中的切入点。

3、初始化bean,判定bean对应的类中的方法是否匹配到任意切入点:

  • 匹配失败,创建原始对象。
  • 匹配成功,创建原始对象(目标对象)的代理对象。

4、获取bean执行方法:

  • 获取的bean是原始对象时,调用方法并执行,完成操作。
  • 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作。(切入点)

目标对象(Target):被代理的对象,也叫原始对象,该对象中的方法没有任何功能增强。
代理对象(Proxy):代理后生成的对象,由Spring帮我们创建代理对象。


4. AOP切入点表达式


4.1 语法格式


1、切入点:要进行增强的方法。

2、切入点表达式:要进行增强的方法的描述方式:(能定位到增强方法的表达式)

  • 描述方式一:执行com.baidou.dao包下的BookDao接口中的无参数update方法;(推荐在接口方法中增强)
execution(void com.baidou.dao.BookDao.update())
    
// execution:执行
  • 描述方式二:执行com.baidou.dao.impl包下的BookDaoImpl类中的无参数update方法;
execution(void com.baidou.dao.impl.BookDaoImpl.update())

3、切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)

execution(public User com.baidou.service.UserService.findById(int))
  • 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点。
  • 访问修饰符:public,private等,可以省略。
  • 返回值:写返回值类型。
  • 包名:多级包使用点连接。
  • 类/接口名。
  • 方法名。
  • 参数:直接写参数的类型,多个类型用逗号隔开。
  • 异常名:方法定义中抛出指定异常,可以省略。

4.2 通配符


可以在切点表达式中使用通配符快速描述切入点。

1、*单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现。

示例:匹配com.baidou包下的任意包中的UserService类或接口中所有find开头带有一个参数的方法

execution(public * com.baidou.*.UserService.find*(*)) 

2、..多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

示例:匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法

execution(public User com..UserService.findById(..))

3、+专用于匹配子类类型

execution(* *..*Service+.*(..))
//以Service结尾的子类或子接口    

@Component
@Aspect
public class MyAdvice {
    //切入点表达式:
    //@Pointcut("execution(void com.baidou.dao.BookDao.update())")  //标准格式
    //@Pointcut("execution(* com.baidou.dao.BookDao.update())")     //返回值任意
    //@Pointcut("execution(* com.baidou.dao.BookDao.update(*))")    //有一个参数的update
    //@Pointcut("execution(* com.baidou.dao.BookDao.update(..))")   //任意参数个数的update
    //@Pointcut("execution(* com.baidou.dao.*.update(..))")         //dao包下任意类的任意参数个数的update方法
    //@Pointcut("execution(* com.baidou.dao.*.u*(..))")      //dao包下任意类的任意参数个数的以"u"开头方法
    //@Pointcut("execution(* com.baidou.dao.*.*e(..))")      //dao包下任意类的任意参数个数的以"e"结尾方法
    //@Pointcut("execution(* com.baidou.dao..*.*e(..))")     //dao包下的所有包的任意类的任意参数个数的以"e"结尾方法
    //@Pointcut("execution(* com.baidou.dao..*.*(..))")      //dao包下的所有包的任意类的任意参数个数的任意方法
    //@Pointcut("execution(* com..*.*(..))")                 //com包下的所有包下的任意类的任意参数个数的任意方法
    //@Pointcut("execution(* *..*.*(..))")                   //任意包下的任意类的任意参数个数的任意方法,一般不用
    @Pointcut("execution(* *(..))")                    //上面方式的简写方式,一般不用
    //@Pointcut("execution(* com.baidou.service.*.*(..))")   //常用写法
    private void pt(){}

    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

4.3 书写技巧


  • 所有代码按照标准规范开发,否则以下技巧全部失效。
  • 切入点表达式,描述切入点**通常描述接口**,而不描述实现类。
  • 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)。
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述。
  • 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配。
  • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名。
  • 方法名书写以动词进行精准匹配,名词采用*匹配,例如getById书写成getBy*,selectAll书写成selectAll。
  • 参数规则较为复杂,根据业务方法灵活调整。
  • 通常**不使用异常作为匹配**规则。

示例:

//切入点表达式:
//@Pointcut("execution(public void com.baidou.dao.BookDao.u*())")
	//指定包下指定接口中以"u"开头的无参方法且返回值为void

//@Pointcut("execution(public void com.baidou.dao.BookDao.*e())")
	//指定包下指定接口中以"e"结尾的无参方法且返回值为void

//@Pointcut("execution(public void com.baidou.dao.BookDao.*())")
	//指定包下指定接口中所有无参方法且返回值为void

//@Pointcut("execution(public void com.baidou.dao.BookDao.*(*))")
	//指定包下指定接口中所有有一个参数的方法且返回值为void

//@Pointcut("execution(public void com.baidou.dao.BookDao.*(..))")
	//指定包下指定接口中所有方法且返回值为void

//@Pointcut("execution(public void com.baidou.dao.*.*(..))")//指定包下所有类中所有方法且返回值为void

//@Pointcut("execution(* com.baidou.dao.*.*(..))")//指定包下所有类中所有方法

//@Pointcut("execution(* com.baidou.*.*.*(..))")//com.baidou包下所有子包中所有类中所有方法

//@Pointcut("execution(* com.baidou..*.*(..))")//com.baidou包下所有包中所有类中所有方法

//@Pointcut("execution(* *..*.*(..))")//任意包下所有包中所有类中所有方法

//@Pointcut("execution(* *(..))")//任意包下所有包中所有类中所有方法
private void pt(){}

5. AOP通知类型


5.1 AOP通知分类


AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置。

AOP有5种通知类型:

  • 前置通知(before):在切入点方法执行之前执行。
  • 后置通知(after):在切入点方法执行之后执行,无论切入点方法内部是否出现异常,后置通知都会执行。
  • **环绕通知(around):**手动调用切入点方法并对其进行增强的通知方式。
  • 返回后通知(afterReturning):在切入点方法执行之后执行,如果切入点方法内部出现异常将不会执行。
  • 抛出异常后通知(afterThrowing):在切入点方法执行之后执行,只有当切入点方法内部出现异常之后才执行。

5.2 AOP通知详解


5.2.1 前置通知

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

示例:

@Component
@Aspect
public class MyAdvice {

   	//切入点
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")
    private void pt(){}

    @Before("pt()") //前置通知
    public void before() {
        System.out.println("before advice ...");
    }
}

5.2.2 后置通知

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

示例:

@Component
@Aspect
public class MyAdvice {

   	//切入点
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")
    private void pt(){}

    @After("pt()") //后置通知
    public void after() {
        System.out.println("after advice ...");
    }
}

5.2.3 返回后通知

  • 名称:@AfterReturning(了解)
  • 类型:方法注解
  • 位置:定义在通知方法上
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行,如果切入点方法内部出现异常将不会执行。

示例:

@Component
@Aspect
public class MyAdvice {

   	//切入点
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")
    private void pt(){}

    @AfterReturning("pt()") //返回后通知
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }
}

5.2.4 抛出异常后通知

  • 名称:@AfterThrowing(了解)
  • 类型:方法注解
  • 位置:定义在通知方法上
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行。(只有当切入点方法内部出现异常之后才执行)

示例:

@Component
@Aspect
public class MyAdvice {

   	//切入点
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")
    private void pt(){}

    @AfterThrowing("pt()") //抛出异常后通知
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}

示例:

5.2.5 环绕通知

  • 名称:@Around(重点,常用)
  • 类型:方法注解
  • 位置:定义在通知方法上
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行。

示例:

@Component
@Aspect
public class MyAdvice {

   	//切入点
    @Pointcut("execution(void com.baidou.dao.BookDao.update())")
    private void pt(){}

    @Around("pt()") // 环绕通知
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

}

环绕通知注意事项:

  • 环绕通知方法形参必须是ProceedingJoinPoint,表示正在执行的连接点,使用该对象的proceed()方法表示对原始对象方法进行调用,返回值为原始对象方法的返回值。

  • 环绕通知方法的返回值建议写成Object类型,用于将原始对象方法的返回值进行返回,哪里使用代理对象就返回到哪里。


二、AOP案例

1. 测量业务接口万次执行效率


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

案例分析:

  • 业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率。
  • 通知类型选择前后均可以增强的类型——环绕通知。

代码实现:

1、前期准备:

  • Spring整合mybatis对spring_db数据库中的Account进行CRUD操作。(在上一个spring笔记中写过)

  • Spring整合Junit测试CRUD是否OK。

  • 在pom.xml中添加aspectjweaver切入点表达式依赖。

<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>

2、编写通知类

@Component //让spring容器管理
@Aspect //定义当前类是切面
public class ProjectAdvice {
    //匹配业务层的所有方法
    @Pointcut("execution(* com.baidou.service.*Service.*(..))")
    private void servicePt(){}

    //设置环绕通知,在原始操作的运行前后记录执行时间
    @Around("ProjectAdvice.servicePt()") //本类类名可以省略不写
    public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
        //获取执行的签名对象
        Signature signature = pjp.getSignature();
        //获取接口/类全限定名
        String className = signature.getDeclaringTypeName();
        //获取方法名
        String methodName = signature.getName();
        //记录开始时间
        long start = System.currentTimeMillis();
        //执行万次操作
        for (int i = 1; i <= 10000; i++) {
           pjp.proceed();//调用原始方法的
        }
        //记录结束时间
        long end = System.currentTimeMillis();
        //打印执行结果
        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    }
}

3、在SpringConfig配置类上开启AOP注解功能

@Configuration
@ComponentScan("com.baidou") //spring注解包扫描
@PropertySource("classpath:jdbc.properties") //加载配置文件
@Import({JdbcConfig.class,MybatisConfig.class}) //导入其他配置类(第三方bean)
@EnableAspectJAutoProxy //开启AOP注解功能
public class SpringConfig {
}

4、编写测试

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

在这里插入图片描述


2. AOP切入点数据获取


在环绕通知中可以获取到哪些数据?

  • 获取参数
  • 获取返回值
  • 获取异常

2.1 获取参数


说明:在所有通知中都可以获取到连接点方法的参数。jp.getArgs()

  • JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数。
@Before("pt()")//前置通知
public void before(JoinPoint jp) {
    Object[] args = jp.getArgs(); //获取连接点方法的参数
    System.out.println(Arrays.toString(args));
}
  • ProccedJointPoint是JoinPoint的子类,适用于环绕通知,可以实现对原始方法的调用。
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs(); //获取连接点方法的参数
    System.out.println(Arrays.toString(args));
    Object ret = pjp.proceed();//调用原始方法
    return ret;
}

2.2 获取返回值


说明:在返回后通知和环绕通知中都可以获取到连接点方法的返回值

  • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象。
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret) { //变量名要和returning="ret"的属性值一致
    System.out.println("afterReturning advice ..."+ret);
}
  • 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值。
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // 手动调用连接点方法,返回值就是连接点方法的返回值
    Object ret = pjp.proceed();
    return ret;
}

2.3 获取异常


说明:在抛出异常后通知和环绕通知中都可以获取到连接点方法中出现的异常

  • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {//变量名要和throwing = "t"的属性值一致
    System.out.println("afterThrowing advice ..."+ t);
}
  • 使用环绕通知获取切入点方法的异常信息:
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)  {
    Object ret = null;
    //此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常
    try {
        ret = pjp.proceed();
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return ret;
}

3. 网盘密码数据兼容处理


需求:对网盘分享链接输入密码时尾部多输入的空格做兼容处理。

  • 在业务方法执行之前对所有的输入参数进行格式处理——trim()去首尾空格。
  • 使用处理后的参数调用原始方法——环绕通知中存调用原始方法,传入处理后的密码参数。
//突发小技巧:百度网盘通过接口传参实现密码校验
https://pan.baidu.com/s/1HgvjN9yFgNpQ5q4TP2bB5w?pwd=1111

1、编写接口

//service层代码
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);
    }
}
//dao层代码
public interface ResourcesDao {
    boolean readResources(String url, String password);
}
@Repository
public class ResourcesDaoImpl implements ResourcesDao {
    public boolean readResources(String url, String password) {
        System.out.println(password.length());
        //模拟校验
        return password.equals("root");
    }
}

2、编写通知类

@Component
@Aspect//通知类
public class HandlePwdAdvice {

    //1.定义切入点
    @Pointcut("execution(* com.baidou.service.*.openURL(..))")
    private void pt(){}

    //2.编写环绕通知,配置切面
    @Around("pt()")
    public Object handlePwd(ProceedingJoinPoint pjp) throws Throwable {
        //获取原始方法第二个参数(密码)
        Object[] args = pjp.getArgs();
        Object pwd = args[1];

        //b.对密码进行trim操作
        args[1] = pwd.toString().trim();

        //调用原始方法,但是使用修改后的参数,接收返回值
        Object result = pjp.proceed(args);//不传参数的话,就还是使用原来的参数.

        //d.返回密码校验结果
        return result;
    }
}

3、在SpringConfig配置类上开启AOP注解功能

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

4、测试

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/helloworld", "root ");
        System.out.println(flag);
    }
}

三、Spring事务管理

1. Spring事务介绍

事务作用:在数据层保障一系列的数据库操作,要么一起成功,要么一起失败。

Spring事务作用:在数据层或业务层保障一系列的数据库操作,要么一起成功,要么一起失败。

我们只需要在业务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。

//spring中进行事务管理的核心接口
public interface PlatformTransactionManager extends TransactionManager {
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}    
//我们的数据层使用的是Mybatis,所以使用DataSourceTransactionManager实现类
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
		implements ResourceTransactionManager, InitializingBean {
    
 ...   
}

在这里插入图片描述

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

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

相关文章

LeetCode刷题模版:221 - 230

目录 简介221. 最大正方形222. 完全二叉树的节点个数223. 矩形面积224. 基本计算器【未理解】225. 用队列实现栈226. 翻转二叉树227. 基本计算器 II【未理解】228. 汇总区间229. 多数元素 II230. 二叉搜索树中第K小的元素结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有…

【学习打卡 Free-Excel 】Task6 查找函数

文章目录1. VLOOKUP用法示例注意情况一情况二2. 通配符用法示例3.XLOOKUP用法匹配类型搜索模式1. VLOOKUP 用法 【VLOOKUP函数】VLOOKUP&#xff08;要查找的值&#xff0c;查找区域&#xff0c;要返回的结果在查找区域的第几列&#xff0c;精确匹配或近似匹配&#xff09;示…

JAVA Web 常见问题解决方案(持续更新,欢迎投稿常见问题)

大家有什么问题未解决的可以试图联系博主&#xff0c;群号在主页的详细资料 都是博主自己学习过程中遇到的问题&#xff0c;大家有什么常见问题欢迎Git 问题集目录 找到适合自己版本的pom依赖 IDEA实体类快速创建 get(),set(),toString()方法 报错Cannot find class: com.mysq…

【图卷积网络】03-空域卷积介绍 (一)

注&#xff1a;本文为3.1-3.2 空域卷积视频笔记&#xff0c;仅供个人学习使用 1、谱域图卷积 1.1 回顾 上篇博客【图卷积神经网络】02-谱域图卷积介绍讲到了三个经典的谱域图卷积&#xff1a; SCNN用可学习的对角矩阵来代替谱域的卷积核。 ChebNet采用Chebyshev多项式代替谱…

webpack使用Ammo.js - 在react中使用Ammo.js

真实麻烦啊[我的项目仓库 Next.js项目 仅供参考](https://gitee.com/honbingitee/three-template-next.js/tree/feature%2Fphysics/)本文展示使用ammo.wasm.js 结合ammo.wasm.wasm的wasm版本使用方法1. 配置webpack2. 导出Ammo 修改ammo.wasm.js文件3. 删除语句 通过查找 this.…

【Flink】详解Flink的八种分区

简介 Flink是一个流处理框架&#xff0c;一个Flink-Job由多个Task/算子构成&#xff0c;逻辑层面构成一个链条&#xff0c;同时Flink支持并行操作&#xff0c;每一个并行度可以理解为一个数据管道称之为SubTask。我们画图来看一下&#xff1a; 数据会在多个算子的SubTask之间相…

【奇妙的数据结构世界】用图像和代码对链表的使用进行透彻学习 | C++

第九章 链表 目录 第九章 链表 ●前言 ●一、链表是什么&#xff1f; 1.简要介绍 2.具体情况 ●二、链表操作的关键代码段 1.类型定义 2.常用操作 ●总结 前言 简单来说&#xff0c;数据结构是一种辅助程序设计并且进行优化的方法论&#xff0c;它不仅讨论数据的存储与处…

打工人必知必会(一)——规章制度保险劳动合同变更

目录 参考 1、规章制度的生效要件 2、工资的发放形式 3、社会保险的基本规定 4、基本养老保险 5、医疗保险、失业保险、工伤保险、生育保险 6、劳动合同的变更 第一节 协商变更劳动合同 第二节 单方变更劳动合同 参考 《HR全程法律顾问&#xff1a;企业人力资源管理高…

5-6中央处理器-多处理器系统硬件多线程

文章目录一.多处理器系统&#xff08;一&#xff09;计算机体系结构分类1.单指令单数据流SISD2.单指令多数据流SIMD3.多指令单数据流MISD4.多指令多数据流MIMD&#xff08;1&#xff09;(共享内存)多处理器系统/多核处理器&#xff08;2&#xff09;多计算机系统&#xff08;二…

逆水寒魔兽老兵服副本攻略及代码分析(英雄武林风云录,后续更新舞阳城、扬州、清明等副本攻略)

文章目录一、武林风云录1&#xff09;老一&#xff1a;陈斩槐&#xff08;只有四个机制&#xff0c;dps压力不大&#xff0c;留爆发打影子就行&#xff09;&#xff08;1&#xff09;点名红色扇形区域&#xff08;2&#xff09;点名红色长条&#xff0c;注意最后还有一段大劈&a…

MongoDB入门(特点,使用场景,命令行操作,SpringData-MongoDB)

今天我们将通过这一篇博客来了解MongoDB的体系结构&#xff0c;命令行操作和在JAVA 当中使用SpringData-MongoDB 来 操作MongoDB。 如果没有安装的小伙伴 可以看一下 这篇文章 (59条消息) 开源的文档型数据库–MongoDB&#xff08;安装&#xff09;_一切总会归于平淡的博客-CS…

LeetCode[128]最长连续序列

难度&#xff1a;中等题目&#xff1a;给定一个未排序的整数数组 nums&#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。请你设计并实现时间复杂度为 O(n)的算法解决此问题。示例 1&#xff1a;输入&#xff1a;nums [100,4,2…

java中new的含义如何理解?

在Java中&#xff0c;new关键字被使用来创建一个新的对象&#xff0c;可以理解为创建的意思。使用关键字new来创建一个对象也叫类的实例化&#xff0c;使用new创建对象时&#xff0c;会调用构造方法初始化对象声明对象Cat cat 在栈内存中实例化对象 new Cat(参数); 在堆内存中每…

C++:类的static成员,友元和构造函数初始化列表

目录 一.类的构造函数的初始化列表 1.类的构造函数初始化列表的引入和介绍 2.初始化列表用于类的类对象成员的拷贝构造函数的调用 3.初始化列表的使用细则 4.使用初始化列表的一个注意事项 二.explicit关键字 三.C类的static成员 1.类中static修饰的成员变量 2.类中st…

Lesson 4.2 逻辑回归参数估计:极大似然估计、相对熵与交叉熵损失函数

文章目录一、逻辑回归参数估计基本思路1. 构建损失函数2. 损失函数求解二、利用极大似然估计进行参数估计三、熵、相对熵与交叉熵1. 熵&#xff08;entropy&#xff09;的基本概念与计算公式2. 熵的基本性质3. 相对熵&#xff08;relative entropy&#xff09;与交叉熵&#xf…

LeetCode[947]移除最多的同行或同列石头

难度&#xff1a;中等题目&#xff1a;n块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。如果一块石头的 同行或者同列 上有其他石头存在&#xff0c;那么就可以移除这块石头。给你一个长度为 n的数组 stones&#xff0c;其中 stones[i] [xi, yi]…

MATLAB算法实战应用案例精讲-【人工智能】Grover量子搜索算法(补充篇)

前言 因为量子计算的并行性, 搜索问题, 比如说数据库搜索, 最短路径问题, 加密问题, 图形着色问题等, 都被视为可以做到量子加速. Grover 算法,有时也称为量子搜索算法(quantum search algorithm),指一种在量子计算机上运行的非结构化搜索算法,是量子计算的典型算法…

LeetCode[765]情侣牵手

难度&#xff1a;困难题目&#xff1a;n对情侣坐在连续排列的 2n个座位上&#xff0c;想要牵到对方的手。人和座位由一个整数数组 row表示&#xff0c;其中 row[i]是坐在第 i 个座位上的人的 ID。情侣们按顺序编号&#xff0c;第一对是 (0, 1)&#xff0c;第二对是 (2, 3)&…

#A. Balanced Lineup排队(rmq模板题)

题目思路建议先看看详解rmq问题很明显这道题意是跟你一段数列&#xff0c;并给出多次询问,询问区间内最大值和最小值的差。如果去暴力枚举显然会超时,所以要用st算法来解决。我们要建立两个RMQ预处理内容&#xff0c;分别处理最大值和最小值。建一个mx[i][j]代表从i开始,长度为…

精品图表Crack:TeeChart ActiveX version 2023.1

TeeChart ActiveX version 2023 数据可视化专家,Visual Studio.Net、Visual Basic、Visual Studio 6和 IIS / ASP的图表组件 概述 TeeChart Pro ActiveX 图表组件库提供数百种 2D 和 3D 图形样式、56 种数学和统计函数供您选择&#xff0c;以及无限数量的轴和 14 个工具箱组件…