朱晔和你聊Spring系列S1E3:Spring咖啡罐里的豆子

标题中的咖啡罐指的是Spring容器,容器里装的当然就是被称作Bean的豆子。本文我们会以一个最基本的例子来熟悉Spring的容器管理和扩展点。
阅读PDF版本

为什么要让容器来管理对象?

首先我们来聊聊这个问题,为什么我们要用Spring来管理对象(的生命周期和对象之间的关系)而不是自己new一个对象呢?大家可能会回答是方便,为了解耦。我个人觉得除了这两个原因之外,还有就是给予了我们更多可能性。如果我们以容器为依托来管理所有的框架、业务对象,那么不仅仅我们可以无侵入调整对象的关系,还有可能无侵入随时调整对象的属性甚至悄悄进行对象的替换。这就给了我们无限多的可能性,大大方便了框架的开发者在程序背后实现一些扩展。不仅仅Spring Core本身以及Spring Boot大量依赖Spring这套容器体系,一些外部框架也因为这个原因可以和Spring进行无缝整合。
Spring可以有三种方式来配置Bean,分别是最早期的XML方式、后来的注解方式以及现在最流行的Java代码配置方式。

Bean的回调事件

在前文parent模块(空的一个SpringBoot应用程序)的基础上,我们先来创建一个beans模块:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>me.josephzhu</groupId><artifactId>spring101-beans</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>spring101-beans</name><description></description><parent><groupId>me.josephzhu</groupId><artifactId>spring101</artifactId><version>0.0.1-SNAPSHOT</version></parent><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

然后来创建我们的豆子:

package me.josephzhu.spring101beans;import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@Component
public class MyService implements InitializingBean, DisposableBean {public int increaseCounter() {this.counter++;return counter;}public int getCounter() {return counter;}public void setCounter(int counter) {this.counter = counter;}private int counter=0;public MyService(){counter++;System.out.println(this + "#constructor:" + counter);}public String hello(){return this + "#hello:" + counter;}@PreDestroypublic void preDestroy() {System.out.println(this + "#preDestroy:" + counter);}@Overridepublic void afterPropertiesSet() {counter++;System.out.println(this + "#afterPropertiesSet:" + counter);}@PostConstructpublic void postConstruct(){counter++;System.out.println(this + "#postConstruct:" + counter);}@Overridepublic void destroy() {System.out.println(this + "#destroy:" + counter);}
}

这里可以看到,我们的服务中有一个counter字段,默认是0。这个类我们实现了InitializingBean接口和DisposableBean接口,同时还创建了两个方法分别加上了@PostConstruct和@PreDestroy注解。这两套实现方式都可以在对象的额外初始化功能和释放功能,注解的实现不依赖Spring的接口,侵入性弱一点。
接下去,我们创建一个Main类来测试一下:

package me.josephzhu.spring101beans;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;import javax.annotation.Resource;@SpringBootApplication
public class Spring101BeansApplication implements CommandLineRunner {@Autowiredprivate ApplicationContext applicationContext;@Resourceprivate MyService helloService;@Autowiredprivate MyService service;public static void main(String[] args) {SpringApplication.run(Spring101BeansApplication.class, args);}@Overridepublic void run(String... args) throws Exception {System.out.println("====================");applicationContext.getBeansOfType(MyService.class).forEach((name, service)->{System.out.println(name + ":" + service);});System.out.println("====================");System.out.println(helloService.hello());System.out.println(service.hello());}
}

ApplicationContext直接注入即可,不一定需要用ApplicationContextAware方式来获取。执行程序后可以看到输出如下:

me.josephzhu.spring101beans.MyService@7fb4f2a9#constructor:1
me.josephzhu.spring101beans.MyService@7fb4f2a9#postConstruct:2
me.josephzhu.spring101beans.MyService@7fb4f2a9#afterPropertiesSet:3
====================
myService:me.josephzhu.spring101beans.MyService@7fb4f2a9
====================
me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3
me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3
me.josephzhu.spring101beans.MyService@7fb4f2a9#preDestroy:3
me.josephzhu.spring101beans.MyService@7fb4f2a9#destroy:3

