两种动态代理(可以看到代理类的样子,方便理解)

这里写目录标题

  • jdk动态代理例子
  • CGlib动态代理例子
  • 手写spring中的事务
  • 部分自定义注解版aop实现方式

Spring的两大重点,IOC和AOP,今天我们就来学AOP,众所周知AOP的底层是动态代理,让我们看一下这两种动态代理的区别。

例子:
我们常常使用aop切面编程打印日志,但是他的底层是什么呢?我们常常看到的是封装好的注解,并不知道他的底层是如何实现的。
那我们下边先看手动调用动态代理实现执行方法前后打印日志。

jdk动态代理例子

在这里插入图片描述
客户端首先调用代理对象的方法
在这里插入图片描述

CalculatorProxy类

package AOP.Proxy;

import AOP.service.Calculator;
import AOP.util.Util;

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

/**
 * @BelongsProject: JAVAtest
 * @BelongsPackage: AOP.Proxy
 * @Author: GuoYuan.Zhao
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2024-01-24 14:47
 * @Version: 1.0
 */

public class CalculatorProxy {
    //必须要有接口,如果没有接口,不能使用,这种方式是用jdk提供的reflect 包下边的类,但是
    //生产环境中我不能保证每个类都有具体的接口,所有有第二中方式cglib

    //两种动态代理的方式,一种是JDK   一种是cglib
    public  static Calculator  getCalculator(  final Calculator  calculator){

        //获取被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();

        Class<?>[] interfaces  = calculator.getClass().getInterfaces();
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try{
                    System.out.println(method.getName()+"方法开始执行,参数列表是:"+ Arrays.asList(args));

                    result = method.invoke(calculator,args);
                System.out.println(method.getName()+"方法结束执行,参数列表是:"+ result);
                }catch (Exception e){
                    System.out.println(method.getName()+"方法抛出异常"+e.getMessage());

                }finally {
                    System.out.println(method.getName()+"方法执行结束over");

                }
//
                return result;
            }
        };
        Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
        return (Calculator) instance;
    }





Calculator接口



//        //获取被代理对象的类加载器
//        ClassLoader loader = calculator.getClass().getClassLoader();
//
//        Class<?>[] interfaces  = calculator.getClass().getInterfaces();
//        InvocationHandler handler = new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                Object result = null;
//                try{
                    System.out.println(method.getName()+"方法开始执行,参数列表是:"+ Arrays.asList(args));
//                    Util.start(method,args);
//                     result = method.invoke(calculator,args);
                System.out.println(method.getName()+"方法开始执行,参数列表是:"+ result);
//                    Util.stop(method,result);
//                }catch (Exception e){
                    System.out.println(method.getName()+"方法抛出异常"+e.getMessage());
//                    Util.logExpection(method,e);
//                }finally {
                    System.out.println(method.getName()+"方法执行结束over");
//
//                    Util.logFinally(method);
//                }

//                return result;
//            }
//        };
//        Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
//        return (Calculator) instance;
//    }


MyCalculator类

//        //获取被代理对象的类加载器
//        ClassLoader loader = calculator.getClass().getClassLoader();
//
//        Class<?>[] interfaces  = calculator.getClass().getInterfaces();
//        InvocationHandler handler = new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                Object result = null;
//                try{
                    System.out.println(method.getName()+"方法开始执行,参数列表是:"+ Arrays.asList(args));
//                    Util.start(method,args);
//                    result = method.invoke(calculator,args);
                System.out.println(method.getName()+"方法开始执行,参数列表是:"+ result);
//                    Util.stop(method,result);
//                }catch (Exception e){
                    System.out.println(method.getName()+"方法抛出异常"+e.getMessage());
//                    Util.logExpection(method,e);
//                }finally {
                    System.out.println(method.getName()+"方法执行结束over");
//
//                    Util.logFinally(method);
//                }

//                return result;
//            }
//        };
//        Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
//        return (Calculator) instance;

}

public interface Calculator {
    public Integer  add(Integer i,Integer j) throws NoSuchMethodException;

    public Integer  div(Integer i,Integer j);
    public Integer  mul(Integer i,Integer j);
    public Integer  sub(Integer i,Integer j);


}

package AOP.service;

import AOP.util.Util;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;

/**
 * @BelongsProject: JAVAtest
 * @BelongsPackage: AOP.service
 * @Author: GuoYuan.Zhao
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2024-01-24 14:15
 * @Version: 1.0
 */

@Service
public class MyCalculator implements  Calculator{

