简单实现Spring容器(五) 实现bean后置处理器BeanPostProcessor机制

news2024/11/16 2:47:21

阶段5:

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

思路:

Spring容器中可能有多个后置处理器,所以需要考虑如果别人配置了多个Bean后置处理器,怎么处理,怎么切入到整个Spring容器里.

代码实现:

1.定义一个InitializingBean接口

package com.hspedu.spring.processor;

/**
 * @author 45
 * @version 1.0
 * 1. 我们根据原生Spring 定义了一个InitializingBean
 * 2. 该InitializingBean接口有一个方法void afterPropertiesSet() throws Exception;
 * 3. afterPropertiesSet() 在Bean的 setter后执行,即就是我们原来的初始化方法
 * 4. 当一个Bean实现这个接口后,就实现afterPropertiesSet() , 这个方法就是初始化方法
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}


2.MonsterService去实现接口

package com.elf.spring.component;
import com.elf.spring.annotation.Autowired;
import com.elf.spring.annotation.Component;
import com.elf.spring.annotation.Scope;
import com.elf.spring.processor.InitializingBean;

/**
 * @author 45~
 * @version 1.0
 * 说明 MonsterService 是一个Servic
 */
@Component //把MonsterService注入到我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
    //这里@Autowired匹配的形式很多,不用每个都去实现,这里先按名字来装配
    //这里使用的是自己定义的@Autowired来修饰属性,表示该属性,是通过容器完成依赖注入的
    //说明:我们实现按照名字来进行组装
    @Autowired
    private MonsterDao monsterDao;
    public void m1(){
        monsterDao.hi();
    }

    /**
     * 1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
     * 2 即就是初始化方法
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {//这个方法是在Spring容器里边被调用的,你不会主动地调用它,
        //因为现在是用容器来搞定这个事
        System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
    }
}

3.在创建好Bean实例后,判断是否需要初始化,容器文件

在容器中常用的一个方法是:根据该类是否实现了某个接口来判断是否要执行某个业务逻辑,这里就是基础的接口编程的实际应用
有一种接口叫标记接口,里面一个方法都没有,它实现这个接口的价值就是在它底层去判断你这个类是否要去走一个业务,Serilaizable接口就是,实现了这个接口但并没有实现任何方法,它其实就是个标记接口.(标记接口主要是给底层使用的=>看到你有这个接口,我就要干什么事情.)

运行看实现了接口的Bean在创建好后,有没有触发初始化方法. 在这里插入图片描述
因为MonsterService里实现了接口,就自动重写了afterPropertiesSet()初始化方法.所以创建好了MonsterService实例后,就调用初始化方法成功.已然是通过容器底层的接口编程的方式,将我们指定的初始化方法调用起来了.

  1. processor包下定义接口BeanPostProcessor
package com.elf.spring.processor;

/**
 * @author 45
 * @version 1.0
 * 1. 参考原生Spring容器定义一个接口BeanPostProcessor
 * 2. 该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization
 * 3. 这两个方法,会对Spring容器的所有Bean生效, 已经是切面编程的概念.
 */
public interface BeanPostProcessor {

    /**
     * 说明
     * 1. postProcessBeforeInitialization在Bean的初始化方法前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 1. postProcessAfterInitialization在Bean的初始化方法后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

5.component包下写类

package com.elf.spring.component;

import com.elf.spring.annotation.Component;
import com.elf.spring.processor.BeanPostProcessor;

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

/**
 * @author 45
 * @version 1.0
 * 说明
 * 1. 这是我们自己的一个后置处理器
 * 2. 实现了BeanPostProcessor
 * 3. 我们可以重写before和after方法
 * 4. 在Spring容器中,仍然把ElfBeanPostProcessor当做一个Bean对象, 要在注入到容器
 * 5. @Component 标识
 * 6. 我们要让ElfBeanPostProcessor成为真正的后置处理器, 需要在容器中加入业务代码
 * 7. 还要考虑多个后置处理器对象注入到容器问题
 */
@Component
//这个后置处理器如果不在容器里边写代码支撑,它就只是一个普通类
public class ElfBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {

        //要体会到,后置处理器是会容器的创建的bean生效
        //,相当于是可以对多个对象编程, 切面编程
        //日志,权限,身份, 事务.......
        if (bean instanceof Car) {
            System.out.println("这是一个Car对象, 我可以处理");
            //((Car)bean)
        }
        System.out.println("后置处理器ElfBeanPostProcessor Before调用 bean类型="
                + bean.getClass() + " bean的名字=" + beanName);
        return bean;
   }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {

        System.out.println("后置处理器ElfBeanPostProcessor After调用 bean类型="
                + bean.getClass() + " bean的名字=" + beanName);
//
//        //实现AOP, 返回代理对象, 即对Bean进行包装
//        //1. 先死后活-> 后面我们可以通过注解就可以更加灵活
//        if ("smartDog".equals(beanName)) {
//            //使用Jdk的动态代理,返回返回bean的代理对象
//            //如果没有印象的小伙伴,回去看老韩讲过的动态代理
//            Object proxyInstance = Proxy.newProxyInstance(ElfBeanPostProcessor.class.getClassLoader(),
//                    bean.getClass().getInterfaces(), new InvocationHandler() {
//                        @Override
//                        public Object invoke(Object proxy, Method method, Object[] args)
//                                throws Throwable {
//                            System.out.println("method=" + method.getName());
//                            Object result = null;
//                            //假如我们进行前置通知+返回通知 处理的方法是getSum
//                            //后面可以通过注解来做的更加灵活
//                            if ("getSum".equals(method.getName())) {
//                                SmartAnimalAspect.showBeginLog();
//                                result = method.invoke(bean, args);//执行目标方法
//                                //进行返回通知的处理
//                                SmartAnimalAspect.showSuccessLog();
//                            } else {
//                                result = method.invoke(bean, args);//执行目标方法
//                            }
//                            return result;
//                        }
//                    });
//            //如果bean是需要返回代理对象的, 这里就直接return proxyInstance
//            return proxyInstance;
//        }
//        //如果不需要AOP, 返回 bean
       return bean;
    }
}

6.容器文件

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");

        }

    }

}

运行结果

在这里插入图片描述

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

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

相关文章

Vue3+Ts项目(Naive UI组件)——创建有图标可伸缩的左边菜单栏

文章目录 安装、配置vue-router1、安装2、main.ts配置3、在App.vue中&#xff0c;渲染路由配置到的组件 创建测试路径页面1、src\views\dashboard\index.vue2、src\views\dashboard\test.vue3、src\views\table\index.vue 配置页面路由1、src\router\modules\dashboard.ts2、sr…

2023年【北京市安全员-B证】考试及北京市安全员-B证复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 北京市安全员-B证考试参考答案及北京市安全员-B证考试试题解析是安全生产模拟考试一点通题库老师及北京市安全员-B证操作证已考过的学员汇总&#xff0c;相对有效帮助北京市安全员-B证复审考试学员顺利通过考试。 1、…

PyCharm连接远程服务器上Docker容器,使用远程服务器的python intercepter解释器和GPU资源 [本地调试深度学习代码]

概述 在编写常规深度学习代码时&#xff0c;总是需要使用服务器上的GPU资源&#xff0c;所以一般要写完代码&#xff0c;放到服务器&#xff0c;然后使用GPU运行。但是由于之前的习惯&#xff0c;总想本地调试一下或者本地直接跑测试结果&#xff0c;再放到服务器去跑。 网上…

格式工厂功能详解!!

格式工厂&#xff08;Format Factory&#xff09;是由上海格诗网络科技有限公司创立于2008年2月&#xff0c;是面向全球用户的互联网软件。 下载地址https://www.onlinedown.net/soft/64717.htm&#xff1a; 该软件的主打产品“格式工厂”发展以来&#xff0c;已经成为全球领…

华为云之云监控服务CES基础入门实践

华为云之云监控服务CES基础入门实践 一、云监控服务CES介绍二、本次实践介绍1.本次实践介绍2.本次实践目录 三、购买华为云ECS云服务器1.进入ECS云服务器控制台2.购买ECS云服务器3.检查ECS弹性云主机的状态 四、监控ECS弹性云主机性能1.远程连接ECS2.进入云监控服务CES页面3.使…

开源协作知识库软件AFFINE如何本地部署并结合内网穿透实现远程访问——“cpolar内网穿透”

前言 本篇文章讲解Notion开源平替全能知识库工具AFFINE如何本地部署&#xff0c;并实现公网远程访问。AFFiNE 是一个全新的开源项目&#xff0c;旨在克服 Notion 和 Miro 在安全和隐私方面的一些局限性。它的设计目标是帮助用户将会议记录、待办事项、文档中的目标、视频会议白…

Leetcode刷题笔记题解(C++):328. 奇偶链表