这里我们使用@Resource注解和@Autowired注解分别引用了两次对象,可以看到由于Bean默认配置为singleton单例,所以容器中MyService类型的对象只有一份,代码输出也可以证明这点。此外,我们也通过输出看到了构造方法以及两套Bean回调的次序是:

  1. 类自己的构造方法
  2. @PostConstruct注释的方法
  3. InitializingBean接口实现的方法
  4. @PreDestroy注释的方法
  5. DisposableBean接口实现的方法

Java 代码方式创建Bean

从刚才的输出中可以看到,在刚才的例子中,我们为Bean打上了@Component注解,容器为我们创建了名为myService的MyService类型的Bean。现在我们再来用Java代码方式来创建相同类型的Bean,创建如下的文件:

package me.josephzhu.spring101beans;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
public class ApplicationConfig {@Bean(initMethod = "init")public MyService helloService(){MyService myService = new MyService();myService.increaseCounter();return myService;}}

这里可以看到在定义Bean的时候我们关联了一个initMethod,因此我们需要修改Bean加上这个方法:

public void init() {counter++;System.out.println(this + "#init:" + counter);}

现在我们运行代码看看结果,得到了如下错误:

Field service in me.josephzhu.spring101beans.Spring101BeansApplication required a single bean, but 2 were found:- myService: defined in file [/Users/zyhome/IdeaProjects/spring101/spring101-beans/target/classes/me/josephzhu/spring101beans/MyService.class]- helloService: defined by method 'helloService' in class path resource [me/josephzhu/spring101beans/ApplicationConfig.class]

出现错误的原因是@Autowired了一个MyService,@Resource注解因为使用Bean的名称来查找Bean,所以并不会出错,而@Autowired因为根据Bean的类型来查抄Bean找到了两个匹配所有出错了,解决方式很简单,我们在多个Bean里选一个作为主Bean。我们修改一下MyService加上注解:

@Component
@Primary
public class MyService implements InitializingBean, DisposableBean

这样,我们的@Resource根据名字匹配到的是我们@Configuration出来的Bean,而@Autowired根据类型+Primary匹配到了@Component注解定义的Bean,重新运行代码来看看是不是这样:

me.josephzhu.spring101beans.MyService@6cd24612#constructor:1
me.josephzhu.spring101beans.MyService@6cd24612#postConstruct:3
me.josephzhu.spring101beans.MyService@6cd24612#afterPropertiesSet:4
me.josephzhu.spring101beans.MyService@6cd24612#init:5
me.josephzhu.spring101beans.MyService@7486b455#constructor:1
me.josephzhu.spring101beans.MyService@7486b455#postConstruct:2
me.josephzhu.spring101beans.MyService@7486b455#afterPropertiesSet:3
====================
myService:me.josephzhu.spring101beans.MyService@7486b455
helloService:me.josephzhu.spring101beans.MyService@6cd24612
====================
me.josephzhu.spring101beans.MyService@6cd24612#hello:5
me.josephzhu.spring101beans.MyService@7486b455#hello:3
me.josephzhu.spring101beans.MyService@7486b455#preDestroy:3
me.josephzhu.spring101beans.MyService@7486b455#destroy:3
me.josephzhu.spring101beans.MyService@6cd24612#preDestroy:5
me.josephzhu.spring101beans.MyService@6cd24612#destroy:5

从输出中我们注意到几点:

  1. 先输出的的确是helloService,说明@Resource引入的是我们Java代码配置的MyService,helloService由于在我们配置的多调用了一次increaseCounter()以及关联的initMethod,所以counter的值是5
  2. initMethod执行的顺序在@PostConstruct注释的方法和InitializingBean接口实现的方法之后
  3. 虽然我们的MySerive的两种Bean的定义都是单例,但是这不代表我们的Bean就是一套,在这里我们通过代码配置和注解方式在容器内创建了两套MyService类型的Bean,它们都经历了自己的初始化过程。通过@Resource和@Autowired引入到了是不同的Bean,当然也就是不同的对象
    你还可以试试在使用@Autowired引入MyService的时候直接指定需要的Bean:
@Autowired
@Qualifier("helloService")
private MyService service;

两个重要的扩展点

我们来继续探索Spring容器提供给我们的两个有关Bean的重要扩展点。

  • 用于修改Bean定义的BeanFactoryPostProcessor。所谓修改定义就是修改Bean的元数据,元数据有哪些呢?如下图所示,类型、名字、实例化方式、构造参数、属性、Autowire模式、懒初始化模式、初始析构方法。实现了这个接口后,我们就可以修改这些已经定义的元数据,实现真正的动态配置。这里需要注意,我们不应该在这个接口的实现中去实例化Bean,否则这相当于提前进行了实例化会破坏Bean的生命周期。

1662e9802f9ea16b?w=1982&h=962&f=png&s=161724

  • 用于修改Bean实例的BeanPostProcessor。在这个阶段其实Bean已经实例化了,我们可以进行一些额外的操作对Bean进行修改。如下图,我们可以清晰的看到Bean的生命周期如下(BeanPostProcessor缩写为BPP):
  1. Bean定义加载
  2. BeanFactoryPostProcessor来修改Bean定义
  3. Bean逐一实例化
  4. BeanPostProcessor预处理
  5. Bean初始化
  6. BeanPostProcessor后处理

1662e98559c7ec26?w=830&h=504&f=png&s=162435
好,我们现在来实现这两种类型的处理器,首先是用于修改Bean定义的处理器:

package me.josephzhu.spring101beans;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("helloService");if (beanDefinition != null) {beanDefinition.setScope("prototype");beanDefinition.getPropertyValues().add("counter", 10);}System.out.println("MyBeanFactoryPostProcessor");}
}

这里,我们首先找到了我们的helloService(Java代码配置的那个Bean),然后修改了它的属性和Scope(还记得吗,在之前的图中我们可以看到,这两项都是Bean的定义,定义相当于类描述,实例当然就是类实例了)。
然后,我们再来创建一个修改Bean实例的处理器:

package me.josephzhu.spring101beans;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof MyService) {System.out.println(bean + "#postProcessAfterInitialization:" + ((MyService)bean).increaseCounter());}return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof MyService) {System.out.println(bean + "#postProcessBeforeInitialization:" + ((MyService)bean).increaseCounter());}return bean;}
}

实现比较简单,在这个处理器的两个接口我们都调用了一次增加计数器的操作。我们运行代码来看一下这两个处理器执行的顺序是否符合刚才那个图的预期:

MyBeanFactoryPostProcessor
me.josephzhu.spring101beans.MyService@41330d4f#constructor:1
me.josephzhu.spring101beans.MyService@41330d4f#postProcessBeforeInitialization:11
me.josephzhu.spring101beans.MyService@41330d4f#postConstruct:12
me.josephzhu.spring101beans.MyService@41330d4f#afterPropertiesSet:13
me.josephzhu.spring101beans.MyService@41330d4f#init:14
me.josephzhu.spring101beans.MyService@41330d4f#postProcessAfterInitialization:15
me.josephzhu.spring101beans.MyService@6f36c2f0#constructor:1
me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessBeforeInitialization:11
me.josephzhu.spring101beans.MyService@6f36c2f0#postConstruct:12
me.josephzhu.spring101beans.MyService@6f36c2f0#afterPropertiesSet:13
me.josephzhu.spring101beans.MyService@6f36c2f0#init:14
me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessAfterInitialization:15
me.josephzhu.spring101beans.MyService@3b35a229#constructor:1
me.josephzhu.spring101beans.MyService@3b35a229#postProcessBeforeInitialization:2
me.josephzhu.spring101beans.MyService@3b35a229#postConstruct:3
me.josephzhu.spring101beans.MyService@3b35a229#afterPropertiesSet:4
me.josephzhu.spring101beans.MyService@3b35a229#postProcessAfterInitialization:5
====================
me.josephzhu.spring101beans.MyService@6692b6c6#constructor:1
me.josephzhu.spring101beans.MyService@6692b6c6#postProcessBeforeInitialization:11
me.josephzhu.spring101beans.MyService@6692b6c6#postConstruct:12
me.josephzhu.spring101beans.MyService@6692b6c6#afterPropertiesSet:13
me.josephzhu.spring101beans.MyService@6692b6c6#init:14
me.josephzhu.spring101beans.MyService@6692b6c6#postProcessAfterInitialization:15
myService:me.josephzhu.spring101beans.MyService@3b35a229
helloService:me.josephzhu.spring101beans.MyService@6692b6c6
====================
me.josephzhu.spring101beans.MyService@41330d4f#hello:15
me.josephzhu.spring101beans.MyService@6f36c2f0#hello:15
me.josephzhu.spring101beans.MyService@3b35a229#preDestroy:5
me.josephzhu.spring101beans.MyService@3b35a229#destroy:5

