spring复习04,静态代理动态代理,AOP

news2024/12/23 14:50:34

spring复习04,静态代理动态代理,AOP

  • 概念引入
    • java代理模式
  • 静态代理
    • 静态代理模式的好处:
    • 静态代理模式的缺点:
  • 动态代理
    • jdk动态代理
    • 1. InvocationHandler接口
    • 2. Proxy类
    • jdk动态代理代码
  • AOP
    • AOP的概念
    • spirng中aop的实现
      • 方式一:spring原生的接口
      • 方式二:spring中注解配置aop

概念引入

先用一个小例子,来引入我们为什么需要代理,以及代理是什么?
假设当前有一个业务需求,一个加减乘除的四则运算:
方法接口:

package com.gothic.sunset;

public interface Calculate {

    public int add(int a,int b);

    public int sub(int a,int b);

    public int mul(int a,int b);

    public int div(int a,int b);
}

方法实现类:

package com.gothic.sunset;

public class CalculateImpl implements Calculate{

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

    @Override
    public int sub(int a, int b) {
        int result = a - b ;
        return result;
    }

    @Override
    public int mul(int a, int b) {
        int result = a * b ;
        return result;
    }

    @Override
    public int div(int a, int b) {
        int result = a / b ;
        return result;
    }
}

测试类|用户端:

package com.gothic.sunset;

public class Client {
    public static void main(String[] args) {
        CalculateImpl calculate = new CalculateImpl();
        System.out.println(calculate.add(1,2));//3
        System.out.println(calculate.sub(2,1));//1
        System.out.println(calculate.mul(1,2));//2
        System.out.println(calculate.div(2,1));//2
    }
}


新的需求:需要看到每次执行的是什么方法,以及返回的结果是什么,和参与运算的数字分别是什么。
大家可以想一下,怎么修改,以及在原有的业务代码上面修改还是通过别的方法进行修改更好一点。答案当然是后者,因为当你原有的业务代码如果有很多呢,那你一个一个的修改,岂不是很浪费时间和拉低效率,而且假设你别的业务也需要添加类似的功能,怎么办也是在原有的业务代码上修改吗?


java代理模式

java代理模式,即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。

代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

在这里插入图片描述


静态代理

需求:需要看到每次执行的是什么方法,以及返回的结果是什么,和参与运算的数字分别是什么。

解决上面的需求,需要抽象出对应的代理类,然后在代理类中添加新增的业务功能,并且调用其对应的实体类对象。
如下:
代理类的抽象,代理类也需要实现相同的接口,从而添加新增的业务功能。需要注意的是,它需要将真实角色注入,然后在原有的方法中去调用真实角色的方法即可。

package com.gothic.sunset;

public class CalculateProxy implements Calculate{
    private Calculate calculate;

    public void setCalculate(Calculate calculate) {
        this.calculate = calculate;
    }

    @Override
    public int add(int a, int b) {
        argPrint(a,b);
        logBack("add");
        int rs = calculate.add(a,b);
        rsPrint(rs);
        return rs;
    }

    @Override
    public int sub(int a, int b) {
        argPrint(a,b);
        logBack("sub");
        int rs = calculate.sub(a,b);
        rsPrint(rs);
        return rs;
    }

    @Override
    public int mul(int a, int b) {
        argPrint(a,b);
        logBack("mul");
        int rs = calculate.mul(a,b);
        rsPrint(rs);
        return rs;
    }

    @Override
    public int div(int a, int b) {
        argPrint(a,b);
        logBack("div");
        int rs = calculate.div(a,b);
        rsPrint(rs);
        return rs;
    }

    public void argPrint(int a,int b){
        System.out.println("参数A:"+a+"参数B:"+b);
    }

    public void logBack(String msg){
        System.out.println("执行了"+msg+"方法");
    }

    public void rsPrint(int rs){
        System.out.println("结果为--->"+rs);
    }
}

(用户端)测试输出:

package com.gothic.sunset;

public class Client {
    public static void main(String[] args) {
        CalculateImpl calculate = new CalculateImpl();
        CalculateProxy cp = new CalculateProxy();
        cp.setCalculate(calculate);
        cp.add(1,2);
        cp.sub(8,1);
        cp.mul(2,3);
        cp.div(8,4);
    }
}

在这里插入图片描述