    @Override
    public Integer add(Integer i, Integer j) throws NoSuchMethodException {

        //11111
//        System.out.println("add方法开始执行:参数是"+i+"___"+j);
//        int result = i+j;
//        System.out.println("add方法结束,执行结果是"+result);
//        return result;


        //22222

//        Method add = MyCalculator.class.getMethod("add", Integer.class, Integer.class);
//        Util.start("add",i,j);
//        Integer result = i+j;
//        Util.stop(add,result);
//        return result;


        //333333
        Integer result = i+j;
        return result;
    }

    @Override
    public Integer div(Integer i, Integer j) {

        System.out.println("div方法开始执行:参数是"+i+"___"+j);
        Integer result = i/j;
        System.out.println("div方法结束,执行结果是"+result);
        return result;
    }

    @Override
    public Integer mul(Integer i, Integer j) {


        System.out.println("mul方法开始执行:参数是"+i+"___"+j);
        int result = i*j;
        System.out.println("mul方法结束,执行结果是"+result);
        return result;
    }

    @Override
    public Integer sub(Integer i, Integer j) {
        System.out.println("sub方法开始执行:参数是"+i+"___"+j);
        Integer result = i-j;
        System.out.println("sub方法结束,执行结果是"+result);
        return result;
    }
}

客户端


主函数{
 Calculator calculator = CalculatorProxy.getCalculator(new MyCalculator());
        calculator.add(1,1);
        saveProxyClass("E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");

}

  public static void saveProxyClass(String path) throws IOException {
        byte[] $proxy1s = ProxyGenerator.generateProxyClass("$Proxy1", MyCalculator.class.getInterfaces());

        FileOutputStream out = new FileOutputStream(new File(path + "$Proxy1.class"));

        out.write($proxy1s);
    }

最后可以看到代理类
在这里插入图片描述
这个类里边有 我的目标方法,其实客户端调用的就是这个方法
在这里插入图片描述
然后h就是我们上边那个匿名内部类的对象

CGlib动态代理例子

CglibFactory类

public class CglibFactory  implements MethodInterceptor {


    public CglibFactory(MyCalculatorCGlib target) {
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行方法"+ method.getName());
        Object result = methodProxy.invokeSuper(o, objects);

        //这里实现将返回值字符串变为大写的逻辑
//        if(result != null) {
//            result = ((String) result).toUpperCase();
//        }
        System.out.println("执行方法完毕结果是"+result);
        return result;
    }

    public MyCalculatorCGlib myCglibCreator() {

        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");

        Enhancer enhancer = new Enhancer();
        //将目标类设置为父类,cglib动态代理增强的原理就是子类增强父类,cglib不能增强目标类为final的类
        //因为final类不能有子类
        enhancer.setSuperclass(MyCalculatorCGlib.class);
        //设置回调接口,这里的MethodInterceptor实现类回调接口,而我们又实现了MethodInterceptor,其实
        //这里的回调接口就是本类对象,调用的方法其实就是intercept()方法
        enhancer.setCallback(this);
        //create()方法用于创建cglib动态代理对象
        return (MyCalculatorCGlib) enhancer.create();
    }
}

MyCalculatorCGlib类

public class MyCalculatorCGlib {
    public Integer add(int i, int j) {
        Integer result = i+j;
        return result;
    }

    public void doSecond() {
//        System.out.println("doSecond()方法");
    }

}

客户端

     MyCalculatorCGlib target = new MyCalculatorCGlib();
//        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E:\\zy\\TGB-zgy-2022\\MiMust\\MiDesignDemo\\JAVAtest\\NormalTest1\\demo\\src");
        MyCalculatorCGlib proxy = new CglibFactory(target).myCglibCreator();
        Integer result = proxy.add(1,1);

按照上边的思路,说完上边两种代理方式,我们下边来说如何使用这种方式手写事务呢

手写spring中的事务

首先从原理分析,事务的原理就是当sql出现问题的时候,数据回滚为原来的样子,具体他们是通过spring中的DataSourceTransactionManager实现的,在sql执行前,开启事务事务,然后执行sql,如果正常就提交事务,如果不正常就回滚事务。
这就是大概得逻辑,是不是就像上边打印日志一样,在执行方法前进行一些操作,在执行方法后进行一些操作。

在这里插入图片描述

