Spring Bean生命周期以及PostProcessor后置处理器

news2024/10/5 14:12:20

简介

所谓Bean的生命周期,就是一个 Bean 从创建到销毁,所经历的各种方法调用。

一个Bean的生命周期分为四个阶段:

  1. 实例化(Instantiation):Spring容器负责创建Bean的实例,可以通过构造方法或者无参构造方法进行实例化
  2. 属性赋值(populate):Spring容器通过属性注入的方式为Bean的属性赋值,可以通过setter方法或者无参构造方法进行属性赋值。
  3. 初始化(Initialization):在初始化阶段,Spring容器会调用Bean的初始化方法(例如InitializingBean接口afterPropertiesSet() 方法),以确保Bean的正确初始化。
  4. 销毁(Destruction):在销毁阶段,Spring容器会调用Bean的销毁方法(例如DisposableBean接口destroy() 方法),以便在容器关闭时清理资源。

在整个生命周期中,多个增强接口(后置处理器)贯穿了这四个阶段,这些增强接口为开发者提供了更灵活和强大的功能。

整个过程如下:

img

设计模式——模板方法

上述使用增强接口的处理方式用到了模板方法这种设计模式。

模板方法:方法中固定不变的步骤不动,会变化的部分(刚开始设计时不可以确定的部分)设计成抽象接口,在特定的时机调用抽象方法。

// 利用模板方法这种设计模式,可以实现在属性注入阶段进行动态扩展
public class TestMethodTemplate {

    public static void main(String[] args) {
        MyBeanFactory beanFactory = new MyBeanFactory();
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Autowired"));
        beanFactory.addBeanPostProcessor(bean -> System.out.println("解析 @Resource"));
        beanFactory.getBean();
    }

    // 模板方法  Template Method Pattern
    static class MyBeanFactory {
        private final List<BeanPostProcessor> processors = new ArrayList<>();

        /**
         * 构造、依赖注入、初始化以及销毁这些方法都是固定的保持不动
         * 在依赖注入阶段扩展属于会变化的部分,抽象成接口动态添加增强方法
         */
        public Object getBean() {
            Object bean = new Object();
            System.out.println("构造 " + bean);
            System.out.println("依赖注入 " + bean); // @Autowired, @Resource
            // 会变化的部分
            for (BeanPostProcessor processor : processors) {
                processor.inject(bean);
            }
            System.out.println("初始化 " + bean);
            System.out.println("销毁" + bean);
            return bean;
        }

        // 添加增强接口实现
        public void addBeanPostProcessor(BeanPostProcessor processor) {
            processors.add(processor);
        }
    }

    /**
     * 抽象接口
     */
    interface BeanPostProcessor {
        // 对依赖注入阶段的扩展
        void inject(Object bean);
    }
}

自定义BeanPostProcessor

自定义BeanPostProcessor,可以在每个Bean生命周期任意一个阶段执行自定义逻辑

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor.class);

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        // 返回null不会替换
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            log.debug("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            log.debug("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

}

BeanPostProcessor执行顺序

BeanPostProcessor执行顺序可以排序:(无排序按添加顺序执行)

/*
    bean 后处理的的排序
 */
public class TestProcessOrder {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 添加一些后置处理器&排序器
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

        List<BeanPostProcessor> list = new ArrayList<>();
        list.add(new P1());
        list.add(new P2());
        list.add(new P3());
        list.add(new P4());
        list.add(new P5());
        // bean后置处理器排序 按 order 排序 (order从小到大排序)
        list.sort(beanFactory.getDependencyComparator());

        list.forEach(processor -> {
            processor.postProcessBeforeInitialization(new Object(), "");
        });

        /*
            排序之后:
                1. 实现了 PriorityOrdered 接口的优先级最高
                2. 实现了 Ordered 接口与加了 @Order 注解的平级, 按数字升序
                3. 其它的排在最后
         */
    }

    @Order(1)
    static class P1 implements BeanPostProcessor {
        private static final Logger log = LoggerFactory.getLogger(P1.class);

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            log.debug("postProcessBeforeInitialization @Order(1)");
            return bean;
        }
    }

    @Order(2)
    static class P2 implements BeanPostProcessor {
        private static final Logger log = LoggerFactory.getLogger(P2.class);

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            log.debug("postProcessBeforeInitialization @Order(2)");
            return bean;
        }

    }

    // 优先级最高(排序的话)
    static class P3 implements BeanPostProcessor, PriorityOrdered {
        private static final Logger log = LoggerFactory.getLogger(P3.class);

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            log.debug("postProcessBeforeInitialization PriorityOrdered");
            return bean;
        }

        @Override
        public int getOrder() {
            return 100;
        }
    }

    static class P4 implements BeanPostProcessor, Ordered {
        private static final Logger log = LoggerFactory.getLogger(P4.class);

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            log.debug("postProcessBeforeInitialization Ordered");
            return bean;
        }

        @Override
        public int getOrder() {
            return 0;
        }
    }

    static class P5 implements BeanPostProcessor {
        private static final Logger log = LoggerFactory.getLogger(P5.class);

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            log.debug("postProcessBeforeInitialization");
            return bean;
        }
    }
}