静态代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关心一些公共的业务
  • 公共就是交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

静态代理模式的缺点:

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。


动态代理

动态代理:

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理 ,基于类的动态代理
  • 基于接口的动态代理:jdk动态代理
  • 基于类的动态代理:cglib

jdk动态代理

JDK动态代理是指:代理类实例在程序运行时,由JVM根据反射机制动态的生成。也就是说代理类不是用户自己定义的,而是由JVM生成的。

1. InvocationHandler接口

代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。

也就是说,我们创建的每一个代理实例都要有一个关联的InvocationHandler,并且在调用代理实例的方法时,会被转到InvocationHandler的invoke方法上。
publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable;
该invoke方法的作用是:处理代理实例上的方法调用并返回结果。

其有三个参数,分别为:

  1. proxy:是调用该方法的代理实例。
  2. method:是在代理实例上调用的接口方法对应的Method实例。
  3. args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null。
    其返回值为:调用代理实例上的方法的返回值。

2. Proxy类

Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类。

代理类具有以下属性:

代理类的名称以 “$Proxy” 开头,后面跟着一个数字序号。代理类继承了Proxy类。代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。
每个代理类都有一个公共构造函数,它接受一个参数,即接口InvocationHandler的实现,用于设置代理实例的调用处理器。
Proxy提供了两个静态方法,用于获取代理对象。

getProxyClass,用于获取代理类的Class对象,再通过调用构造函数创建代理实例。

	@CallerSensitive
    public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException

该方法有两个参数:

  1. loader:为类加载器。
  2. intefaces:为接口的Class对象数组。

方法返回值为动态代理类的Class对象。

newProxyInstance,用于创建一个代理实例。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)  throws IllegalArgumentException

该方法有三个参数:

  1. loader:为类加载器。
  2. interfaces:为接口的Class对象数组。
  3. h:指定的调用处理器。

返回值为指定接口的代理类的实例。

Proxy类主要用来获取动态代理对象,InvocationHandler接口主要用于方法调用的约束与增强。

jdk动态代理代码

创建一个代理实例的调用处理器,需要实现InvocationHandler接口

proxy:代理对象
method 表示要执行的方法,
args要执行的方法的参数列表。
通过method.getName可以获取对应方法名

package com.gothic.sunset;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    //set注入不同的接口
    public void setTarget(Object target) {
        this.target = target;
    }

    //得到生成的代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }

    //处理代理类实例,并且返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object rs =null;
        try {
            System.out.println("日志,方法"+method.getName()+",参数"+ Arrays.toString(args));
            //proxy 表示代理对象 ,method 表示要执行的方法,args要执行的方法的参数列表
            rs = method.invoke(target, args);
            System.out.println("日志,方法"+method.getName()+",结果"+ rs);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("日志,方法"+method.getName()+",异常"+ e);
        }  finally {
            System.out.println("日志,方法"+method.getName()+",执行完毕");
        }
        return rs;
    }
}

方法测试:

package com.gothic.sunset;

public class Client {
    public static void main(String[] args) {
        //实际角色
        CalculateImpl calculate = new CalculateImpl();
        //代理角色 不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理的对象
        pih.setTarget(calculate);
        //动态生成代理类
        Calculate proxy = (Calculate) pih.getProxy();
        proxy.add(1, 2);
    }
}

输出:
在这里插入图片描述


AOP

AOP的概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

相关术语:

  1. 连接点(join Point):程序执行的某个特定位置,比如(类开始初始化前,初始化后,方法前,方法后,异常后) 一个类或者一段程序代码拥有一些具有边界的特定点,这些特定点称为连接点
  2. 切点(pointcut):每个程序都拥有多个连接点,比如一个类有2个方法,这2个方法就是连接点,连接点就是程序中具体的事物,AOP 通过切点来定位特定的连接点,连接点相当于数据库中的记录,而切点相当于是查询记录的条件。在程序中,切点是连接点位置的集合
  3. 增强(advice):就是我们要具体做的事情,也就在原有的方法之上添加新的能力
  4. 目标对象:就是我们要增强的类;
  5. 引入:特殊的增强,为类添加属性和方法。哪怕这个类没有任何的属性和方法我们也可以通过aop去添加方法和属性实现逻辑
  6. 织入(weaving):就是把增强添加到目标类具体的连接点上的过程
  7. 代理:一个类被AOP织入增强后产生的结果类,他是原类和增强后的代理类,更具代理s不同的方式,代理类可能是和原类具有相同接口的类,也可能是原类的子类
  8. 切面(Aspect):切面由切点和增强组成,他既包含横切的定义,也包括了连接点的定义,spring aop就是负责实施切面的框架,他将切面定义为横切逻辑织入到切面所指定的连接点

