简单实现Spring容器(六) 实现AOP机制

news2024/11/18 23:23:07

阶段5:

// 1.编写自己的Spring容器,实现扫描包,得到bean的class对象.
// 2.扫描将 bean 信息封装到 BeanDefinition对象,并放入到Map.
// 3.初始化单例池并完成getBean() createBean()方法
// 4.完成依赖注入(如果创建某个Bean对象,存在依赖注入,需要进行bean组装操作)
// 5.bean后置处理器实现,如果存在BeanPostProcessor扩展机制,就进行处理.
//   先完成原生Spring使用Bean后置处理器案例
//   再实现自己的bean的后置处理器
   6.实现AOP机制

思路:

先完成原生的Spring使用AOP机制,明白Aop机制需要:
后置处理器机制+动态代理机制

具体实现

1.在component包下写
(1)martAnimalable接口

package com.elf.spring.component;

/**
 * @author 45
 * @version 1.0
 */
public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}

(2)SmartDog类

package com.elf.spring.component;

import com.elf.spring.annotation.Component;

/**
 * @author 45
 * @version 1.0
 */
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable {
    public float getSum(float i, float j) {
        float res = i + j;
        System.out.println("SmartDog-getSum-res=" + res);
        return res;
    }

    public float getSub(float i, float j) {
        float res = i - j;
        System.out.println("SmartDog-getSub-res=" + res);
        return res;
    }
}

(3) 切面类

package com.elf.spring.component;
import com.elf.spring.annotation.AfterReturning;
import com.elf.spring.annotation.Aspect;
import com.elf.spring.annotation.Before;
import com.elf.spring.annotation.Component;

/**
 * @author 45
 * @version 1.0
 * 说明:SmartAnimalAspect当做一个切面类来使用
 * ,后面再分析如何做的更加灵活
 */
@Aspect //我们的注解
@Component //这是实现了
public class SmartAnimalAspect {

    @Before(value = "execution com.elf.spring.aop.aspectj.SmartDog getSum")
    public static void showBeginLog() {

        System.out.println("前置通知..");
    }

    @AfterReturning(value = "execution com.elf.spring.aop.aspectj.SmartDog getSum")
    public static void showSuccessLog() {

        System.out.println("返回通知..");
    }
}


2.annotation包下
<1> Aspect注解

package com.elf.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 45
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
    String value() default "";
}

<2>Before注解

package com.elf.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 45
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
    String value();

    String argNames() default "";
}

<3>After注解

package com.elf.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 45
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface After {
    String value();

    String argNames() default "";
}

<4>AfterReturning注解

package com.elf.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 45
 * @version 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
    String value() default "";

    String pointcut() default "";

    String returning() default "";

    String argNames() default "";
}

3.Spring包下写测试类 ElfTest

package com.elf.spring;

import com.elf.spring.annotation.AfterReturning;
import com.elf.spring.annotation.Before;
import com.elf.spring.component.SmartAnimalAspect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author 45
 * @version 1.0
 */
public class ElfTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

        //1. 获取SmartAnimalAspect的class对象
        Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;
        //2. 遍历该类的所有方法(因为如果要拿到切面类的切入方法的名称,以及注解肯定要一个一个遍历)
        for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {
            //如果切面类的方法有Before注解
            if (declaredMethod.isAnnotationPresent(Before.class)) {
                //得到切面类的切入方法名
                System.out.println("m:= " + declaredMethod.getName());
                //得到Before(value="xxxx")
                //得到Before注解
                Before annotation = declaredMethod.getAnnotation(Before.class);
                //得到Before注解的value
                System.out.println("value:= " + annotation.value());

                //得到切入要执行的方法.[反射基础]
                Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
                //调用切入方法[通过反射调用]
                declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);

            } else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {

                //如果发现切面类有AfterReturning注解,同样可以进行处理..
                System.out.println("m:= " + declaredMethod.getName());
                AfterReturning annotation = declaredMethod.getAnnotation(AfterReturning.class);
                System.out.println("value:= " + annotation.value());

                //得到切入要执行的方法.
                Method declaredMethod1 = smartAnimalAspectClass.getDeclaredMethod(declaredMethod.getName());
                //调用切入方法[反射调用]
                declaredMethod1.invoke(smartAnimalAspectClass.newInstance(), null);
            }
        }
    }
}