public class Main {
    public static void main(String[] args)  {
//        TransactionManager transactionManager = new SimpleTransactionManager();
//        TransactionProxyFactory factory = new TransactionProxyFactory(transactionManager);
//        MyService service = factory.createProxy(new MyService());
//        service.doSomething(); // 这个调用将被代理拦截,并处理事务


        MyService myService = new MyServiceImpl();
        TransactionManager transactionManager = new SimpleTransactionManager();
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai");

        dataSource.setUsername("root");
        dataSource.setPassword("123456");

        dataSourceTransactionManager.setDataSource(dataSource);

        transactionManager.setDataSourceTransactionManager(dataSourceTransactionManager);

        // 创建代理对象
        MyService proxy = TransactionalInvocationHandler.createProxy(myService, transactionManager, MyService.class);

        // 调用代理对象的方法,这将触发事务管理

            proxy.doSomething();

        }
}

@Service
public interface MyService {

//    @Transactional
    public void doSomething()  ;
}

@Service
public class MyServiceImpl implements MyService {
    @Override
    public void doSomething()  {

        try {
            System.out.println("我真的要执行sql语句");
            int i = 1/0;
        } catch (ArithmeticException e) {
            // 处理算术异常,例如记录日志或返回错误消息
            throw new RuntimeException("An arithmetic operation has failed", e);
        }

    }
}
@Component
public interface TransactionManager {

    TransactionStatus beginTransaction();
    void commitTransaction(TransactionStatus transactionStatus);
    void rollbackTransaction(TransactionStatus transactionStatus);

    public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) ;

}
@Component
public class SimpleTransactionManager implements TransactionManager {
    private boolean isActive = false;

    @Override
    public void setDataSourceTransactionManager(DataSourceTransactionManager dataSourceTransactionManager) {
        this.dataSourceTransactionManager = dataSourceTransactionManager;
    }

    private  DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();



    @Override
    public TransactionStatus beginTransaction()  {
        if (isActive) {
            throw new IllegalStateException("Transaction already active");
        }
        // 模拟事务开始逻辑(在实际应用中,这里会涉及到数据库连接的获取、设置隔离级别等操作)
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());

        isActive = true;
        System.out.println("开启事务了");
        return  transaction;
    }

    @Override
    public void commitTransaction(TransactionStatus transactionStatus) {
        if (!isActive) {
            throw new IllegalStateException("No active transaction");
        }
        // 模拟事务提交逻辑
        dataSourceTransactionManager.commit(transactionStatus);

        isActive = false;
        System.out.println("提交事务了");
    }

    @Override
    public void rollbackTransaction(TransactionStatus transactionStatus)  {
        if (!isActive) {
            throw new IllegalStateException("No active transaction");
        }
        // 模拟事务回滚逻辑
        dataSourceTransactionManager.rollback(transactionStatus);
        isActive = false;
        System.out.println("回滚事务了");
    }
}
public class TransactionalInvocationHandler implements InvocationHandler {
    @Autowired
    private final Object target;

    @Autowired
    private final TransactionManager transactionManager;

    public TransactionalInvocationHandler(Object target, TransactionManager transactionManager) {
        this.target = target;
        this.transactionManager = transactionManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
        TransactionStatus transactionStatus = null;
        try {
            transactionStatus = transactionManager.beginTransaction();
            Object result = method.invoke(target, args);
            transactionManager.commitTransaction(transactionStatus);
            return result;
        } catch (Exception e) {
            transactionManager.rollbackTransaction(transactionStatus);
            throw e;
        }
    }

    public static <T> T createProxy(T target, TransactionManager transactionManager, Class<T> interfaceType) {
        return (T) Proxy.newProxyInstance(
                interfaceType.getClassLoader(),
                new Class<?>[]{interfaceType},
                new TransactionalInvocationHandler(target, transactionManager)
        );
    }
}

运行结果:
在这里插入图片描述

部分自定义注解版aop实现方式

在这里插入图片描述

自定义注解 MyTransactionAnnotation

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTransactionAnnotation {
}

MyTransaction类

@Component
@Slf4j
public class MyTransaction {

    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * 开启事务,并配置默认的事务传播机制
     * @return
     */
    public TransactionStatus begin(){
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        log.info("事务开启成功");
        return transaction;
    }


    /**
     * 事务提交
     * @param transaction
     */
    public void commit(TransactionStatus transaction){
        log.info("事务提交成功");
        dataSourceTransactionManager.commit(transaction);
    }

    /**
     * 事务回滚
     * @param transaction
     */
    public void rollback(TransactionStatus transaction){
        log.info("事务回滚成功");
        dataSourceTransactionManager.rollback(transaction);
    }
}

MyTransactionAop类

@Slf4j
@Aspect
@Component
public class MyTransactionAop {