PostProcessor

BeanFactoryPostProcessor 和 BeanPostProcessor 作用及执行时机

  • BeanFactoryPostProcessor解析Bean定义
  • BeanPostProcessor 在初始化Bean阶段进行增强扩展

image-20230710074857991

BeanPostProcessor

用于对象属性赋值

如果不往该容器里添加一些后置处理器的话,将类交给Spring容器管理,获得对象时,对象中的属性一般不会被赋值

添加一些后置处理器:

AutowiredAnnotationBeanPostProcessor:用于解析@Autowired、@Value

CommonAnnotationBeanPostProcessor:用于解析@Resource、@PostConstruct、@PreDestroy

ConfigurationPropertiesBindingPostProcessor:用于解析@ConfigurationProperties,读取配置信息

public class A04 {
    public static void main(String[] args) {
        // ⬇️GenericApplicationContext 是一个【干净】的容器 【没有添加任何后置处理器】
        GenericApplicationContext context = new GenericApplicationContext();

        // ⬇️用原始方法注册三个 bean
        context.registerBean("bean1", Bean1.class);
        context.registerBean("bean2", Bean2.class);
        context.registerBean("bean3", Bean3.class);
        context.registerBean("bean4", Bean4.class);

        // 设置解析器,否则@Value注解会报错
        context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // 解析@Autowired @Value

        context.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy

        // 解析@ConfigurationProperties(prefix = "java")
        ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());

        // ⬇️初始化容器
        context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例 (不执行该方法会报错)

        System.out.println(context.getBean(Bean1.class));
        System.out.println(context.getBean(Bean4.class));

        // ⬇️销毁容器
        context.close();

        /*
            学到了什么
                a. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能
                b. 这些扩展功能由 bean 后处理器来完成
         */
    }
}

AutowiredAnnotationBeanPostProcessor 运行分析 (DI依赖注入)

// AutowiredAnnotationBeanPostProcessor 运行分析
public class DigInAutowired {
    public static void main(String[] args) throws Throwable {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.registerSingleton("bean2", new Bean2()); // 创建过程,依赖注入,初始化
        beanFactory.registerSingleton("bean3", new Bean3());
        beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); // @Value
        beanFactory.addEmbeddedValueResolver(new StandardEnvironment()::resolvePlaceholders); // ${} 的解析器

        // 1. 查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata
//        AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
//        processor.setBeanFactory(beanFactory);

//        Bean1 bean1 = new Bean1();
//        System.out.println(bean1);
//        processor.postProcessProperties(null, bean1, "bean1"); // 执行依赖注入 @Autowired @Value
//        System.out.println(bean1);

        // 反射调用 findAutowiringMetadata 方法找到类中带有@Autowired、@Value注解的属性信息
//        Method findAutowiringMetadata = AutowiredAnnotationBeanPostProcessor.class.getDeclaredMethod("findAutowiringMetadata", String.class, Class.class, PropertyValues.class);
//        findAutowiringMetadata.setAccessible(true);
//        InjectionMetadata metadata = (InjectionMetadata) findAutowiringMetadata.invoke(processor, "bean1", Bean1.class, null);// 获取 Bean1 上加了 @Value @Autowired 的成员变量,方法参数信息
//        System.out.println(metadata);

        // 2. 调用 InjectionMetadata 来进行依赖注入, 注入时按类型查找值
//        metadata.inject(bean1, "bean1", null);
//        System.out.println(bean1);

        // 3. 如何按类型查找值 反射通过属性名或者方法名找到属性值
        Field bean3 = Bean1.class.getDeclaredField("bean3");
        // true找不到属性对象直接报错,false找不到属性对象返回null,然后使用反射将属性对象设置给bean对象
        DependencyDescriptor dd1 = new DependencyDescriptor(bean3, false);
        Object o = beanFactory.doResolveDependency(dd1, null, null, null);
        System.out.println(o);

        Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
        DependencyDescriptor dd2 =
                new DependencyDescriptor(new MethodParameter(setBean2, 0), true);
        Object o1 = beanFactory.doResolveDependency(dd2, null, null, null);
        System.out.println(o1);