spirng中aop的实现

首先在pom.xml中导入依赖:

	<dependencies>
        <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- spring-aspects会帮我们传递过来aspectjweaver -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.1</version>
        </dependency>
    </dependencies>

方式一:spring原生的接口

然后观察其导入的jar包
在这里插入图片描述

只要实现下面对应的spring接口即可完成原生的springAop
在这里插入图片描述
新建一个SpringProxyTest 类,然后实现spring中的aop相关的一些接口:

package com.gothic.sunset;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;

public class SpringProxyTest implements MethodBeforeAdvice, AfterReturningAdvice {

    /**
     *
     * @param method 要执行的目标对象的方法名
     * @param objects 参数数组
     * @param o 要执行的目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("执行开始--->"+o.getClass().getName()+"中的方法"+method.getName()+"----参数为:"+ Arrays.toString(objects));
    }

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行完毕--->"+target.getClass().getName()+"中的方法"+method.getName()+"----参数为:"+ Arrays.toString(args)+"结果为:"+returnValue.toString());
    }
}

我这里只实现类前置通知和后置通知,其余的大家可自行补充,和jdk动态代理也差不多(其实还是主要用注解去设置,这里只是另一种方法,注解会在下面使用)。
编写spring配置文件:

  1. 在spring中向ioc容器注册bean:
 	<!--注册bean,也可以使用注解,这里再回忆一下-->
    <bean id="calculate" class="com.gothic.sunset.CalculateImpl"/>
    <bean id="springProxyTest" class="com.gothic.sunset.SpringProxyTest"/>
  1. 在spring配置文件中的头文件导入aop约束:
xmlns:aop="http://www.springframework.org/schema/aop"

3.配置aop:

	<!--aop配置,需要导入aop的约束-->
    <aop:config>
        <!--切入点  expression表达式 execution要执行的位置-->
        <aop:pointcut id="pt" expression="execution(* com.gothic.sunset.CalculateImpl.*(..))"/>
        <!--aop环绕通知-->
        <aop:advisor advice-ref="springProxyTest" pointcut-ref="pt"/>
    </aop:config>

junit测试用例:

import com.gothic.sunset.Calculate;
import com.gothic.sunset.CalculateImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest {
    @Test
    public void testAop(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calculate calculate = (Calculate) context.getBean("calculate");
        calculate.add(1,2);
    }

}

输出:
在这里插入图片描述

方式二:spring中注解配置aop

注解解释:

  1. 通过注解 @Aspect标识一个类作为切面类
  2. 前置通知:使用@Before注解标识,在被代理的目标方法前执行
  3. 返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行
  4. 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行
  5. 后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行
  6. 环绕通知:使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

JointPoint对象:
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
常用API:

方法名功能
Signature getSignature();获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();获取传入目标方法的参数对象
Object getTarget();获取被代理的对象
Object getThis();获取代理对象

新建一个AnnoatationPointCut 类,用来实现切面(将切面类通过注解@Aspect标识):

package com.gothic.sunset;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

@Aspect //标注这个类是一个切面类
public class AnnoatationPointCut {

    //切入点的重用,通过 @PointCut注解的value属性值来设置,然后其他的各种通知注解的value属性值,即可简写
    @Pointcut("execution(* com.gothic.sunset.CalculateImpl.*(..))")
    public void pointCut(){};


    //获取连接点信息可以在通知方法的参数位置设置JoinPoint类型的形参
    //前置通知
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint){
        //获取目标对象签名信息
        Signature signature = joinPoint.getSignature();
        //获取目标对象的参数列表
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,前置通知,方法:"+signature.getName()+",参数:"+ Arrays.toString(args));
    }

    //后置通知
    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,后置通知,方法:"+signature.getName());
    }


    //回溯通知
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,回溯通知,方法:"+signature.getName()+",结果:"+result.toString());
    }


    //异常通知
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void afterThrowException(JoinPoint joinPoint,Throwable ex){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,回溯通知,方法:"+signature.getName()+",异常:"+ex);
    }


    //环绕通知   --- 切面类型的返回值必须与目标对象类型一致
    @Around("pointCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标方法的执行,目标方法的返回值一定要返回给外界调用者
            result = joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }
}

修改spring配置文件:

基于注解的aop需要使用<aop:aspectj-autoproxy/>,开启AspectJ的自动代理,为目标对象自动生成代理

测试输出:

import com.gothic.sunset.Calculate;
import com.gothic.sunset.CalculateImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mytest {
    @Test
    public void testAop(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Calculate calculate = (Calculate) context.getBean("calculate");
        calculate.add(1,2);
    }

}

在这里插入图片描述


好嘞,AOP就暂时到这里啦!!!!AOP的相关术语大家理解即可,不需要死究定义(根据例子慢慢感悟)。
在这里插入图片描述

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

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

相关文章

基于树莓派的freeRDP桌面云终端的简单实现

VDI虚拟桌面云系统可以通过RmoteFX, PCoIP,HDX等远程桌面显示协议将服务器端的虚拟操作系统桌面环境在远程云终端上显示出来。商用的VDI系统需要部署服务器端虚拟机管理软件如System Center, XenServer等软件,购买价格不菲的服务器硬件,以及500~1000元之间的云终端接入设备。…

模糊控制算法实例matlab程序

参考文献《智能控制——刘金锟》 以水位的模糊控制为例。如图4一4所示&#xff0c;设有一个水箱&#xff0c;通过调节阀可向内注水和向外抽水。设计一个模糊控制器&#xff0c;通过调节阀门将水位稳定在固定点附近。按照日常的操作经验&#xff0c;可以得到基本的控制规则为&am…

TikTok营销策略 如何打造TikTok爆款视频?

最近&#xff0c;电商商家们总在问&#xff0c;如何打造出TikTok爆款视频&#xff0c;以吸引更多浏览量&#xff0c;刺激TikTok变现。这里不难发现&#xff0c;视频创作也只是一种TikTok营销方式&#xff0c;今天就来说说如何打造TikTok爆款视频。TikTok视频营销策略一——清晰…

模型案例推荐:电力大数据项目案例模型分享

电力行业大数据项目模型 电力高架线路巡检绝缘子缺陷智能检测 涉及关键技术&#xff1a; 语义分割 目标检测 图像增强 图像切分 主要工具&#xff1a;Python 技术大类&#xff1a;计算机视觉 主要业务问题&#xff1a; 随着我国经济的高速发展&#xff0c;国民用电量逐年增…

SpringCloud - Sleuth分布式请求链路跟踪

文章目录一.概述二.zipkin搭建三.Sleuth链路监控实现1. payment8001生产者修改2. Consumer80消费者修改3. 测试一.概述 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会…

【信息检索与数据挖掘】期末笔记(一)

文章目录什么是信息检索词项-文档矩阵倒排索引构建过程前三步最后一步布尔检索模型布尔查询的处理查询优化如何存储词典哈希表树有序检索模型对基本布尔操作的扩展短语查询和位置索引短语查询第一个解决方案第二个解决方案更快的索引表合并什么是信息检索 信息检索是从大规模非…

nuxt3:我们开始吧!

一、背景介绍 2022 年 11 月 16 日&#xff0c;全球最大的 Nuxt 会议 Nuxt Nation 2022 在线举行&#xff0c;并正式发布了 Nuxt.js 3.0 的第一个稳定版本。Nuxt 3 是基于 Vite、Vue3 和 Nitro 的 Nuxt 框架的现代重写&#xff0c;具有一流的 Typescript 支持&#xff0c;是两…

深入浅出的给大家分析下现在做抖音短视频还来得及吗?

2500字&#xff0c;需要一些耐心用心读完&#xff0c;让自己对于未来更清晰一些&#xff01; 大家好&#xff0c;我是我赢助手短视频运营&#xff0c;专注于短视频运营和创作&#xff1b;今天给大家分享下现在还来得及做短视频吗&#xff1f;深入浅出的给大家分析下这个问题。…

加缪——人生到底有什么意义?生命的意义就是生命本身

核心问题&#xff1a;人生到底有什么意义&#xff1f;如果人生没有意义&#xff0c;那么人是否应该自杀&#xff1f; 如果不是&#xff0c;那么人要以什么样的态度活着&#xff1f; 目录 一、《局外人》 二、名言 三、「个人感悟」对活着意义的幡然醒悟 四、《西西弗神话》…

SpringBoot+Vue实现前后端分离的校园健康检测管理系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

整合Druid数据源

1.数据库连接池简介 数据库连接池是个容器&#xff0c;负责分配、管理数据库连接(Connection)。它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。…

如何构建 API 生态促进企业上下游合作

构建 API 生态是互联网趋势 技术变革推动API数量爆发式增长 互联网在这10多年里&#xff0c;已经经历了多次的技术变革&#xff1a; 2010年以前&#xff0c;还是比较传统的开发模式。单体架构&#xff0c;自己开发大部分的代码&#xff0c;只有少量边缘业务会使用开源的项目代…

二肽Ile-Ala,24787-73-3,H2N-IA-OH

Forms nanotubes as its retroanalog H-Ala-Ile-OH 形成纳米管作为它的反模拟h - ala - il - oh。 编号: 196850中文名称: 二肽Ile-Ala英文名: Ile-AlaCAS号: 24787-73-3单字母: H2N-IA-OH三字母: H2N-Ile-Ala-COOH氨基酸个数: 2分子式: C9H18N2O3平均分子量: 202.25精确分子量…

PyCharm安装教程

PyCharm安装教程第一阶段&#xff1a;安装python第二阶段&#xff1a;安装pycharm第三阶段&#xff1a;新建python项目安装包链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1JIbdnhupYLVHK6Q_fzNCcw?pwddvzo 提取码&#xff1a;dvzo 第一阶段&#xff1a;安装pyt…

2022CTF培训(四)花指令字符串混淆入门

附件下载链接 花指令 花指令的介绍 花指令(JunkCode)指的是使用一些技巧将代码复杂化&#xff0c;使人难以阅读的技术。广义上花指令与代码混淆(ObfusedCode)同义&#xff0c;包括结构混淆、分支混淆、语句膨胀等等 狭义上指的主要是干扰反汇编解析的技术。 花指令的原理 …

Sentinel规则持久化到Nacos教程

环境&#xff1a; 1、sentinel版本&#xff1a;1.8.6&#xff0c;下载地址&#xff1a;https://github.com/alibaba/Sentinel/releases/tag/1.8.6 2、nacos版本&#xff1a;2.1.2&#xff0c;下载地址&#xff1a;https://github.com/alibaba/nacos/releases 3、JDK版本&#x…

欧洲航天局(ESA)< ESA WorldCover 10m 2020 > 介绍

欧洲航天局&#xff08;ESA&#xff09;< ESA WorldCover 10m 2020 > 产品基于 Sentinel-1 和 Sentinel-2 数据提供了一种新的全球地表覆盖产品&#xff0c;分辨率为 10 米&#xff0c;时间为 2020 年。 该产品是在欧空局世界覆盖项目 < ESA WorldCover Project >的…

1. 认识复杂度和简单排序算法

1. 认识复杂度和简单排序算法 常数时间的操作&#xff0c;一个操作如果和样本的数据量没有关系&#xff0c;每次都是固定时间内完成的操作&#xff0c;叫做常数操作。 例子: int a arr[i];时间复杂度为一个算法流程中&#xff0c;常数操作数量的一个指标。常用O(读作big O)来…

基于随机森林、svm、CNN机器学习的风控欺诈识别模型

在信息爆炸时代&#xff0c;“信用”已成为越来越重要的无形财产。 ”数据风控“的实际意义是用DT&#xff08;Data Technology&#xff09;识别欺诈&#xff0c;将欺诈防患于未然&#xff0c;然后净化信用体系。 最近我们被客户要求撰写关于风控欺诈识别模型的研究报告&#x…

JavaScript基础语法(输出语句)

JavaScript基础语法&#xff08;输出语句&#xff09; 1.书写规范 区分大小写&#xff1a;与 Java 一样&#xff0c;变量名、函数名以及其他一切东西都是区分大小写的 每行结尾的分号可有可无 如果一行上写多个语句时&#xff0c;必须加分号用来区分多个语句。 注释 单行注释…