4.容器文件

package com.elf.spring.ioc;

import com.elf.spring.annotation.*;
import com.elf.spring.processor.BeanPostProcessor;
import com.elf.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author 45~
 * @version 1.0
 */
public class ElfSpringApplicationContext {
    //第一步,扫描包,得到bean的class对象,排除包下不是bean的,因此还没有放到容器中
    //因为现在写的spring容器比原先的基于注解的,要更加完善,所以不会直接把它放在ConcurrentHashMap
    private Class configClass;

    //定义属性BeanDefinitionMap -> 存放BeanDefinition对象
    private  ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();
    //定义属性SingletonObjects -> 存放单例对象 (跟原生容器的名字保持一致)
    //因为将来存放单例池的时候肯定要指定单例对象是对应哪个Bean的,所以k用String来充当
    //存放单例对象的类型是不确定的,可能是Dog,Cat,或者其他的对象,所以用Object
    private  ConcurrentHashMap<String, Object> singletonObjects =
            new ConcurrentHashMap<>();

    //定义一个属性ArrayList,存放后置处理器[]
    private List<BeanPostProcessor> beanPostProcessorList =
            new ArrayList<>();

    //构造器
    public ElfSpringApplicationContext(Class configClass) {

        //完成扫描指定包
        beanDefinitionsByScan(configClass);

        //通过beanDefinitionMap,初始化singletonObjects单例池
        //封装成方法
        //遍历所有的beanDefinition,用到集合和枚举的知识
        Enumeration<String> keys = beanDefinitionMap.keys();//把所有bean的名字拿到
        while (keys.hasMoreElements()) {
            //得到beanName
            String beanName = keys.nextElement();
            //通过beanName得到对应的beanDefinition对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //将该bean实例放入到singletonObjects集合
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
//        System.out.println("singletonObjects 单例池=" + singletonObjects);
//        System.out.println("beanDefinitionMap=" + beanDefinitionMap);

    }//构造器结束

    //该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,在放入到Map
    public void beanDefinitionsByScan(Class configClass) {
        this.configClass = configClass;

        /**获取要扫描的包:
         1.先得到ElfSpringConfig配置的 @ComponentScan(value= "com.elf.spring.component")
         2.通过 @ComponentScan的value => 即要扫描的包 **/
        ComponentScan componentScan =
                (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
//        System.out.println("要扫描的包path=" + path);

        /**
         * 得到要扫描包下的所有资源(类.class)
         * 1.得到类的加载器 -> APP类加载器是可以拿到 target目录下的classes所有文件的
         * 2.通过类的加载器获取到要扫描的包的资源url => 类似一个路径
         * 3.将要加载的资源(.class)路径下的文件进行遍历 => io
         */
        ClassLoader classLoader = ElfSpringApplicationContext.class.getClassLoader();
        path = path.replace(".", "/"); // 把.替换成 /
        URL resource = classLoader.getResource(path);
//        System.out.println("resource=" + resource);

        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) { //把所有的文件都取出来
//                System.out.println("============================");
//                System.out.println("f.getAbsolutePath()=" + f.getAbsolutePath());
                String fileAbsolutePath = f.getAbsolutePath();//到target的classes目录下了

                //这里只处理.class文件
                if (fileAbsolutePath.endsWith(".class")) {
                    //1.获取类名
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                            fileAbsolutePath.indexOf(".class"));
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    //System.out.println("classFullName=" + classFullName);
                    //3.判断该类是否需要注入,就看是不是有注解@Component @Service @Contoller @Re....
                    try {
                        Class<?> cla = classLoader.loadClass(classFullName);
                        if (cla.isAnnotationPresent(Component.class) ||
                                cla.isAnnotationPresent(Controller.class) ||
                                cla.isAnnotationPresent(Service.class) ||
                                cla.isAnnotationPresent(Repository.class)) {
                            //演示机制
                            //如果该类使用了@Component注解,说明是一个Spring bean
                            System.out.println("这是一个Spring bean=" + cla + " 类名=" + className);

                            //1.真实的还是走下面是代码,为了方便,这里将后置处理器放到一个ArrayList
                            //2.如果发现是一个后置处理器,就将其放入到beanPostProcessorList
                            //3. 在原生的Spring容器中, 对后置处理器还是走的getBean, createBean
                            //   , 但是需要我们在singletonObjects 加入相应的业务逻辑
                            //4. 因为这里我们是为了讲解后置处理去的机制,我就简化
                            //5. 如果小伙伴们,仍然走以前的逻辑,也可以,就是要麻烦一点


                            //判断当前的这个cla有没有实现BeanPostProcessor
                            //说明, 这里我们不能使用 instanceof 来判断cla是否实现了BeanPostProcessor
                            //原因: cla不是一个实例对象,而是一个类对象/cla, 使用isAssignableFrom
                            //小伙伴将其当做一个语法理解
                            if (BeanPostProcessor.class.isAssignableFrom(cla)) {

                                BeanPostProcessor beanPostProcessor =
                                        (BeanPostProcessor) cla.newInstance();
                                //放入到beanPostProcessorList
                                beanPostProcessorList.add(beanPostProcessor);
                                continue;//把后置处理器放好以后就不在往下走,让它去调用create方法
                            }



                            //先得到beanName(有可能通过经典4注解,例如Component注解的value值来指定)
                            //1.得到类上的Component注解,此时的clazz已经是当前bean的class对象,通过类加载器得到的 反射知识
                            Component componentAnnotation = cla.getDeclaredAnnotation(Component.class);
                            //2.得到配置的value
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName)) {//如果没有写value,空串
                                //将该类的类名首字母小写作为beanName
                                //StringUtils其实是在springframework的框架下面的类,而这里本身我就是要自己写所以不用

                                beanName = StringUtils.uncapitalize(className);
                            }
                            //3.将Bean的信息封装到BeanDefinition对象中,然后将其放入到BeanDefinitionMap集合中
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //!!!多看看这里多理解
                            beanDefinition.setClazz(cla);//由类加载器通过反射得到对象,Class<?> cla = classLoader.loadClass(classFullName);
                            //4.获取Scope值
                            if (cla.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,就获取它配置的值
                                Scope scopeAnnotation = cla.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置Scope,就以默认的值singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinitionMap对象放入Map
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            //如果该类没有使用了@Component注解,说明是一个Spring bean
                            System.out.println("这不是一个Spring bean" + cla + " 类名=" + className);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
//                System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
            }//遍历文件结束
        }
    }

    //完成createBean(BeanDefinition beanDefinition)方法
    private  Object createBean(String beanName,BeanDefinition beanDefinition) {
        //得到Bean的Clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到实例
            Object instance = clazz.getDeclaredConstructor().newInstance();

            //这里加入依赖注入的业务逻辑!!!
            //1.遍历当前要创建的对象的所有字段( 比如:要创建MonsterService,需要把所有的字段都扫描一遍,看看字段上有没有@Autowired的注解)
            for(Field declaredField : clazz.getDeclaredFields()){
               //2.判断这个字段是否有@Autwired的修饰
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    //提示:这里处理@Autowired的required
//                    Autowired annotation = declaredField.getAnnotation(Autowired.class);
//                    annotation.required()=>进行下一步其他处理

                    //3.得到这个字段的名字
                    String name = declaredField.getName();
                    //4.通过getBean()方法来获取要组装的对象
                    Object bean = getBean(name);
                    //5.进行组装
                    declaredField.setAccessible(true);//因为反射里属性是私有的,须进行暴破.
                    declaredField.set(instance,bean);
                }
            }
            System.out.println("========创建好实例=========" + instance);

            //我们在bean的初始化方法前,调用后置处理器的before方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
               //在后置处理器的before方法,可以对容器生成的bean实例进行处理
                //然后返回处理后的bean实例,相当于做了一个前置处理
                Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
                if(current != null){//判断后置处理器的before方法,返回来的对象是不是一个空,
                    //如果不是空,再去改变
                    //如果是一个空,就不用去该说明还是以前的instance
                    //原生SPringl容器比这个复杂
                    instance = current;
                }
            }