这个输出结果有点长,第一行就输出了MyBeanFactoryPostProcessor这是预料之中,Bean定义的修改肯定是最先发生的。我们看下输出的规律,1、11、12、13、14、15出现了三次,之所以从1跳到了11是因为我们的BeanFactoryPostProcessor修改了其中的counter属性的值为10。这说明了,我们的helloService的初始化进行了三次:

  • 第一套指针地址是5a7fe64f,对应输出第一个hello(),这是我们@Resource引入的

1662e9889dc0535f?w=830&h=616&f=png&s=321049

  • 第二套指针地址是69ee81fc,对应输出第二个hello(),这是我们@Autowird+@Qualifier引入的(刚才一节最后我们指定了helloService)

1662e98998f48d60?w=830&h=616&f=png&s=338496

  • 第三套指针地址是29f7cefd,这是我们getBeansOfType的时候创建的,对应下面Key-Value的输出:

1662e98aafeb37de?w=830&h=504&f=png&s=257113
这里的输出说明了几点:

  • 我们的BeanFactoryPostProcessor生效了,不但修改了helloService的Scope为prototype而且修改了它的counter属性
  • 对于Scope=ptototype的Bean,显然在每次使用Bean的时候都会新建一个实例
  • BeanPostProcessor两个方法的顺序结合一开始说的Bean事件回调的顺序整体如下:
  1. 类自己的构造方法
  2. BeanFactoryPostProcessor接口实现的postProcessBeforeInitialization()方法
  3. @PostConstruct注释的方法
  4. InitializingBean接口实现的afterPropertiesSet()方法
  5. Init-method定义的方法
  6. BeanFactoryPostProcessor接口实现的postProcessAfterInitialization()方法
  7. @PreDestroy注释的方法
  8. DisposableBean接口实现的destroy()方法

最后,我们可以修改BeanFactoryPostProcessor中的代码把prototype修改为singleton看看是否我们的helloService这个Bean恢复为了单例:

MyBeanFactoryPostProcessor
me.josephzhu.spring101beans.MyService@51891008#constructor:1
me.josephzhu.spring101beans.MyService@51891008#postProcessBeforeInitialization:11
me.josephzhu.spring101beans.MyService@51891008#postConstruct:12
me.josephzhu.spring101beans.MyService@51891008#afterPropertiesSet:13
me.josephzhu.spring101beans.MyService@51891008#init:14
me.josephzhu.spring101beans.MyService@51891008#postProcessAfterInitialization:15
me.josephzhu.spring101beans.MyService@49c90a9c#constructor:1
me.josephzhu.spring101beans.MyService@49c90a9c#postProcessBeforeInitialization:2
me.josephzhu.spring101beans.MyService@49c90a9c#postConstruct:3
me.josephzhu.spring101beans.MyService@49c90a9c#afterPropertiesSet:4
me.josephzhu.spring101beans.MyService@49c90a9c#postProcessAfterInitialization:5
====================
myService:me.josephzhu.spring101beans.MyService@49c90a9c
helloService:me.josephzhu.spring101beans.MyService@51891008
====================
me.josephzhu.spring101beans.MyService@51891008#hello:15
me.josephzhu.spring101beans.MyService@51891008#hello:15
me.josephzhu.spring101beans.MyService@49c90a9c#preDestroy:5
me.josephzhu.spring101beans.MyService@49c90a9c#destroy:5
me.josephzhu.spring101beans.MyService@51891008#preDestroy:15
me.josephzhu.spring101beans.MyService@51891008#destroy:15

