Spring-AOP 讲解

news2024/11/18 8:46:23

1、为什么会出现AOP思维

我们知道,在我们的项目中,会出现核心代码和非核心代码,对于非核心代码,在各个方法中可能是冗余的,此时为了解决这种非核心代码的冗余以及不方便管理的问题,就出现了AOP思维。

2、AOP思维是什么?

AOP:Aspect Oriented Programming面向切面编程。它是面向对象编程OOP的完善与补充。

面向对象编程是垂直性的,我们可以继承父类的方法,但是如果我们想添加一些其他东西,就需要完全重写该方法。

而AOP是切面性质的,我们需要做的就是解耦将冗余代码取出来,然后再动态的添加到每个业务方法中。

此时我们就需要一些技术,来完成我们的AOP思维,该技术就是代理技术。

3、代理技术

代理模式是二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

无代理类时:我们是直接调用业务方法。

当出现代理类时:

我们首先调用代理类,之后代理类再去调用我们的目标方法。最后将两者填充在一起,返回给最初的调用者。这样做的好处是:我们可以将那些冗余代码写在代理类中,进行统一管理,通过代理类来调用目标方法。可以让目标方法专注于去完成它自己的业务逻辑。

代理在开发中有两种模式:一种是静态代理,一种是动态代理(jdk,cblib)

静态代理需要我们为每个目标类都编写一个目标类,这样虽然实现了解耦,但是还是需要大量代码,并没有实现统一管理。

动态代理包括jdk动态代理和cglib

jdk动态代理与cglib的区别:

jdk动态代理:其目标类必须有一个接口,目标类实现该接口,他会根据目标类的接口动态生成一个代理对象!代理对象和目标对象有相同的接口!(拜把子)。

cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口!(认干爹)

4、Spring aop框架

  不论是静态代理还是动态代理技术,都需要编写大量代码,并没有简化操作。所以提出了Spring AOP框架,该框架底层用的仍然是代理技术,但是由于封装了起来,对程序员来说就很方便。

4.1 AOP主要应用场景(了解)

只要记住对于那些非核心的冗余代码,我们可以使用AOP即可。

AOP(面向切面编程)是一种编程范式,它通过将通用的横切关注点(如日志、事务、权限控制等)与业务逻辑分离,使得代码更加清晰、简洁、易于维护。AOP可以应用于各种场景,以下是一些常见的AOP应用场景:

1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。

综上所述,AOP可以应用于各种场景,它的作用是将通用的横切关注点与业务逻辑分离,使得代码更加清晰、简洁、易于维护。

4.2 AOP术语

1-横切关注点

从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务、异常等。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

2-通知(增强)

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

- 前置通知:在被代理的目标方法前执行
- 返回通知:在被代理的目标方法成功结束后执行(**寿终正寝**)
- 异常通知:在被代理的目标方法异常结束后执行(**死于非命**)
- 后置通知:在被代理的目标方法最终结束后执行(**盖棺定论**)
- 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

3-连接点 joinpoint

这也是一个纯逻辑概念,不是语法定义的。

指那些被拦截到的点。在 Spring 中,可以被动态代理拦截目标类的方法

4-切入点 pointcut

定位连接点的方式,或者可以理解成被选中的连接点!

是一个表达式,比如execution(* com.spring.service.impl.*.*(..))。符合条件的每个方法都是一个具体的连接点。

5-切面 aspect

切入点和通知的结合。是一个类。

6-目标 target

被代理的目标对象。

7-代理 proxy

向目标对象应用通知之后创建的代理对象。

8-织入 weave

指把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。

5、Spring-AOP基于注解方式实现和细节

5.1 Spring AOP底层实现技术

- 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
- cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
- AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

AspectJ 是AOP早期实现框架,Spring AOP继承了该注解方式,但是底层实现技术仍然是代理技术。

依赖:

我们需要导入的依赖有 spring-aop,spring-aspects,AspectJ

但是我们使用spring容器导入的有Spring-context,其带有Spring-AOP依赖,而AspectJ依赖也不用手动导入,只需要导入Spring-aspects依赖即可,其带有Aspectj依赖。

导入依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.6</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.6</version>
</dependency>