            //这里判断是否要执行Bean的初始化方法
            //1.判断当前创建的Bean对象是否实现了InitializingBean接口
            //2.instanceof是比较操作符,基础 PPT363,
            // 表示判断某个对象的运行类型,是不是某个类型或者某个类型的子类型
            //如果某个对象instance 实现了这个接口InitializingBean,那它可以认为是这个接口的子类型,那就是true
            if(instance instanceof InitializingBean){
                //3.将instance转成InitializingBean接口类型
                //接口编程的具体体现
                try {
                    ((InitializingBean)instance).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            //我们在bean的初始化方法后,调用后置处理器的after方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                //在后置处理器的after方法,可以对容器生成的bean实例进行处理
                //然后返回处理后的bean实例,相当于做了一个后置处理
                Object current = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            if(current != null){
                instance = current;
            }
            }

            System.out.println("-------------------------------------------");
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //如果反射创建对象失败
        return null;
    }

    //编写getBean(String name)方法,返回容器中的对象.
    // 既然是getBean()那么返回类型是Object,因为不管是单例池还是创建的对象也好,类型是不确定的
    public  Object getBean(String name) {
        //加一个判断,严谨. 传入的beanName是否在beanDefinitonMap中存在..
        if(beanDefinitionMap.containsKey(name)) {//如果存在
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            //得到beanDefinition的scope,分别进行处理
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //说明是单例配置,就直接从单例池获取
                return singletonObjects.get(name);
            } else {//如果不是单例的,我们就调用createBean, 反射一个对象
                return createBean(name,beanDefinition);
            }
        } else { //如果不存在,就抛出一个空指针异常,也可自定义
            throw new NullPointerException("没有该bean");

        }

    }

}