本次输出结果的hello()方法明显是同一个bean,结果中也没出现三次1、11、12、13、14、15。

总结

本文以探索的形式讨论了下面的一些知识点:

  1. 容器管理对象的意义是什么
  2. Bean的生命周期回调事件
  3. Spring提供的Bean的两个重要扩展点
  4. @Resource和@Autowired的区别
  5. 注解方式和代码方式配置Bean
  6. @Primary和@Qualifier注解的作用
  7. Bean的不同类型的Scope

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

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

相关文章

ab实验置信度_为什么您的Ab测试需要置信区间

ab实验置信度by Alos Bissuel, Vincent Grosbois and Benjamin HeymannAlosBissuel&#xff0c;Vincent Grosbois和Benjamin Heymann撰写 The recent media debate on COVID-19 drugs is a unique occasion to discuss why decision making in an uncertain environment is a …

基于Pytorch的NLP入门任务思想及代码实现:判断文本中是否出现指定字

今天学了第一个基于Pytorch框架的NLP任务&#xff1a; 判断文本中是否出现指定字 思路&#xff1a;&#xff08;注意&#xff1a;这是基于字的算法&#xff09; 任务&#xff1a;判断文本中是否出现“xyz”&#xff0c;出现其中之一即可 训练部分&#xff1a; 一&#xff…

支撑阻力指标_使用k表示聚类以创建支撑和阻力

支撑阻力指标Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without seek…

高版本(3.9版本)python在anaconda安装opencv库及skimage库(scikit_image库)诸多问题解决办法

今天开始CV方向的学习&#xff0c;然而刚拿到基础代码的时候发现 from skimage.color import rgb2gray 和 import cv2标红&#xff08;这里是因为我已经配置成功了&#xff0c;所以没有红标&#xff09;&#xff0c;我以为是单纯两个库没有下载&#xff0c;去pycharm中下载ski…

单机安装ZooKeeper

2019独角兽企业重金招聘Python工程师标准>>> zookeeper下载、安装以及配置环境变量 本节介绍单机的zookeeper安装&#xff0c;官方下载地址如下&#xff1a; https://archive.apache.org/dist/zookeeper/ 我这里使用的是3.4.11版本&#xff0c;所以找到相应的版本点…

均线交易策略的回测 r_使用r创建交易策略并进行回测