编写Springaop流程:首先我们先正常写我们的核心代码,①导入依赖,②编写核心代码,③配置ioc,④测试,之后再来管理aop流程,我们的核心是来⑤编写增强代码,编写一个类来写增强代码,增强类方法具体有几个,是要看我们的非核心代码的位置,不同位置增强方法不同。接下来是⑥增强类的配置(插入切点的位置,切点指定,切面配置等),最后是⑦开启AOP配置。

①导入依赖

②编写核心代码

接口

package demo05.aop;

public interface Calculator {
    
    int add(int i, int j);
    
    int sub(int i, int j);
    
    int mul(int i, int j);
    
    int div(int i, int j);
    
}

具体实现类

package demo05.aop.Impl;


import demo05.aop.Calculator;
import org.springframework.stereotype.Component;

/**
 * 实现计算接口,单纯添加 + - * / 实现! 掺杂其他功能!
 */
@Component
public class CalculatorPureImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        int result = i + j;
    
        return result;
    }
    
    @Override
    public int sub(int i, int j) {
    
        int result = i - j;
    
        return result;
    }
    
    @Override
    public int mul(int i, int j) {
    
        int result = i * j;
    
        return result;
    }
    
    @Override
    public int div(int i, int j) {


        int result = i / j;

        return result;
    }
}

③配置类

package demo05.aop.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"demo05.aop.Impl","demo05.aop.proxy"})
@EnableAspectJAutoProxy
public class Myconfig {
}

④测试

⑤编写增强类

在这里我们在方法开始之前输出 以及方法结束和方法异常输出,所以需要写三个增强类方法。

package demo05.aop.proxy;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
// @Aspect表示这个类是一个切面类
@Component
// @Component注解保证这个切面类能够放入IOC容器
public class Myproxy {
    // @Before注解:声明当前方法是前置通知方法
    // value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上
    //第一个*表示 不考虑返回值类型,我们要插入哪一个包下的哪个类,第三个参数*表示该类下的所有方法
    //第四个参数*(..)表示不考虑其参数类型以及有无参数。
    @Before("execution(* demo05.aop.Impl.*.*(..))")
    public void start(){
   System.out.println("方法开始");
    }
    @AfterReturning("execution(* demo05.aop.Impl.*.*(..))")
    public void after(){
        System.out.println("方法结束");
    }
    @AfterThrowing("execution(* demo05.aop.Impl.*.*(..))")
    public void error(){
        System.out.println("方法错误");
    }}

⑥增强配置

在前面的代码中,我们已经将其写过了,包括切点配置,切面配置等

我们也需要将增强类加上ioc组件注解,因为代理也需要在ioc容器中,才能操作我们的核心代码类。最后返回的其实也是我们的代理类

⑦开启aop配置

我们可以在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: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.atguigu" />
    <!-- 开启aspectj框架注解支持-->
    <aop:aspectj-autoproxy />

</beans>

 

也可以在配置类中开启

package demo05.aop.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"demo05.aop.Impl","demo05.aop.proxy"})
@EnableAspectJAutoProxy
public class Myconfig {
}

 ⑧测试

@Test
public void test_06(){
    AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(Myconfig.class);
  //有接口时,使用的时jdk代理,这里要用接口类接收,因为返回的是代理类,是目标类的一个兄弟,不能用目标类接收
//如果没有接口,就可以用目标类接收,因为用cglib,返回的是目标类的一个继承代理类。
    Calculator calculator=context.getBean(Calculator.class);
    System.out.println(calculator.add(1,1));
    context.close();
}

结果:

5.2获取切点详细信息

有时候,我们需要我们目标方法的具体信息,比如参数,方法名,方法类的信息,返回值等。

这时候我们就需要一些方法来实现该需求。

如果我们需要获取切点的详细信息,比如方法名,方法类的信息,参数信息等

我们需要在增强类的参数中添加上 JoinPoint joinPoint

如果要获取返回值或者异常信息 就要在参数以及注解中添加上额外信息,见下面代码

package demo05.aop.proxy;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.lang.reflect.Modifier;