运行结果

在这里插入图片描述
PPT89%的部分有扩展思考…

自我梳理: Spring容器是怎么工作的?

== 1.首先去扫描bean.xml文件或者配置文件(通过注解来获取),以拿到Conponent包.
2.拿到过后先进行扫描,得到bean里的class对象,因为这个包下的有些bean是不需要进行注入的,所以需要排除不是bean的class.
3.将扫描到的bean信息先封装到definition对象去,再放入到Map中,放好以后在这个过程中,还要完成很多任务:
eg: 初始化单例池,也就是说如果bean是单例的,就实例化并放入到单例池中,供你使用.但若为多例,最终是把bean的信息放在了beanDefinitionMap中(其里面放到是key-value,k指beanName,value指BeanDefinition对象[对象里最核有2点: class对象, scope表明它是单例还是多例],如果是多实例的对动态地返回对象,如果是单例的直接从单例池中返回即可)
4.要获取的时候执行getBean()方法:思路是首先根据传入的名字到BeanDefinitionMap里面去拿BeanDefinition对象,拿到后看是一个单例的还是一个多例的,如果是单例的直接从单例池获取,如果是protorype,通过到BeanDefinitionMap中得到Bean的class对象,使用反射,创建bean对象返回.这就是ioc的一个机制.
5.Spring的扩展功能:
(1)依赖注入,就是在createBean的时候判断是否需要依赖注入,通过@Autowired来进行匹配,还是基于BeanDefinitionMap来处理的,进行装配.
(2)Bean的后置处理机制BeanPostProcessor
(3)AOP的机制是基于Bean的后置处理机制和动态代理的,因为是在后置处理器的after方法里面通过一个处理返回了一个动态代理对象交给使用者,使用者通过调用这个方法,比如getSum()实际上是走的动态代理对象的invoke()方法来实现这样一个动态调用的,在这个过程可以进行前置通知,返回通知,异常通知,最终通知来进行切入.==

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

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