    @Autowired
    private MyTransaction myTransaction;
    @Around(value = "@annotation(com.transaction.annotation.MyTransactionAnnotation)")
    public Object myTransactionAop(ProceedingJoinPoint joinPoint){

        log.info("进入到事务aop, 准备开启事务");
        TransactionStatus transactionStatus = myTransaction.begin();
        try {
            log.info("准备执行目标方法");
            // 执行目标方法
            Object object = joinPoint.proceed();

            log.info("目标方法执行完毕,准备提交");

            // 执行方法完毕,提交事务,并返回
            myTransaction.commit(transactionStatus);
            return object;
        }catch (Throwable throwable) {

            log.error("目标函数异常,手动回滚");
            // 目标函数异常,手动回滚
            myTransaction.rollback(transactionStatus);
            return "目标函数异常";
        }
    }
}

TestController类

@RestController
@Slf4j
public class TestController {

    @GetMapping("test_transaction")
    @MyTransactionAnnotation
    public String testTransaction(@RequestParam(value = "name") String name){

//        int i = 1/0;
        return name;

    }
}
@SpringBootApplication
@EnableTransactionManagement
public class HbzTransactionApplication {

    public static void main(String[] args) {

        SpringApplication.run(HbzTransactionApplication.class, args);
    }
}

application.yml文件

server:
  port: 9001
  servlet:
    context-path: /test
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

在这里插入图片描述

总结:当我们大概了解这个AOP的思想以后,再去看源码,发现都离不开动态代理,那就是离不开回调反射

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

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

相关文章

软件压力测试:测试方法与步骤详解

随着软件应用的不断发展&#xff0c;用户对系统性能的要求也逐渐提高。在不同的负载条件下&#xff0c;系统必须能够保持稳定、高效的运行。软件压力测试是一种验证系统在各种负载情况下性能表现的关键手段。本文将详细探讨软件压力测试的方法和步骤。 1. 明确测试目标 在进行压…

数字化转型:八大关键词揭示企业增长新引擎

导语&#xff1a; 在数字化浪潮的席卷下&#xff0c;企业正面临前所未有的转型挑战与增长机遇。为了把握这一时代的脉搏&#xff0c;我们需要深入剖析数字化转型的核心要素。本文将通过“洞察不确定性、驾驭复杂系统、重塑竞争边界、数据驱动决策、智能软件崛起、技术架构重塑…

WooCommerce商品采集与发布插件

如何采集商品或产品信息&#xff0c;并自动发布到Wordpress系统的WooCommerce商品&#xff1f; 推荐使用简数采集器&#xff0c;操作简单方便&#xff0c;且无缝衔接WooCommerce插件&#xff0c;快速完成商品的采集与发布。 简数采集器的智能自动生成采集规则和可视化操作功能…

C 语言基本语法及实用案例分享

一、什么是 C 语言&#xff1f; C语言是一种较早的程序设计语言&#xff0c;诞生于1972年的贝尔实验室。1972 年&#xff0c;Dennis Ritchie 设计了C语言&#xff0c;它继承了B语言的许多思想&#xff0c;并加入了数据类型的概念及其他特性。C语言是一门面向过程的计算机编程语…

AI绘画与修图:重塑数字艺术的新纪元

文章目录 一、AI绘画与修图的原理二、AI绘画的应用三、AI修图的优势四、面临的挑战五、未来发展趋势《AI绘画与修图实战&#xff1a;PhotoshopFirefly从入门到精通 轻松玩转AI绘画与修图实战》亮点内容简介作者简介 随着人工智能技术的飞速发展&#xff0c;AI绘画与修图已经成为…

DTV的LCN功能介绍

文章目录 LCN简介LCN获取LCN Conflict LCN简介 Logical Channel Number&#xff08;LCN&#xff09;是数字电视系统中用于标识和组织频道的逻辑编号。LCN的目的是为了方便用户浏览和选择频道&#xff0c;使得数字电视接收设备能够根据这些逻辑编号对频道进行排序和显示。 LCN…

掌握CSS盒子模型:一站式指南

更多web开发知识欢迎访问我的专栏>>> 盒子模型 01-选择器 结构伪类选择器 基本使用 作用&#xff1a;根据元素的结构关系查找元素。 选择器说明E:first-child匹配父元素的第一个子元素EE:last-child匹配父元素的最后一个子元素EE:nth-child(n)匹配父元素的第n个…

043-WEB攻防-PHP应用SQL注入符号拼接请求方法HTTP头JSON编码类