思路&#xff1a;遍历链表生成奇链表和偶链表&#xff0c;然后拼接两个链表生成新的链表。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), ne…

策略模式-大道至简

文章目录 摆个类图本质 摆个类图 本质 定义一个标准策略接口Strategy&#xff0c;这个接口中声明一个场景下应该使用的策略&#xff08;执行的逻辑&#xff09;。随后具体的执行器&#xff08;具体的场景&#xff09;应当实现这个接口&#xff0c;并实现自己的策略执行逻辑。为…

高通平台开发系列讲解(外设篇)高通平台EMMC适配说明

文章目录 一、EMMC的内部框图说明二、EMMC 设备树配置三、EMMC 内核配置四、EMMC 源码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要图解高通平台 EMMC适配说明。 eMMC(嵌入式多媒体卡)是一种集成了闪存存储器和控制器的存储芯片,通常用于嵌入式设备中,…

实时输电线路故障监测系统:精准定位、快速修复

随着电力系统的不断发展&#xff0c;输电线路的故障检测和维修变得越来越重要。为了提高故障检测的效率和准确性&#xff0c;恒峰智慧科技设计的实时输电线路故障监测系统应运而生。本文将介绍一种采用分布式行波测量技术的输电线路故障定位及隐患监测装置&#xff0c;以帮助您…

6.3 C++11 原子操作与原子类型

一、原子类型 1.多线程下的问题 在C中&#xff0c;一个全局数据在多个线程中被同时使用时&#xff0c;如果不加任何处理&#xff0c;则会出现数据同步的问题。 #include <iostream> #include <thread> #include <chrono> long val 0;void test() {for (i…

微信小程序单图上传和多图上传

图片上传主要用到 1、wx.chooseImage(Object object) 从本地相册选择图片或使用相机拍照。 参数 Object object 属性类型默认值必填说明countnumber9否最多可以选择的图片张数sizeTypeArray.<string>[original, compressed]否所选的图片的尺寸sourceTypeArray.<s…

docker-ubuntu中基于keepalived+niginx模拟主从热备完整过程

一、环境准备 &#x1f517;在Ubuntu中安装docker 二、主机 1、环境搭建 1.1 镜像拉取 docker pull ubuntu:16.041.2 创建网桥 docker network create -dbridge --subnet192.168.126.0/24 br11.3 启动容器 docker run -it --name ubuntu-1 --privileged -v /home/vac/l…

运行和部署若依分离版前端

一、运行 一、用vscode打开 二、安装依赖 # 建议不要直接使用 cnpm 安装依赖&#xff0c;会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 npm install --registryhttps://registry.npmmirror.com# 启动服务 npm run dev浏览器访问 http://localhost:80二、部…

美颜技术讲解:视频美颜SDK的开发与集成

如今&#xff0c;美颜技术的应用愈发成为吸引用户的一项重要功能。本文将深入探讨视频美颜SDK的开发与集成&#xff0c;揭示其背后的技术原理和实现步骤。 一、美颜技术的背后 美颜技术并非仅仅是简单的滤镜效果&#xff0c;而是一项涉及复杂图像处理和算法的技术。在视频美颜…

(第31天)RHEL 7 安装 Oracle 11GR2 RAC 数据库

前言 Oracle RAC是什么? Oracle Real Application Clusters (RAC) 允许客户跨多台服务器运行单个 Oracle 数据库,以最大限度地提高可用性并实现水平可扩展性,同时访问共享存储。连接到 Oracle RAC 实例的用户会话可以在中断期间进行故障转移并安全地重放更改,而无需对最终…

12.7 GNSS学习笔记记录

1.接收机坐标初值一般采用单点定位获得 2.定位精度主要取决于以下两个因素&#xff1a;一是所测卫星在空间的几何分布&#xff0c;通常称为卫星的几何分布图形&#xff1b;二是观测量的精度。其中&#xff0c;观测量的精度与卫星轨道和钟差、大气传播延迟、接收机噪声等因素有…

2024 年,新程序员如何与AI共赢!!

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

JAVA:注册表窗口的实现

目录 题目要求&#xff1a; 思路大意&#xff1a; 窗体的实现&#xff1a; 窗口A&#xff1a; 窗口B&#xff1a; 窗体之间的构思&#xff1a; 关键代码的实现&#xff1a; 窗口A&#xff1a; 封装列表&#xff1a; 窗口B&#xff1a; 题目要求&#xff1a; 使用…