相关文章

12.11QSS优化界面——对话框

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

c语言为什么要引入变量

大家好&#xff0c;今天给大家介绍c语言为什么要引入变量&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 C语言引入变量的原因主要是为了存储数据并且方便后续的操作和计算。 变…

深入解析C++中的虚函数和虚继承:实现多态性与继承关系的高级特性

这里写目录标题 虚函数虚函数实现动态绑定虚继承抽象类 虚函数 虚函数是在C中用于实现多态性的一种特殊函数。它通过使用关键字"virtual"进行声明&#xff0c;在基类中定义&#xff0c;可在派生类中进行重写。虚函数允许在运行时根据对象的实际类型来调用相应的函数…

FTR223限时回归?经典三花再加金翅膀,CL500特别款亮相

FTR223可以说是非常经典的一款本田小攀爬车型了&#xff0c;之前我还有幸玩过一段时间&#xff0c;最近本田在泰国车展上展出了CL500的特别版&#xff0c;其中FTR223纪念版的版画让人眼前一亮&#xff0c;经典的白、红、蓝三色搭配让人眼前一亮。 CL500这台车在国内今年刚上市&…

开关量防抖滤波器(梯形图和SCL源代码)

模拟量防抖超限报警功能块请查看下面文章链接: https://rxxw-control.blog.csdn.net/article/details/133969425https://rxxw-control.blog.csdn.net/article/details/133969425 1、开关量防抖滤波器 2、防抖滤波 3、梯形图代码

设计模式(二)-创建者模式(5)-建造者模式

一、为何需要建造者模式&#xff08;Builder&#xff09;? 在软件系统中&#xff0c;会存在一个复杂的对象&#xff0c;复杂在于该对象包含了很多不同的功能模块。该对象里的各个部分都是按照一定的算法组合起来的。 为了要使得复杂对象里的各个部分的独立性&#xff0c;以及…

透析跳跃游戏

关卡名 理解与贪心有关的高频问题 我会了✔️ 内容 1.理解跳跃游戏问题如何判断是否能到达终点 ✔️ 2.如果能到终点&#xff0c;如何确定最少跳跃次数 ✔️ 1. 跳跃游戏 leetCode 55 给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。数组中的每个元素代表…

轻松构建超市管理小程序

随着科技的发展&#xff0c;越来越多的超市开始使用管理系统来提高效率、提升顾客体验和管理库存。如果你也想要为自己的超市打造一个便捷、高效的小程序&#xff0c;下面将为你提供一些帮助。 首先&#xff0c;你需要打开乔拓云第三方平台。在这个平台上&#xff0c;你可以轻松…

云降水物理基础

云降水物理基础 云的分类 相对湿度变化方程 由相对湿度的定义&#xff0c;两边取对数之后可以推出 联立克劳修斯-克拉佩龙方程&#xff08;L和R都为常数&#xff09; 由右式看出&#xff0c;增加相对湿度的方式&#xff1a;增加水汽&#xff08;de增大&#xff09;和降低…

【后端学前端】第二天 css动画 动感菜单(css变量、过渡动画、过渡延迟、js动态切换菜单)

目录 1、学习信息 2、源码 3、变量 1.1 定义变量 1.2 使用变量 1.3 calc() 函数 4、定位absolute和fixed 5、transform 和 transition&#xff0c;动画 5.1 变形transform 5.2 transition 5.3 动画animation 6、todo 1、学习信息 视频地址&#xff1a;css动画 动感菜…