均线交易策略的回测 rR Programming language is an open-source software developed by statisticians and it is widely used among Data Miners for developing Data Analysis. R can be best programmed and developed in RStudio which is an IDE (Integrated Development…

opencv入门课程:彩色图像灰度化和二值化(采用skimage库和opencv库两种方法)

用最简单的办法实现彩色图像灰度化和二值化&#xff1a; 首先采用skimage库&#xff08;skimage库现在在scikit_image库中&#xff09;实现&#xff1a; from skimage.color import rgb2gray import numpy as np import matplotlib.pyplot as plt""" skimage库…

instagram分析以预测与安的限量版运动鞋转售价格

Being a sneakerhead is a culture on its own and has its own industry. Every month Biggest brands introduce few select Limited Edition Sneakers which are sold in the markets according to Lottery System called ‘Raffle’. Which have created a new market of i…

opencv:用最邻近插值和双线性插值法实现上采样(放大图像)与下采样(缩小图像)

上采样与下采样 概念&#xff1a; 上采样&#xff1a; 放大图像&#xff08;或称为上采样&#xff08;upsampling&#xff09;或图像插值&#xff08;interpolating&#xff09;&#xff09;的主要目的 是放大原图像,从而可以显示在更高分辨率的显示设备上。 下采样&#xff…

CSS魔法堂:那个被我们忽略的outline

前言 在CSS魔法堂&#xff1a;改变单选框颜色就这么吹毛求疵&#xff01;中我们要模拟原生单选框通过Tab键获得焦点的效果&#xff0c;这里涉及到一个常常被忽略的属性——outline&#xff0c;由于之前对其印象确实有些模糊&#xff0c;于是本文打算对其进行稍微深入的研究^_^ …

初创公司怎么做销售数据分析_初创公司与Faang公司的数据科学

初创公司怎么做销售数据分析介绍 (Introduction) In an increasingly technological world, data scientist and analyst roles have emerged, with responsibilities ranging from optimizing Yelp ratings to filtering Amazon recommendations and designing Facebook featu…

opencv:灰色和彩色图像的像素直方图及直方图均值化的实现与展示

直方图及直方图均值化的理论&#xff0c;实现及展示 直方图&#xff1a; 首先&#xff0c;我们来看看什么是直方图&#xff1a; 理论概念&#xff1a; 在图像处理中&#xff0c;经常用到直方图&#xff0c;如颜色直方图、灰度直方图等。 图像的灰度直方图就描述了图像中灰度分…

交换机的基本原理配置(一)

1、配置主机名 在全局模式下输入hostname 名字 然后回车即可立马生效&#xff08;在生产环境交换机必须有自己唯一的名字&#xff09; Switch(config)#hostname jsh-sw1jsh-sw1(config)#2、显示系统OS名称及版本信息 特权模式下&#xff0c;输入命令 show version Switch#show …

opencv:卷积涉及的基础概念,Sobel边缘检测代码实现及Same(相同)填充与Vaild(有效)填充

滤波 线性滤波可以说是图像处理最基本的方法&#xff0c;它可以允许我们对图像进行处理&#xff0c;产生很多不同的效果。 卷积 卷积的概念&#xff1a; 卷积的原理与滤波类似。但是卷积却有着细小的差别。 卷积操作也是卷积核与图像对应位置的乘积和。但是卷积操作在做乘…

r psm倾向性匹配_南瓜香料指标psm如何规划季节性广告

r psm倾向性匹配Retail managers have been facing an extraordinary time with the COVID-19 pandemic. But the typical plans to prepare for seasonal sales will be a new challenge. More seasonal products have been introduced over the years, making August the bes…

主成分分析:PCA的思想及鸢尾花实例实现

主成份分析算法PCA 非监督学习算法 PCA的实现&#xff1a; 简单来说&#xff0c;就是将数据从原始的空间中转换到新的特征空间中&#xff0c;例如原始的空间是三维的(x,y,z)&#xff0c;x、y、z分别是原始空间的三个基&#xff0c;我们可以通过某种方法&#xff0c;用新的坐…

两家大型网贷平台竟在借款人审核问题上“偷懒”?

python信用评分卡&#xff08;附代码&#xff0c;博主录制&#xff09; https://study.163.com/course/introduction.htm?courseId1005214003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 放贷流量增加&#xff0c;逾期率也会随之增加&…

opencv:边缘检测之Laplacian算子思想及实现

Laplacian算子边缘检测的来源 在边缘部分求取一阶导数&#xff0c;你会看到极值的出现&#xff1a; 如果在边缘部分求二阶导数会出现什么情况? 从上例中我们可以推论检测边缘可以通过定位梯度值大于邻域的相素的方法找到(或者推广到大 于一个阀值). 从以上分析中&#xff0c…

使用机器学习预测天气_如何使用机器学习预测着陆

使用机器学习预测天气Based on every NFL play from 2009–2017根据2009-2017年每场NFL比赛 Ah, yes. The times, they are changin’. The leaves are beginning to fall, the weather is slowly starting to cool down (unless you’re where I’m at in LA, where it’s on…

laravel 导出插件

转发&#xff1a;https://blog.csdn.net/gu_wen_jie/article/details/79296470 版本&#xff1a;laravel5 php 5.6 安装步骤&#xff1a; 一、安装插件 ①、首先在Laravel项目根目录下使用Composer安装依赖&#xff1a; composer require "maatwebsite/excel:~2.1.0"…