        Method setHome = Bean1.class.getDeclaredMethod("setHome", String.class);
        DependencyDescriptor dd3 = new DependencyDescriptor(new MethodParameter(setHome, 0), true);
        Object o2 = beanFactory.doResolveDependency(dd3, null, null, null);
        System.out.println(o2);

    }
}

BeanFactoryPostProcessor

用于解析Bean定义

image-20230710225709410

ConfigurationClassPostProcessor:用于解析@ComponentScan、@Bean、@Import、 @ImportResource,将类解析成一个一个bean注册到Spring容器中

MapperScannerConfigurer:Mybatis自定义后置处理器,相当于@MapperScan,将一个个Mybatis Mapper接口解析成MapperFactoryBean对象注册到BeaFactory容器中

/*
    BeanFactory 后处理器的作用
 */
public class A05 {

    public static void main(String[] args) throws IOException {

        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        // Spring自带后置处理器
        context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
        // 三方自定义后置处理器
        context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScan
            bd.getPropertyValues().add("basePackage", "com.lkl.spring.chapter5.mapper");
        });

        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        Mapper1 mapper1 = context.getBean(Mapper1.class);
        Mapper2 mapper2 = context.getBean(Mapper2.class);

        System.out.println("-->" + mapper1);
        System.out.println("-->" + mapper2);

        // ⬇️销毁容器
        context.close();

        /*
            学到了什么
                a. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
                b. 这些扩展功能由不同的 BeanFactory 后处理器来完成, 其实主要就是补充了一些 bean 定义
         */
    }
}

Config配置类:

@Configuration
@ComponentScan("com.lkl.spring.chapter5.component")
public class Config {
    @Bean
    public Bean1 bean1() {
        return new Bean1();
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean(initMethod = "init")
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

/*    @Bean
    public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper1> factory = new MapperFactoryBean<>(Mapper1.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }

    @Bean
    public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean<Mapper2> factory = new MapperFactoryBean<>(Mapper2.class);
        factory.setSqlSessionFactory(sqlSessionFactory);
        return factory;
    }*/
}

自定义简易PostProcessor,解析@ComponentScan注解

public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override // context.refresh
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                for (String p : componentScan.basePackages()) {
                    System.out.println(p);
                    // com.lkl.spring.chapter5.component -> classpath*:com/lkl/spring/chapter5/component/**/*.class 文件夹及子文件夹下所有类
                    String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                    System.out.println(path);
                    CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
                    // 根据路径寻找资源
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
                    for (Resource resource : resources) {
                        // System.out.println(resource);
                        // 解析类的元数据
                        MetadataReader reader = factory.getMetadataReader(resource);
                        // System.out.println("类名:" + reader.getClassMetadata().getClassName());
                        AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
                        // System.out.println("是否加了 @Component:" + annotationMetadata.hasAnnotation(Component.class.getName()));
                        // System.out.println("是否加了 @Component 派生:" + annotationMetadata.hasMetaAnnotation(Component.class.getName()));
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                            || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            // 创建BeanDefinition注册到Spring中
                            AbstractBeanDefinition bd = BeanDefinitionBuilder
                                    .genericBeanDefinition(reader.getClassMetadata().getClassName())
                                    .getBeanDefinition();
                            // 生成beanName
                            String name = generator.generateBeanName(bd, beanFactory);
                            beanFactory.registerBeanDefinition(name, bd);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

自定义简易PostProcessor,解析@Bean注解

public class AtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/lkl/spring/chapter5/Config.class"));
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                System.out.println(method);
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                // 设置自动装配的类型 构造器
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition bd = builder.getBeanDefinition();
                beanFactory.registerBeanDefinition(method.getMethodName(), bd);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

自定义简易PostProcessor,解析@Mapper注解

public class MapperPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanFactory) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:com/lkl/spring/chapter5/mapper/**/*.class");
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            for (Resource resource : resources) {
                MetadataReader reader = factory.getMetadataReader(resource);
                ClassMetadata classMetadata = reader.getClassMetadata();
                if (classMetadata.isInterface()) {
                    // 创建 MapperFactoryBean BeanDefinition
                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    // 根据 Mapper className 生成 BeanDefinition 从而生成 beanName
                    AbstractBeanDefinition bd2 = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    String name = generator.generateBeanName(bd2, beanFactory);
                    beanFactory.registerBeanDefinition(name, bd);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

注册自定义后置处理器,与上方效果一致

public class A05 {

    public static void main(String[] args) throws IOException {

        // ⬇️GenericApplicationContext 是一个【干净】的容器
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan

        context.registerBean(AtBeanPostProcessor.class); // 解析 @Bean
        context.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口

        // ⬇️初始化容器
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        Mapper1 mapper1 = context.getBean(Mapper1.class);
        Mapper2 mapper2 = context.getBean(Mapper2.class);

        System.out.println("-->" + mapper1);
        System.out.println("-->" + mapper2);

        // ⬇️销毁容器
        context.close();

        /*
            学到了什么
                自定义后置处理器解析注解,封装为BeanDefinition 注册到Spring容器中
         */
    }
}

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

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

相关文章

电脑应用程序发生异常怎么办?

有时候我们打开电脑上面的某个软件时&#xff0c;会打不开&#xff0c;并且会弹出如下的错误提示“应用程序发生异常 未知的软件异常&#xff08;&#xff58;&#xff58;&#xff58;&#xff09;&#xff0c;位置为&#xff58;&#xff58;”。相信大多数的人在使用电脑的时…

springMVC(三)—— 整合SSM框架

环境 IDEA Mysql 5.7.19 tomcat 8 maven 3.8.4 数据库设计 CREATE DATABASE ssmbuild;USE ssmbuild;DROP TABLE IF EXISTS books;CREATE TABLE books(bookID INT(10) NOT NULL AUTO_INCREMENT COMMENT 书id,bookName VARCHAR(100) NOT NULL COMMENT 书名,bookCounts INT(…

github 最简单的使用步骤(个人学习记录~)

github 使用步骤&#xff1a; (11条消息) github新手用法详解&#xff08;建议收藏&#xff01;&#xff01;&#xff01;&#xff09;_github详解_怪 咖的博客-CSDN博客 1.获取ssh密钥 打开输入&#xff1a;ssh-keygen -t rsa -C “git账号” 输入之后一路Enter&#xff08…

Tomcat之高可用配置

Nginx搭配Tomcat实现负载均衡 传统模型下&#xff0c;一个项目部署在一台tomcat上&#xff0c;这个时候&#xff0c;假如tomcat因为服务器资源不够&#xff0c;突然挂机了&#xff0c;那么整个项目就无法使用。 Nginx就可以避免单台服务如果挂机&#xff0c;依然能保证服务正…

Python实现操作MySQL【增删改查】

闲话少叙,直接上操作! 一、准备工作 1.本地安装MySQL、Python(以3.6为例) 2.MySQL新建数据库【test】,新建表【user】,新建字段【name】【age】 3.建表方式:navicat工具 字段 二、Python操作—插入数据 #!/usr/bin/env # coding=utf-8import pymysql # Python 连…

Matlab+Yalmip求解优化问题(1)-入门学习

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;YALMIP 1.Yalmip工具箱的下载与安装 1.1下载 Yalmip的作者是Johan Lfberg&#xff0c;是由Matlab平台编程实现的一个免费开源数学优化工具箱&#xff0c;在官网上就可以下载。官方下载…

Spark学习--4、键值对RDD数据分区、累加器、广播变量、SparkCore实战(Top10热门品类)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、键值对RDD数据分区1.1 Hash分区1.2 Ranger分区 二、累加器三、广播变量四、SparkCore实战4.1 数据准备4.2 需求&#xff1a;Top10热门品类4.2.1 需求分析&#…

问题解决:错误: 找不到或无法加载主类 App

问题描述尝试解决 问题描述 昨天刚刚把公司的项目源代码拉下来,结果全是报错,几百条.一看就是环境没配好. 今天刚刚解决配置问题,项目也没有报错了 今天在做项目的时候,我先跑一下看看项目能不能跑起来.结果一跑又报错了 错误:找不到或无法加载主类 xxx.BaasAppApplication原因…

profiles.active多环境开发、测试、部署

1、使用场景 在开始讲profiles.active配置时&#xff0c;我们先来考虑几个场景。 我们在开发过程中&#xff0c;经常会碰到多个环境&#xff0c;特别熟数据库&#xff0c;经常是有开发库&#xff0c;测试库&#xff0c;和生产库。一般我们都是连的开发库进行开发&#xff0c;…

四两拨千斤,训练大模型的PEFT方法

自然语言处理进入大语言模型&#xff08;Large Language Model, LLM&#xff09;时代之后&#xff0c;模型的参数量级越来越庞大&#xff0c;以稍早之前的GPT-3为例&#xff0c;它有175B即1亿7千5百万参数&#xff0c;而ChatGPT及后续模型则更大。一方面大语言模型解决自然语言…

用html+javascript打造公文一键排版系统4:一级标题排版

完成公文标题的排版后&#xff0c;我们要进行对正文中的标题进行处理。 一般正文中的标题分为四级&#xff0c;文中结构层次序数依次可以用“一、”“&#xff08;一&#xff09;”“1.”“&#xff08;1&#xff09;”标注&#xff1b;一般第一层用黑体字、第二层用楷体字加粗…

10.25UEC++/小试牛刀(笨鸟先飞案例)

1.思路整理&#xff1a; 如何入手&#xff1f; 角色可能是每个游戏的最重要的部分&#xff0c;所以一般可以先从角色入手&#xff0c;如果游戏很复杂&#xff0c;可以进行拆分设计。 蓝图创建地图&#xff1a; 创建默认Pawn&#xff1a; 编写GameMode默认构造函数&#xff1a;…

第二十二章:逻辑架构

第二十二章&#xff1a;逻辑架构 22.1&#xff1a;逻辑架构剖析 服务器处理客户端请求 ​ MySQL是典型的C/S架构&#xff0c;即Client/Server架构&#xff0c;服务端程序使用的mysqld。不论客户端进程和服务器进程是采用哪种方式进行通信&#xff0c;最后实现的效果都是&#…

VoLTE信令流程

文章目录 VoLTE概述VoLTE 协议栈IMS(IP Multimedia Subsystem)&#xff0c;IP多媒体子系统会谈边界控制器&#xff08;Session Border Controller, SBC&#xff09;QCI (QoS Class Identifier)&#xff0c;QoS类标识符 SIP&#xff08;Session initialization Protocol&#xf…

Android mac 交叉编译与ffmpeg编译踩坑记 (v7a 与 v8a and 动态库与静态库)

Android mac 交叉编译与ffmpeg编译踩坑记 环境: system: mac NDK: android-ndk-r17c Fffmpeg: ffmpeg-4.0.2 Cmake: 3.10.2 Gradle: 4.1.3 tips: 本文记录踩坑过程,具体细节如果感兴趣可以在评论区留言交流讨论! mac 编译 (动态库(so)) 首先来回顾一下,mac原始库是如何…

Redis常见命令和使用示例

目录 1.使用官方文档学习redis 2.核心命令 SET GET 3.全局/通用命令 KEYS EXISTS DEL EXPIRE TTL TYPE 1.使用官方文档学习redis redis官网 点击搜索&#xff0c;输入&#xff0c;比如输入ping&#xff0c;会显示Commands,是一个命令&#xff0c;点击ping&#xff…

新版2021等启动unity包管理器本地服务器进程失败解决

1、问题 2、解决办法 最后我发现每个unity版本都有一个包管理器&#xff0c;也就是图中的路径&#xff0c;而且都是相同的路径&#xff0c;只是因为版本不同或多或少存在一些小差异&#xff0c;其他版本可以正常启动&#xff0c;就2021不行会不会是2021的包管理器这个文件夹出…

YOLOv5 实现 10 种热力图可视化方式教程

效果展示 GradCAMEigenCAMEigenGradCAMGradCAMElementWiseGradCAMPlusPlusHiResCAMLayerCAM

前端下载PDF内容空白且大小偏大问题解决

前端使用React框架axios请求后端接口下载文件&#xff0c;请求返回正常但文件大小和内容异常。经排查&#xff0c;其原因是使用了mockjs用作数据模拟&#xff0c;导致了请求异常&#xff0c;将其注释掉后&#xff0c;文件大小与内容恢复正常。 目录 1.文件异常 2.定位问题 3…

【C语言进阶技巧】指针解密:炼金术士的秘密面试题揭秘

【C语言进阶技巧】指针解密&#xff1a;炼金术士的秘密面试题揭秘 1. 主要涉及sizeof与strlen函数的使用的笔试题1.1 笔试题一&#xff08;一维整形数组&#xff09;1.2 笔试题二&#xff08;不带\0的字符数组&#xff09;1.3 笔试题三(带\0的字符数组)1.4 笔试题四 &#xff0…