大一作业习题

第一题&#xff1a;答案&#xff1a; #include <stdio.h> void sort(int a[], int m) //将数组a的前m个元素(从小到大)排序 {int i 0;for (i 0; i < m - 1; i){int j 0;int flag 1;for (j 0; j < m - 1 - i; j){if (a[j] > a[j 1]){int t 0;t a[j];…

数据科学实践:探索数据驱动的决策

写在前面 你是否曾经困扰于如何从海量的数据中提取有价值的信息?你是否想过如何利用数据来指导你的决策,让你的决策更加科学和精确?如果你有这样的困扰和疑问,那么你来对了地方。这篇文章将引导你走进数据科学的世界,探索数据驱动的决策。 1.数据科学的基本原则 在我们…

AGM离线下载器使用说明

AGM专用离线下载器示意图&#xff1a; 供电方式&#xff1a; 通过 USB 接口给下载器供电&#xff0c;跳线 JP 断开。如果客户 PCB 的 JTAG 口不能提供 3.3V 电源&#xff0c;或仅需烧写下载器&#xff0c;尚未连接用户 PCB 时&#xff0c;采用此种方式供电。 或者&#xff1a…

测距传感器

测距传感器 电子元器件百科 文章目录 测距传感器前言一、测距传感器是什么二、测距传感器的类别三、测距传感器的应用实例四、测距传感器的作用原理总结前言 测距传感器广泛应用于自动化控制、机器人导航、无人驾驶、测量仪器等领域。不同类型的测距传感器具有不同的测距范围、…

从零开始实现神经网络(三)_RNN循环神经网络

参考文章&#xff1a;rnn循环神经网络介绍 循环神经网络 &#xff08;RNN&#xff09; 是一种专门处理序列的神经网络。它们通常用于自然语言处理 &#xff08;NLP&#xff09; 任务&#xff0c;因为它们在处理文本方面很有效。在这篇文章中&#xff0c;我们将探讨什么是 RNN&a…

【ClickHouse】ClickHouse与MySQL之间实时同步数据(MySQL引擎),将MySQL数据实时同步到clickhouse

参考1:MySQL(通过该配置实现了实时同步) 参考2:experimental MaterializedMySQL 参考3:[experimental] MaterializedMySQL(包含设置 allow_experimental_database_materialized_mysql) MySQL引擎用于将远程的MySQL服务器中的表映射到ClickHouse中&#xff0c;并允许您对表进行I…

GitHub 跑了 1200 多台 MySQL 主机,如何实现无缝升级到 8.0 版本?

文章目录 翻译概述前言升级的动机GitHub 的 MySQL 基础设施准备旅程准备基础设施以进行升级确保应用程序兼容性沟通和透明度升级计划第 1 步&#xff1a;滚动副本升级步骤 2&#xff1a;更新复制拓扑步骤 3&#xff1a;将 MySQL 8.0 主机提升为主主机步骤 4&#xff1a;升级面向…

【Linux】地址空间

本片博客将重点回答三个问题 什么是地址空间&#xff1f; 地址空间是如何设计的&#xff1f; 为什么要有地址空间&#xff1f; 程序地址空间排布图 在32位下&#xff0c;一个进程的地址空间&#xff0c;取值范围是0x0000 0000~ 0xFFFF FFFF 回答三个问题之前我们先来证明地址空…

openlayers-19-分屏对比

分屏对比实现很简单&#xff0c;定义两个map对象&#xff0c;然后让这两个map对象共用一个view即可。 代码如下&#xff1a; <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd…

Linux上编译和测试V8引擎源码

介绍 V8引擎是一款高性能的JavaScript引擎&#xff0c;广泛应用于Chrome浏览器和Node.js等项目中。在本篇博客中&#xff0c;我们将介绍如何在Linux系统上使用depot_tools工具编译和测试V8引擎源码。 步骤一&#xff1a;安装depot_tools depot_tools是一个用于Chromium开发…