@Aspect
@Component
/**编写增强方法
 * 配置增强方法的位置
 * 配置切点信息
 * 配置切面以及ioc
 * 开启aspectj注解支持
 */
 public class Myadvice {
     @Before("execution(* demo05.aop.Impl.*.*(..))")
  public void before(JoinPoint joinPoint){
         //获取方法名
         String name = joinPoint.getSignature().getName();
          //获取类型修饰符
         int modifiers = joinPoint.getSignature().getModifiers();
         String s = Modifier.toString(modifiers);
         //获取类信息
         String simpleName = joinPoint.getTarget().getClass().getSimpleName();
         //获取参数列表
         Object[] args = joinPoint.getArgs();

     }
     //只有正常返回 才能获取返回值
    //获取返回值 1.在形参中添加要接受返回值的名字
    //2. 在 @AfterReturning()中添加是用哪个形参名来接受的返回值
     @AfterReturning(value = "execution(* demo05.aop.Impl.*.*(..))",returning ="returning" )
     public void  afterreturning(JoinPoint joinPoint,Object returning){

     }
     //只有异常返回 才能接收异常信息
     //获取异常信息 1.在形参中添加要接受异常信息的名字
     //2. 在 @AfterThrowing()中添加是用哪个形参名来接受的异常信息
     @AfterThrowing(value = "execution(* demo05.aop.Impl.*.*(..))",throwing ="throwable")
     public void afterthrowing(JoinPoint joinPoint,Throwable throwable){

     }
}

5.3 切点表达式

5.3.1 语法细节

固定语法:execution(1 2 3.4.5(6))

1:方法修饰符

2:方法返回值

如果两者都不考虑 统一用*代替,注意:要考虑都不考虑 不能只考虑一个

3 包

固定的包: com.atguigu.api | service | dao

单层的任意命名: com.atguigu.* = com.atguigu.api com.atguigu.dao * = 任意一层的任意命名 任意层任意命名: com.. = com.atguigu.api.erdaye com.a.a.a.a.a.a.a ..任意层,任意命名 用在包上!

注意: ..不能用作包开头 public int ..

错误语法 com..

找到任何包下: *.. 

4.类名

固定名称: UserService
任意类名: *
部分任意: com..service.impl.*Impl
任意包任意类: *..* 

5.方法名

语法和类名一致
任意访问修饰符,任意类的任意方法: * *..*.* 

6.方法参数

第七位: 方法的参数描述
       具体值: (String,int) != (int,String) 没有参数 ()
       模糊值: 任意参数 有 或者 没有 (..)  ..任意参数的意识
       部分具体和模糊:
         第一个参数是字符串的方法 (String..)
         最后一个参数是字符串 (..String)
         字符串开头,int结尾 (String..int)
         包含int类型(..int..) 

小练习

1.查询某包某类下,访问修饰符是公有,返回值是int的全部方法
2.查询某包下类中第一个参数是String的方法
3.查询全部包下,无参数的方法!
4.查询com包下,以int参数类型结尾的方法
5.查询指定包下,Service开头类的私有返回值int的无参数方法
 

5.4 切点的统一管理

我们发现在增强类中 切点表达式是一样的,我们可以将其提取出来,然后复用

方法1:提取到当前类中(不推荐,每个类都需要提取)

   写一个空方法 public void xxx(){}

   添加注解PointCut

增强注解中直接引用方法名即可 

package demo05.aop.proxy;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
// @Aspect表示这个类是一个切面类
@Component
// @Component注解保证这个切面类能够放入IOC容器
public class Myproxy {
    // @Before注解:声明当前方法是前置通知方法
    // value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上
    //第一个*表示 不考虑返回值类型,我们要插入哪一个包下的哪个类,第三个参数*表示该类下的所有方法
    //第四个参数*(..)表示不考虑其参数类型以及有无参数。
    @Pointcut("execution(* demo05.aop.Impl.*.*(..))")
    public void pc(){}
    @Before("pc()")
    public void start(){
   System.out.println("方法开始");
    }
    @AfterReturning("pc()")
    public void after(){
        System.out.println("方法结束");
    }
    @AfterThrowing("pc()")
    public void error(){
        System.out.println("方法错误");
    }}

方法2:创建一个存储切点的类

该类记得放在ioc容器中

单独维护切点表达式

增强方法使用该切点表达式: 类的全限定符.方法名 

切点类:

package demo05.aop.pointcut;