043-WEB攻防-PHP应用&SQL注入&符号拼接&请求方法&HTTP头&JSON&编码类 #知识点&#xff1a; 1、PHP-MYSQL-SQL注入-数据请求类型 2、PHP-MYSQL-SQL注入-数据请求方法 3、PHP-MYSQL-SQL注入-数据请求格式 演示案例&#xff1a; ➢PHP-MYSQL-数据请求类型…

java替换jar中的class文件

1、编译好class文件2、找到需要修改class文件的路径3、解压需要替换的文件4、上传编译后的class文件5、重新压缩 在调整java代码过程中会遇到需要改jar包中的class文件的情况&#xff0c;改了如何替换呢&#xff1f; 1、编译好class文件 将需要修改的class文件代码复制到java编…

第四套CCF信息学奥赛c++ CSP-J认证初级组 中小学信奥赛入门组初赛考前模拟冲刺题(选择题)

第四套中小学信息学奥赛CSP-J考前冲刺题 1、在网络上,若某台电脑的设备及数据可由其他电脑共享,这台电脑称为 A、个人服务器 B、主机 C、副机 D、服务器 答案&#xff1a;D 考点分析&#xff1a;主要考查网络相关知识&#xff0c;网络上这样的电脑统称为服务器&#xff0…

mac/windows git ssh 配置多平台账号(入门篇)

目录 引子多账号多平台配置git一、.ssh文件夹路径1.1 mac 系统1.2 windows 系统 二、生成new ssh2.1 mac系统2.2 windows 系统 三、配置 config四、验证五、用ssh方式拉取远程仓库代码 引子 push代码到github仓库时&#xff0c;提示报错。 Push failed Remote: Support for pa…

【自然语言处理】:实验5,司法阅读理解

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…

Unity接入SQLite (一):SQLite介绍

1.简介 SQLite是一个开源的嵌入式关系数据库管理系统。它是一种轻量级的数据库引擎&#xff0c;不需要单独的服务器进程&#xff0c;可以直接嵌入到应用程序中使用。Sqlite使用简单、高效&#xff0c;并且具有对标准SQL的完整支持。它适用于需要在本地存储和访问数据的应用程序…

hash,以及数据结构——map容器

1.hash是什么&#xff1f; 定义&#xff1a;hash,一般翻译做散列、杂凑&#xff0c;或音译为哈希&#xff0c;是把任意长度的输入&#xff08;又叫做预映射pre-image&#xff09;通过散列算法变换成固定长度的输出&#xff0c; 该输出就是散列值。这种转换是一种压缩映射&…

Maven depoly:Skipping artifact deployment

问题描述&#xff1a; 使用IDEA执行mvn depoly将本地开发的模块发布到Maven私服时&#xff0c;一直提示&#xff1a;Skipping artifact deployment&#xff0c;自动跳过了depoly部署阶段。 问题分析 Maven构建生命周期中的每一个阶段都是由对应的maven插件执行具体工作的。既然…

linux运维xshell同时控制多个窗口的快捷方式

下面去实现同时操作的功能。 1. 找到 工具- 2. 根据需要&#xff0c;选择需要操作的窗口即可。 以上就是对xshell中同时操作多个窗口的方法

【k8s核心概念与专业术语】

k8s架构 1、服务的分类 服务分类按如下图根据数据服务支撑&#xff0c;分为无状态和有状态 无状态引用如下所示&#xff0c;如果一个nginx服务&#xff0c;删除后重新部署有可以访问&#xff0c;这个属于无状态&#xff0c;不涉及到数据存储。 有状态服务&#xff0c;如redis&a…

四、矩阵的分类

目录 1、相等矩阵 2、同形矩阵 3、方阵&#xff1a; 4、负矩阵、上三角矩阵、下三角矩阵&#xff1a; 5、对角矩阵&#xff1a;是方阵 ​编辑7、单位矩阵&#xff1a;常常用 E或I 来表示。它是一个方阵 8、零矩阵&#xff1a; 9、对称矩阵&#xff1a;方阵 1、相等矩阵 …

力扣经典题目解析--两数之和

两数之和 题目地址: 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 简单来说就是在一个数组中找出两个数&#xff0c;这两个数相加要等于给定的target,下面是完整的题目: 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中…

阿里云SSL免费证书到期自动申请部署程序

阿里云的免费证书只有3个月的有效期&#xff0c;不注意就过期了&#xff0c;还要手动申请然后部署&#xff0c;很是麻烦&#xff0c;于是写了这个小工具。上班期间抽空写的&#xff0c;没有仔细测试&#xff0c;可能存在一些问题&#xff0c;大家可以自己clone代码改改&#xf…