import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
public class Mypointcut {
    @Pointcut("execution(* demo05.aop.Impl.*.*(..))")
    public void pc(){

    }
}d

代理类:

package demo05.aop.proxy;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
// @Aspect表示这个类是一个切面类
@Component
// @Component注解保证这个切面类能够放入IOC容器
public class Myproxy {
    // @Before注解:声明当前方法是前置通知方法
    // value属性:指定切入点表达式,由切入点表达式控制当前通知方法要作用在哪一个目标方法上
    //第一个*表示 不考虑返回值类型,我们要插入哪一个包下的哪个类,第三个参数*表示该类下的所有方法
    //第四个参数*(..)表示不考虑其参数类型以及有无参数。
    @Pointcut("execution(* demo05.aop.Impl.*.*(..))")
    public void pc(){}
    @Before("demo05.aop.pointcut.Mypointcut.pc()")
    public void start(){
   System.out.println("方法开始");
    }
    @AfterReturning("demo05.aop.pointcut.Mypointcut.pc()")
    public void after(){
        System.out.println("方法结束");
    }
    @AfterThrowing("demo05.aop.pointcut.Mypointcut.pc()")
    public void error(){
        System.out.println("方法错误");
    }}

 

5.5 环绕通知

环绕通知可以包含brfore,after等这些通知。可以通过下面的例子来演示。

package demo05.aop.proxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Advicearound {
    //ProceedingJoinPoint 比JoinPoint 多了一个可执行目标函数的方法
    @Around("demo05.aop.pointcut.Mypointcut.pc()")  //配置切点和位置
    public Object aroundSet(ProceedingJoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Object result=null;
        try {
            System.out.println("开启事务");  //相当于Before
            result = joinPoint.proceed(args);
            System.out.println("事务结束"); //相当于AfterReturning
        }
        catch (Throwable e){
            System.out.println("事务异常");//相当于Afterthrowing
            throw new RuntimeException(e);//一定要抛出异常,不然外部无论如何都接收不到异常
        }
        return result;
    }
}

 

5.5 切面优先级设定

比如我们有一个日志增强,一个事务增强,如何确定两个增强的优先级呢?

可以通过@Order()注解

里边的数值越小,优先级越高,前置增强先执行,后置增强后执行

反之越低。

事务增强 @Order(10) 日志增强 @Order(20)

结果:

 

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

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

相关文章

模拟批量转换和报警功能块(博途SCL源代码)

单通道模拟量转换FC算法和源代码,请参考下面文章链接: PLC模拟量采集算法数学基础(线性传感器)_plc3秒采集一次模拟量_RXXW_Dor的博客-CSDN博客模拟量采集库如何设计,具体算法代码请参看我的另一篇博文:PLC模拟量输入 模拟量转换FC:S_ITR_RXXW_BOSS的博客-CSDN博客_s_i…

路由器的路由过程

大家好&#xff0c;我叫徐锦桐&#xff0c;个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识&#xff0c;还有日常折腾的经验&#xff0c;欢迎大家来访。 路由器是连接不同的局域网的一个设备&#xff0c;它一开始的目的是互联异构网络的。 前言 这里…

langchain agent简单使用;文档总结load_summarize_chain

1、langchain agent简单使用 参考&#xff1a;https://zhuanlan.zhihu.com/p/643868934?utm_id0 from langchain.agents.agent_toolkits import create_python_agent from langchain.agents import load_tools, initialize_agent from langchain.agents import AgentType fr…

【讲座笔记】基于 Apache Calcite 的多引擎指标管理最佳实践|CommunityOverCode Asia 2023 | 字节开源

引言 三个问题 (问题解法) 1套SQL 2种语法 统一SQL的实践案例 虚拟列的实践案例 SQL Define Function 指标管理的实现 在这里插入图片描述

交换机/防火墙-基础配置-23.10.11

1.MAC地址 交换机在给主机之间传递信息包时&#xff0c;通过MAC地址来标识每台主机 主机间发生信息包交换时&#xff0c;交换机就会将通信过的主机的mac地址存下 dis mac-address 交换机转发的数据包中&#xff0c;会包含一个目标MAC&#xff0c;交换机识别数据包中的目标MA…

nrf52832 低功耗蓝牙 广播

nrf52832 低功耗蓝牙5.x 文章目录 nrf52832 低功耗蓝牙5.x广播广播的特点&#xff1a; 一、修改代码演示广播初始化修改 广播名称修改广播时间修改广播内容和参数 总结 广播 广播是低功耗蓝牙通信的基础&#xff0c;向外广播信息&#xff0c;等待被连接&#xff1b; 广播包含了…

浅谈 docker run 命令中的 -i -t 和 -d 选项

以 docker Ubuntu 镜像为例&#xff0c;ubuntu镜像启动时默认执行的命令是"/bin/bash"。 文章目录 不带任何选项带 -i 选项带 -i 和 -t 选项-d 选项 不带任何选项 rootubuntu20:~# docker run ubuntu:20.04 rootubuntu20:~# docker ps CONTAINER ID IMAGE …

1997-2017年各省能源投入数据(万吨标准煤)

1997-2017年各省能源投入数据&#xff08;万吨标准煤&#xff09; 1、时间&#xff1a;1997-2017年 2、来源&#xff1a;中国统计年鉴 3、范围&#xff1a;30个省 4、指标&#xff1a;能源投入&#xff08;各省8种能源消费总量计算得出&#xff09;&#xff08;万吨标准煤&…

Day 08 python学习笔记

函数 作用域 作用域&#xff1a;变量的访问权限 全局变量与局部变量 声明在函数外边的变量----全局变量 ----》全局作用域 函数内部的变量------局部变量 ----》局部作用域顶格创建的函数也是全局的 例&#xff1a; a 100def test_01():a 0b 110print…

解决因d3dx9_30.dll丢失程序无法运行,电脑缺失d3dx9_30.dll报错解决方案

我们的生活和工作都离不开电脑。然而&#xff0c;电脑作为一种复杂的工具&#xff0c;也会出现各种各样的问题。其中&#xff0c;丢失d3dx9_30.dll文件是一个常见的问题。d3dx9_30.dll是DirectX的动态链接库文件&#xff0c;如果丢失或损坏&#xff0c;可能会导致许多软件和游戏…

Kettle查询表数据循环到目标表

简介&#xff1a;Kettle工具有一种业务场景是动态查询数据库中某张表的某个字段&#xff0c;将该字段当做循环变量&#xff0c;循环整个作业。下面就是记录该步骤。 一、思路 首先生产环境中我们需要做的一个业务就是&#xff1a; 按品牌循环执行&#xff1a;step3step4&…

LeetCode 2316. 统计无向图中无法互相到达点对数【图,BFS,DFS,并查集】1604

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

raven2 靶机

一.信息收集 靶机的信息收集就是网段 2.使用nmap扫描端口和真实IP nmap 网段 扫描ip和端口 二.查找漏洞 可以查看版本 找到利用信息 cms 以及 apache 组件漏洞 组件的目录漏洞 找到了 一般爆破不出来 敏感目录爆破 wordpress的 dirb --url 网址 直接找…

[C++]类型转换

一、C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与 接收返回值类型不一致时&#xff0c;就需要发生类型转化。 C语言中总共有两种形式的类型转换&#xff1a;隐式类型转换和显式…

HBuilder插件推荐

整理一下我觉得好用的插件&#xff0c;后期可能会有更改 eslint-js eslint-plugin-vue Prettier scss/sass编译 右键复制vue页面路径&#xff0c;主要用于快速复制vue页面的路径到浏览器

HashMap的运用小练习

public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("jack",650);hashMap.put("tom",1200);hashMap.put("smith",2900);System.out.println(hashMap);//将jack的工资更改为2600hashMap.put("jack",…

分发糖果[困难]

优质博文&#xff1a;IT-BLOG-CN 一、题目 n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 【1】每个孩子至少分配到1个糖果。 【2】相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩…

ATPCS:ARM-Thumb程序调用的基本规则

为了使单独编译的c文件和汇编文件之间能够互相调用&#xff0c;需要制定一系列的规则&#xff0c;AAPCS就是ARM程序和Thumb程序中子程序调用的基本规则。 1、ATPCS概述 ATPCS规定了子程序调用过程中寄存器的使用规程、数据站的使用规则、参数的传递规则。为了适应一些特殊的需…