Spring-IoC

文章目录

  • Spring IoC
  • 一、IoC概述
    • 1、概述
    • 2、IoC原理
    • 3、IoC作用
    • 4、Spring Bean
  • 二、Spring容器 相关接口
    • 1、BeanFactory
    • 2、ApplicationContext
      • 1)HierarchicalBeanFactory
      • 2)ListableBeanFactory
      • 3)EnvironmentCapable
      • 4)ApplicationEventPublisher
      • 5)MessageSource
      • 6)ResourcePatternResolver
    • 3、ApplicationContext 的实现
    • 4、BeanFactory & ApplicationContext
  • 三、Bean 的声明方式
    • 1、XML的bean标签
      • 1)构造函数方式
      • 2)静态工厂方式
      • 3)实例工厂方式
    • 2、@Component
      • 1)配合 @ComponentScan
      • 2)@Component 的扩展
    • 3、@Bean
      • 1)搭配 @Configuration
      • 2)搭配 @Component
      • 3)搭配 ApplicationContext
    • 4、FactoryBean
    • 5、SmartFactoryBean
    • 6、registerBean 方法
  • 四、Bean 的作用域
    • 1、bean的作用域
    • 2、@Scope 注解
    • 3、扩展 @Scope 注解
    • 4、单例bean & 单例模式
    • 5、单例池
    • 6、bean的线程安全问题
  • 五、依赖注入
    • 1、手动注入 - xml方式(了解)
      • 1)set方法
      • 2)构造方法
    • 2、自动注入 - xml方式(了解)
      • 1)set方法
      • 2)构造方法
    • 3、自动注入 - @Autowired
      • 1)基本使用
      • 2)搭配 @Qualifier
      • 3)三种注入方式
        • 【1】Filed变量注入
        • 【2】Setter方法注入
        • 【3】构造器注入(推荐)
      • 4)构造器注入 案例分析
        • 【1】单个构造
        • 【2】空参构造 + 有参构造
        • 【3】多个有参构造
        • 【4】多个 @Autowired(false)
      • 5)注意事项
    • 4、自动注入 - @Resource
      • 1)基本使用
      • 2)注意事项
    • 5、自动注入 - @Bean注解
      • 1)NO(主要关注这个)
        • 【1】同一配置类
        • 【2】不同配置类
      • 2)BY_NAME(了解)
      • 3)BY_TYPE(了解)
    • 6、自动注入 - @RequiredArgsConstructor
      • 1)基本功能
      • 2)注入功能
  • 六、Bean 的生命周期
    • 1、Bean的创建
    • 2、Bean的生命周期
    • 3、案例演示

Spring IoC

一、IoC概述

1、概述

IoC 全称为 Inversion of Control,即 控制反转,是一种设计思想,而不是一个具体的技术实现。

  • 控制:控制对象的创建和销毁
  • 反转:将对象的控制权(创建和销毁)交给IoC容器(主动new对象 -> 到IoC容器中取)

2、IoC原理

IoC(Inversion of Control)的原理基于两个重要的概念:依赖注入(Dependency Injection)和控制反转(Inversion of Control)。

  1. 依赖注入(Dependency Injection):

    依赖注入是 IoC 的一个重要实现方式,它通过外部传递依赖对象的方式来实现组件之间的依赖关系。

    简单来说,就是将一个对象所依赖的其他对象,通过构造函数、属性或者方法参数的方式注入到该对象中。这样,对象不需要自己创建和管理依赖对象,而是由外部容器负责注入依赖,从而实现了依赖关系的解耦。

  2. 控制反转(Inversion of Control):

    控制反转是 IoC 的核心概念,它将程序的控制权从应用程序代码中反转到容器或者框架中。

    传统的程序设计中,应用程序代码通常负责管理对象的创建、依赖关系的维护等,而在 IoC 中,这些控制权被转移到了外部容器或者框架中,由它们负责创建和管理对象,管理对象之间的依赖关系,并在需要时将对象注入到需要依赖的组件中。

3、IoC作用

IoC(Inversion of Control)即控制反转,是一种软件设计思想,它将控制权从应用程序代码中转移至框架或容器,使得框架或容器负责控制程序的流程和对象的创建、销毁等行为。IoC 的作用主要体现在以下几个方面:

  1. 降低耦合性(Decoupling):

    将组件之间的依赖关系从代码中解耦,通过将依赖关系交由容器管理,组件不再直接依赖于特定的实现类,而是依赖于抽象接口或者规范。这样,当需要替换某个实现类时,只需要修改配置文件或者注解,而不需要修改代码,从而降低了组件之间的耦合性。

  2. 简化代码(Simplifying Code):

    通过 IoC 容器管理对象的生命周期和依赖关系,可以减少开发人员编写大量的样板代码,例如对象的创建、初始化、销毁等操作,使得代码更加简洁、清晰,并且易于维护。

  3. 集中管理(Centralized Management):

    IoC 容器可以集中管理应用程序中的所有组件,包括对象的创建、依赖关系的管理、配置信息的管理等,从而提高了系统的可管理性和可维护性。

  4. 提高灵活性(Improving Flexibility):

    由于依赖关系和配置信息被集中管理,可以通过修改配置文件或者注解来实现不同的配置,从而使得系统更加灵活,能够快速适应不同的需求和变化。

  5. 提高可测试性(Improving Testability):

    由于依赖关系被注入到组件中,可以更方便地进行单元测试和集成测试,通过替换模拟对象或者配置不同的依赖关系,可以轻松地对组件进行测试,提高了系统的可测试性。

总的来说,IoC 通过将控制权从应用程序代码中转移至框架或容器,降低了组件之间的耦合性,简化了代码,集中了管理,提高了灵活性和可测试性,是一种强大的软件设计思想,被广泛应用于各种软件开发项目中。

4、Spring Bean

简单来说,Spring Bean 就是那些被 IoC 容器所管理的对象。

二、Spring容器 相关接口

在这里插入图片描述

1、BeanFactory

BeanFactory最顶层的容器接口,只提供了获取Bean相关的一些方法。

public interface BeanFactory {// 对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象String FACTORY_BEAN_PREFIX = "&";// 根据bean的名字,在IOC容器中得到bean实例,Object getBean(String name) throws BeansException;// 根据bean的名字,在IOC容器中得到bean实例,args:显式参数(必须为非单例模式)Object getBean(String name, Object... args) throws BeansException;// 根据bean的名字获得对象,并转换为Class类型(可以不用进行类型转换)<T> T getBean(String name, Class<T> requiredType);// 根据bean的类型获得对象(必须是拥有唯一实现类)<T> T getBean(Class<T> requiredType) throws BeansException;// 根据bean的类型获得对象,args:显式参数<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;// 判断IOC容器是否有这个名字的beanboolean containsBean(String name);// 判断这个bean是不是单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 判断这个bean是不是多例 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 获取bean实例的Class类型  Class<?> getType(String name) throws NoSuchBeanDefinitionException;// 这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来  String[] getAliases(String name);
}

2、ApplicationContext

ApplicationContext是更高级的容器接口,继承了许多其他接口,提供了更多的功能。

  • 获取 Environment(EnvironmentCapable)
  • getBean 的扩展(ListableBeanFactory)
  • 事件的发布与监听机制(ApplicationEventPublisher)
  • 支持父子容器(HierarchicalBeanFactory)
  • 国际化(MessageSource)
  • 事件的发布与监听机制(ApplicationEventPublisher)
  • 资源访问(ResourcePatternResolver)
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {@NullableString getId();String getApplicationName();String getDisplayName();long getStartupDate();@NullableApplicationContext getParent();// 用于进行手动的 Bean 注入和依赖注入AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;}

1)HierarchicalBeanFactory

HierarchicalBeanFactory 用于表示具有层次结构的 BeanFactory 容器。

  • 支持父子容器的关系,一个 BeanFactory 可以有一个或多个父容器。

    子容器可以访问父容器中定义的 Bean,但父容器不能访问子容器中定义的 Bean。

  • 如果子容器中不存在某个 Bean,则会委托父容器进行查找。

public interface HierarchicalBeanFactory extends BeanFactory {@NullableBeanFactory getParentBeanFactory();boolean containsLocalBean(String name);
}

2)ListableBeanFactory

ListableBeanFactory 扩展了 BeanFactory 接口,提供了额外的功能来 获取 Bean 及其相关信息

public interface ListableBeanFactory extends BeanFactory {// 获取 Bean 的定义信息boolean containsBeanDefinition(String beanName);int getBeanDefinitionCount();String[] getBeanDefinitionNames();// 通过 类型 获取 BeanString[] getBeanNamesForType(ResolvableType type);String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);String[] getBeanNamesForType(@Nullable Class<?> type);String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);// 通过 注解 获取BeanString[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);// 获取 <beanName, type><T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;// 获取 <beanName, annotation>Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;// 获取bean的注解@Nullable<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)throws NoSuchBeanDefinitionException;
}

3)EnvironmentCapable

EnvironmentCapable 接口用于获取 Environment 实例

public interface EnvironmentCapable {Environment getEnvironment();
}

Environment 用于表示运行时环境(例如配置文件、系统属性、环境变量等)的抽象,提供了访问应用程序配置信息的统一接口。

public interface Environment extends PropertyResolver {// 返回激活profile名称的数组String[] getActiveProfiles();// 返回默认profile名称的数组String[] getDefaultProfiles();// environment是否支持给定profileboolean acceptsProfiles(Profiles profiles);@Deprecated // 弃用boolean acceptsProfiles(String... profiles);
}
public interface PropertyResolver {// 检查某个属性是否存在boolean containsProperty(String key);// 获取指定属性的值(不存在则返回null)String getProperty(String key);// 获取指定属性的值(不存在则返回默认值)String getProperty(String key, String defaultValue);// 获取指定属性的值,可以指定要返回对象的类型(不存在则返回null)<T> T getProperty(String key, Class<T> targetType);// 获取指定属性的值,可以指定要返回对象的类型(不存在则返回默认值)<T> T getProperty(String key, Class<T> targetType, T defaultValue);// 获取指定属性的值(不存在则抛出异常)String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;String resolvePlaceholders(String text);String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}

4)ApplicationEventPublisher

ApplicationEventPublisher 用于发布应用程序事件并通知所有已注册的事件监听器

@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);
}

5)MessageSource

MessageSource 是 Spring Framework 中用于国际化支持的接口之一。

  • 它提供了一种统一的方式来访问应用程序中的消息资源,支持多语言和多地区的国际化功能。
public interface MessageSource {String getMessage(String code, Object[] args, String defaultMessage, Locale locale);String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

6)ResourcePatternResolver

ResourcePatternResolver 加载和访问应用程序中的资源,支持通配符和模式匹配,可以方便地加载多个资源。

public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String var1) throws IOException;
}
public interface ResourceLoader {String CLASSPATH_URL_PREFIX = "classpath:";Resource getResource(String var1);@NullableClassLoader getClassLoader();
}

3、ApplicationContext 的实现

ApplicationContext 负责创建和管理应用程序中的所有 Bean 对象,其实现类的区别仅仅在于如何加载配置

在这里插入图片描述

  • AnnotationConfigApplicationContext:使用「注解方式」加载配置
  • ClassPathXmlApplicationContext:从「资源根目录」下加载配置
  • FileSystemXmlApplicationContext:从「文件路径下」加载配置

4、BeanFactory & ApplicationContext

从接口来看:

  • BeanFactory:Spring容器的顶层接口,只提供了获取Bean相关的一些方法。
  • ApplicationContext:更高级的Spring容器接口,继承了许多其他接口,提供了更多的功能。

从单例bean的加载顺序来看:

  • BeanFactory:延迟加载(什么时候使用,什么时候创建)
  • ApplicationContext:饥饿加载(创建Spring容器时,就创建bean,不管是否使用)

注意:以上只针对单例bean(什么是单例bean请看「Bean的作用域」),多例bean

【案例演示】

<!-- load.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="loadBean" class="com.spring.ioc.demo.LoadBean"/></beans>
public class LoadBeanTest {public static void main(String[] args) {System.out.println("======ApplicationContext容器开始创建=====");ApplicationContext applicationContext = new ClassPathXmlApplicationContext("load.xml");System.out.println("======ApplicationContext容器创建完成=====");applicationContext.getBean("loadBean");System.out.println(" ");System.out.println("======BeanFactory容器开始创建=====");Resource resource = new ClassPathResource("load.xml");BeanFactory beanFactory = new XmlBeanFactory(resource);System.out.println("======BeanFactory容器创建完成=====");beanFactory.getBean("loadBean");}}

运行结果如下所示:

======ApplicationContext容器开始创建=====
LoadBean初始化
======ApplicationContext容器创建完成===========BeanFactory容器开始创建=====
======BeanFactory容器创建完成=====
LoadBean初始化

三、Bean 的声明方式

1、XML的bean标签

resource目录下,定义要声明的bean的xml文件

<!-- spring.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="account" class="com.xxx.spring.pojo.Account"/></beans>

通过 ClassPathXmlApplicationContext,读取 xml文件,注册bean

public class ApplicationContextTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");Account account = (Account) context.getBean("account");}
}

1)构造函数方式

# 说明使用空参构造创建对象,并装配到IOC容器中(默认方式)
# 注意被实例化的Bean中必须要有空参构造函数,否则会创建失败。
# 场景当各个bean的业务逻辑相互比较独立时,或者与外界关联较少时可以使用。

【xml配置】

<!--空参构造实例化-->
<bean id="account" class="com.xxx.spring.pojo.Account"/>

2)静态工厂方式

# 说明使用静态工厂中的静态方法创建对象,并装配到IOC容器中。
# 场景1. 可以统一管理多个bean,本身不被Spring管理。		2. 当各个bean在 创建之前 需要相同的初始化处理时,可以使用静态factory方法进行统一的处理。

【静态工厂】

public class StaticFactory {public static Account createAccount(){System.out.println("静态工厂构建1!");return new Account();}public static User createUser(){System.out.println("静态工厂构建2!");return new User();}
}

【xml配置】

<!--静态工厂实例化-->
<bean id="accountStatic" class="com.xxx.spring.factory.StaticFactory" factory-method="createAccount"/>
<bean id="user"class="com.xxx.spring.factory.StaticFactory"factory-method="createUser"/>
  • id:指定 bean 的 id,用于从容器中获取对象
  • class:静态工厂类的全限定名
  • factory-method:指定创建对象的静态方法

3)实例工厂方式

# 说明使用工厂中的实例方法创建对象,并装配到IOC容器中。
# 注意1. 先要将实例工厂类作为一个bean装配到spring容器中。2. 然后再引用工厂bean,调用里面的非静态方法获取bean,并装配到spring的IOC容器中。
# 场景1. 可以统一管理多个bean,本身也被Spring管理。	2. 将实例factory也作为业务bean控制,常用于集成其他框架bean创建管理配置类

【实例工厂】

public class InstanceFactory {public Account createAccount(){System.out.println("实例工厂构建!");return new Account();}public User createUser(){System.out.println("实例工厂构建!");return new User();}
}

【xml配置】

<!--实例工厂实例化-->
<bean id="instanceFactory" class="com.xxx.spring.factory.InstanceFactory"/>
<bean id="account" factory-bean="instanceFactory" factory-method="createAccount"/>
<bean id="user" factory-bean="instanceFactory" factory-method="createUser"/>
  • id:指定 bean 的 id,用于从容器中获取对象
  • class:实例工厂类的全限定名
  • factory-bean:指定实例工厂类的 bean 的 id
  • factory-method:指定创建对象的实例方法

2、@Component

@Component注解:表明该类会作为组件类,并告知Spring要为这个类创建 bean。

Spring容器中的所有的bean都会指定一个ID,默认是 将类名的第一个字母变为小写

/*** 可以为bean设置不一样的ID*/    
@Component("pc")
public class Computer {}

注意:组件扫描默认是不启用的,需要配置 @ComponentScan 注解开启。

1)配合 @ComponentScan

@ComponentScan注解:开启组件扫描,查找带有@Component注解的类并为其创建一个bean。

Spring会扫描@ComponentScan指定的包下的组件。默认是 @ComponentScan 标记的 配置类所在的包这个包下的所有子包

/*** 指定扫描的基础包(单个) - 通过value属性中指明包的名称*/ 
@Configuration
@ComponentScan("package")
public class BeanConfig {}
/*** 指定不同的基础包(多个) - 通过basePackages属性中指明包的名称*/ 
@Configuration
@ComponentScan(basePackages = {"package1","package2"})
public class BeanConfig {}

如果觉得指定包是类型不安全(not type-safe)的,也可以指定包中所包含的类或接口:

/*** 这些类或接口所在的包,将会作为组件扫描的基础包*/ 
@Configuration
@ComponentScan(basePackageClasses = {ClassA.class, ClassB.class, InterfaceA.class})
public class BeanConfig {}

2)@Component 的扩展

@Repository@Service@Controller这三个注解上都加了@Component,可以起到一样的效果。

  • @Repository:对应 持久层,主要用于数据库相关操作。
  • @Service:对应 服务层,主要涉及一些复杂的逻辑,需要用到 持久层。
  • @Controller:对应 控制层,主要用于接受用户请求并调用 服务层 返回数据给前端页面。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {@AliasFor(annotation = Component.class)String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {@AliasFor(annotation = Component.class)String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {@AliasFor(annotation = Component.class)String value() default "";
}

3、@Bean

1)搭配 @Configuration

@Configuration
public class Config {@Beanpublic User user() {return new User();}
}

2)搭配 @Component

@Component
public class Config {@Beanpublic User user() {return new User();}
}

3)搭配 ApplicationContext

public class Config {@Beanpublic User user() {return new User();}
}

AnnotationConfigApplicationContext 读取 Config 对象,作用和 @Configuration 一致。

public class ApplicationContextTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);User user = (User) context.getBean("user");}
}

4、FactoryBean

/*** 实现FactoryBean后,会生成2个bean对象* 1、beanName = factoryBeanImpl   ------  getObject()返回的类 FactoryBeanObject* 2、beanName = &factoryBeanImpl  ------  FactoryBean的实现类 FactoryBeanImpl*/
@Component
public class FactoryBeanImpl implements FactoryBean<FactoryBeanObject> {/*** 在`getBean`的时候才会调用(懒加载)*/@Overridepublic FactoryBeanObject getObject() {return new FactoryBeanObject();}@Overridepublic Class<?> getObjectType() {return FactoryBeanObject.class;}}

如果 类A 实现了 FactoryBean 接口,那么 类A 就变成了 “A工厂”。

  • 根据 a 获得的实际上是调用 类A 的 getObject() 方法返回的对象, 而不是 “A工厂”自己。

5、SmartFactoryBean

/*** SmartFactoryBean 和 FactoryBean 类似,区别在于:* 		FactoryBean 的 `getObject()` 不是在Spring启动时调用的,是在getBean的时候调用的,有点类似于懒加载。* 		而 SmartFactoryBean 可以设置是否在Spring容器启动时就调用`getObject()`方法。*/
@Component
public class SmartFactoryBeanImpl implements SmartFactoryBean<SmartFactoryBeanObject> {@Overridepublic SmartFactoryBeanObject getObject() {System.out.println("调用了SmartFactoryBeanImpl的getObject()");return new SmartFactoryBeanObject();}@Overridepublic Class<?> getObjectType() {return SmartFactoryBeanObject.class;}/*** 是否在Spring容器启动时就调用getObject()方法*/@Overridepublic boolean isEagerInit() {return false;}}

6、registerBean 方法

/*** 通过 ApplicationContext 的 registerBean方法 注册bean*/
public class DefineByRegisterBean {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);context.registerBean(User.class, new Supplier<User>() {@Overridepublic User get() {// 可以定义bean对象的属性User user = new User();user.setName("zs");return user;}});context.refresh();User user = (User) context.getBean("user");}
}

四、Bean 的作用域

1、bean的作用域

Spring官方文档中给出的bean的scope有五种

value取值value描述
singleton单例,Spring容器只会创建该bean的唯一实例【默认】
prototype多例,每个请求都会创建一个新的实例
request每个 http request 都会创建一个新的实例【仅在 WebApplicationContext 中有效】
session每个 http session 都会创建一个新的实例【仅在 WebApplicationContext 中有效】
application全局的 http session 中,容器只会创建该bean的唯一实例【仅在 WebApplicationContext 中有效】

BeanDefinition中只有两种scope,就是singletonprototype

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;	// singletonString SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;	// prototype
}

其余三种scope,都在WebApplicationContext

package org.springframework.web.context;public interface WebApplicationContext extends ApplicationContext {String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";String SCOPE_REQUEST = "request";String SCOPE_SESSION = "session";String SCOPE_APPLICATION = "application";String SERVLET_CONTEXT_BEAN_NAME = "servletContext";String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";@NullableServletContext getServletContext();
}

在这里插入图片描述

2、@Scope 注解

@Scope注解加在类上方,可以设置bean的作用域

@Configuration
public class ScopeConfig {/*** 默认情况是单例bean*/@Bean("singletonBean")public SingletonBean singletonBean() {return new SingletonBean();}/*** 多例bean需要加上 @Scope("prototype")*/@Bean("prototypeBean")@Scope("prototype")public PrototypeBean prototypeBean() {return new PrototypeBean();}}
public class ScopeTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);System.out.println(context.getBean("singletonBean"));System.out.println(context.getBean("singletonBean"));System.out.println(context.getBean("prototypeBean"));System.out.println(context.getBean("prototypeBean"));}
}
com.dmeo.spring.ioc.scope.model.SingletonBean@1ffe63b9
com.dmeo.spring.ioc.scope.model.SingletonBean@1ffe63b9
com.dmeo.spring.ioc.scope.model.PrototypeBean@6ac13091
com.dmeo.spring.ioc.scope.model.PrototypeBean@5e316c74

3、扩展 @Scope 注解

可以自定义注解,加上@Scope("prototype"),也能实现多例bean。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Scope("prototype")
public @interface CustomPrototype {}
@Configuration
public class ScopeConfig {/*** 使用自定义注解声明多例bean*/@Bean("customPrototypeBean")@CustomPrototypepublic PrototypeBean customPrototypeBean() {return new PrototypeBean();}}
public class ScopeTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(ScopeConfig.class);System.out.println(context.getBean("customPrototypeBean"));System.out.println(context.getBean("customPrototypeBean"));}
}
com.dmeo.spring.ioc.scope.model.PrototypeBean@1e802ef9
com.dmeo.spring.ioc.scope.model.PrototypeBean@2b6faea6

4、单例bean & 单例模式

单例bean 和 单例模式 是有区别的:

  • 单例模式:一个类只有一个实例
  • 单例bean:一个beanName只会获取到一个实例(但是同一个类可能会有多个实例)

5、单例池

单例池:一个存放单例bean的Map集合

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

单例bean的获取:

  • 根据 beanName,到 singletonObjects 单例池 中获取 bean。
    • 如果获取到了,直接返回
    • 如果获取不到
      • 调用 singletonFactorycreateBean方法,创建单例bean
      • 创建成功以后,存入 singletonObjects 单例池,并将引用返回。

6、bean的线程安全问题

  • 对于多例bean来说,每次请求都会生成一个新的bean,是线程安全的
  • 对于单例bean来说:
    • 如果bean是无状态的(无实例变量,不会保存数据),那么bean是线程安全的
    • 如果bean是有状态的(有实例变量,有数据存储功能),那么bean不是线程安全的

那么如何解决单例bean存在的线程安全问题呢?

  1. 改变作用域:
    • 非web应用,在对应的类名上加上注解 @Scope("prototype"),改成多例bean。
    • web应用,在对应的类名上加上注解 @Scope("request"),也可以达到一样的效果。
  2. 不改变作用域:
    • 尽量避免定义可变的成员变量。(不太现实)
    • 将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

实际应用中,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。

五、依赖注入

依赖注入:Dependency Injection(简称DI注入)。

  • 作用:建立bean与bean之间的关系,能够让相互协作的软件组件保持松散耦合
  • 前提:Spring管理的对象
  • 理解:以解耦的方式,给IOC容器中的bean属性赋值,简称DI注入

Spring中,对象无需自己查找或创建与其所关联的其他对象。容器负责把需要相互协作的对象引用赋予各个对象。

创建应用对象之间协作关系的行为 通常称为 装配(wiring),这也是 依赖注入(DI)的本质。

1、手动注入 - xml方式(了解)

1)set方法

<bean name="orderService" class="com.xxx.service.OrderService"/><bean name="userService" class="com.xxx.service.UserService"><property name="orderService" ref="orderService"/>
</bean>
public class OrderService {private UserService userService;// 底层通过set方法注入,必须要有set方法,否则会报错public void setUserService(UserService userService) {this.userService = userService;}}

2)构造方法

<bean name="orderService" class="com.xxx.service.OrderService"/><bean name="userService" class="com.xxx.service.UserService"><constructor-arg index="0" ref="orderService"/>
</bean>
public class OrderService {private UserService userService;public OrderService(UserService userService) {this.userService = userService;}
}

2、自动注入 - xml方式(了解)

1)set方法

<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="byType"/>
<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="byName"/>
public class OrderService {private UserService userService;// 通过set方法注入,必须要有set方法,否则会报错(byName是根据set方法名set后面的部分)public void setUserService(UserService userService) {this.userService = userService;}}

2)构造方法

<bean name="orderService" class="com.xxx.service.OrderService"/>
<bean name="userService" class="com.xxx.service.UserService" autowired="constructor"/>
public class OrderService {private UserService userService;public OrderService(UserService userService) {this.userService = userService;}
}

3、自动注入 - @Autowired

1)基本使用

/*** 依赖注入之 @Autowired*  先byType*      1)根据type有一个 -> 直接注入*      2)根据type有多个 -> 再根据name筛选*  再byName*      1)根据name有一个 -> 直接注入*      2)根据name没找到 -> 项目启动就会报错 NoUniqueBeanDefinitionException(type有多个,name不存在)*      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException**  required属性(默认为true)*      1)required=false -> 允许找不到bean注入,值为null*      2)required=true  -> 必须找到bean注入*/
@Component
public class InjectByAutowired {/*** 先根据type -> 找到多个 -> 再根据name -> 找到一个 -> 注入blue*/@Autowiredprivate Color blue;/*** 启动就报错 NoUniqueBeanDefinitionException(和上面的区别是,有blue,但是没有green)*/
//    @Autowired
//    private Color green;/*** 先根据type*   1)required = true  --> 抛错*   2)required = false --> 不抛错,noBeanObject = null*/@Autowired(required = false)private NoBeanObject noBeanObject;public void testInject() {blue.printName();System.out.println("noBeanObject = " + noBeanObject);	// null}}

2)搭配 @Qualifier

type有多个,name不存在 导致启动报错的情况,通过 @Qualifier 可以解决

@Component
public class InjectWithQualifier {/*** 不加 @Qualifier -> 启动就报错 NoUniqueBeanDefinitionException(type有多个,name不存在)* 加了 @Qualifier -> 从多个Color中选择red注入*/@Autowired@Qualifier("red")private Color green;public void testInject() {green.printName();}}

3)三种注入方式

【1】Filed变量注入
@Component
public class OrderService {@Autowiredprivate DependencyA dependencyA;@Autowiredprivate DependencyB dependencyB;@Autowiredprivate DependencyC dependencyC;
}
【2】Setter方法注入
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;// 参数是 要注入的对象@Autowiredpublic void setDependencyA(DependencyA dependencyA) {this.dependencyA = dependencyA;}@Autowiredpublic void setDependencyB(DependencyB dependencyB) {this.dependencyB = dependencyB;}@Autowiredpublic void setDependencyC(DependencyC dependencyC) {this.dependencyC = dependencyC;}
}
【3】构造器注入(推荐)
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;// 参数是 要注入的对象@Autowiredpublic OrderService(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {this.dependencyA = dependencyA;this.dependencyB = dependencyB;this.dependencyC = dependencyC;}
}

4)构造器注入 案例分析

【1】单个构造

在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入(可省略@Autowired)

/*** 使用空参构造实例化*/
@Component
public class UserService {private User user;public UserService() {}}
/*** 使用有参构造实例化*/
@Component
public class UserService {private User user;public UserService(User user) {this.user = user;}}
【2】空参构造 + 有参构造
/*** 空参构造 + 有参构造* ① 没有@Autowired --> 默认使用空参构造(不会注入InjectService)* ② 一个@Autowired --> 使用@Autowired标记的构造方法* ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... *							 Found constructor with 'required' Autowired annotation already*/
@Component
public class ManyConstruction1 {public InjectService injectService;public ManyConstruction1() {}public ManyConstruction1(InjectService injectService) {this.injectService = injectService;}}
【3】多个有参构造
/*** 多个有参构造* ① 没有@Autowired --> 报错:No default constructor found; *							 nested exception is java.lang.NoSuchMethodException: *							 com.demo.spring.ioc.inject.construct.ManyConstruction2.<init>()* ② 一个@Autowired --> 使用@Autowired标记的构造方法* ③ 多个@Autowired --> 报错:Invalid autowire-marked constructor ... *							 Found constructor with 'required' Autowired annotation already*/
@Component
public class ManyConstruction2 {public InjectService injectService;public InjectService2 injectService2;@Autowiredpublic ManyConstruction2(InjectService injectService) {this.injectService = injectService;}public ManyConstruction2(InjectService injectService1, InjectService2 injectService2) {this.injectService = injectService1;this.injectService2 = injectService2;}}
【4】多个 @Autowired(false)
/*** 如果多个@Autowired都声明了required = false,不会报错* ① 优先使用参数最多的构造方法* ② 如果参数多的构造方法的参数bean不存在/找不到 --> 使用参数第二多的构造方法(以此类推)* ③ 如果存在参数一样多的构造方法 --> 使用声明在上面的构造方法*/
@Component
public class ManyConstruction3 {public InjectService injectService;public InjectService2 injectService2;public NoBeanObject noBeanObject;public ManyConstruction3() {}@Autowired(required = false)public ManyConstruction3(InjectService injectService) {System.out.println("使用了1个参数的构造方法1");this.injectService = injectService;}@Autowired(required = false)public ManyConstruction3(InjectService2 injectService2) {System.out.println("使用了1个参数的构造方法2");this.injectService2 = injectService2;}@Autowired(required = false)public ManyConstruction3(InjectService injectService, InjectService2 injectService2) {System.out.println("使用了2个参数的构造方法");this.injectService = injectService;this.injectService2 = injectService2;}@Autowired(required = false)public ManyConstruction3(InjectService injectService, InjectService2 injectService2, NoBeanObject noBeanObject) {System.out.println("使用了3个参数的构造方法");this.injectService = injectService;this.injectService2 = injectService2;this.noBeanObject = noBeanObject;}}

5)注意事项

父类中 @Autowired 修饰的会被注入

public class BaseService {// 是否会被注入??@Autowiredpublic UserService userService;}@Component
public class OrderService extends BaseService {private void test() {System.out.println(userService); // 会被注入!}}

static修饰的属性不会被注入

@Component
public class OrderService {// 是否会被注入??@Autowiredprivate static UserService userService;private void test() {System.out.println(userService); // null 不会被注入}}

4、自动注入 - @Resource

1)基本使用

/*** 依赖注入之 @Resource*  1. 指定name(根据name)*      1)根据name有一个 -> 直接注入*      2)根据name没找到 -> 项目启动就会报错 NoSuchBeanDefinitionException*      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException**  2. 指定type(先根据type,再根据name)*      1)根据type有一个 -> 直接注入*      2)根据type没找到 -> 抛错*      3)根据type有多个 -> 再根据name找**  3. 都不指定(先根据name,再根据type)*      1)根据name有一个 -> 直接注入*      2)根据name没找到 -> 再根据type找 *						-> 根据type有一个 -> 直接注入*						-> 根据type有多个 -> 抛错 NoUniqueBeanDefinitionException*      3)根据name有多个 -> 项目启动就会报错 ConflictingBeanDefinitionException*/
@Component
public class InjectByResource {/*** 先根据name -> 注入的是Red*/@Resource(name = "red")private Color blue;/*** 先根据type -> 找到多个 -> 再根据name -> 注入white*/@Resource(type = Color.class)private Color white;/*** 默认先根据name -> 找不到 -> 再根据type -> 找到多个 -> 抛错 NoUniqueBeanDefinitionException*/
//    @Resource
//    private Color green;/*** 默认先根据name -> 找不到 -> 再根据type -> 找到一个 -> 注入oneBeanObject*/@Resourceprivate OneBeanObject oneBeanObj;public void testInject() {blue.printName();white.printName();}}

2)注意事项

@Resource 注解不是Spring提供的,是由JDK提供的(javax.annotation.Resource),但是解析是由Spring负责的。

5、自动注入 - @Bean注解

可以看到,@Beanautowire属性已经被打上了@Deprecated,这里重点关注default Autowire.NO,其他的做一个了解。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {@DeprecatedAutowire autowire() default Autowire.NO;
}
public enum Autowire {/*** Constant that indicates no autowiring at all.*/NO(AutowireCapableBeanFactory.AUTOWIRE_NO),/*** Constant that indicates autowiring bean properties by name.*/BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),/*** Constant that indicates autowiring bean properties by type.*/BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);// ...
}

1)NO(主要关注这个)

autowire 这个属性已经过期了,所以我们主要关注 default Autowire.NO(也就是不指定该属性),其余两个了解一下即可。

【1】同一配置类

同一配置类中,可以直接引用依赖对象的@Bean方法进行注入(必须在同一配置类中)

@Configuration
public class Config {/*** orderService()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,* 确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。*/@Beanpublic OrderService orderService() {return new OrderService();}@Beanpublic UserService userService() {return new UserService(orderService());}}

注意:看起来 UserService 对应的bean 是通过调用 orderService() 得到的,但情况并非完全如此。

默认情况下,@Configuration标记的Config是代理对象,因此调用的 orderService() 是代理类调用的

  • 如果 OrderService 对应的bean不存在,会创建对应的bean
  • 如果 OrderService 对应的bean已存在,会直接返回已存在的bean

也就是说,每次调用 orderService() 方法,得到的都是同一个bean

【2】不同配置类

不同配置类中,可以通过@Bean修饰方法的参数进行注入(不需要在统一配置类也可以)

@Configuration
public class Config1 {@Beanpublic OrderService orderService() {return new OrderService();}
}
@Configuration
public class Config2 {// 创建UserService的bean时,会自动装配一个OrderService@Beanpublic UserService userService(OrderService orderService) {return new UserService(orderService);}
}

这样在构建UserService的bean时,Spring会找到OrderService的bean,并传入@Bean方法构建UserService的bean。

2)BY_NAME(了解)

@Configuration
public class BeanInjectConfig {/*** autowire = Autowire.BY_NAME    根据set方法注入(name 根据 setXxx 的 Xxx)* 1、没有set方法 -> 注入失败(属性为null,不报错)* 2、根据name没找到 -> 注入失败(属性为null,不报错)* 3、根据name找到了 -> 注入成功*/@Bean(autowire = Autowire.BY_NAME)public BeanInjectService beanInjectService() {return new BeanInjectService();}}
public class BeanInjectService {private OneBeanObject oneBeanObject;private NoBeanObject noBeanObject;private Color color;/*** 根据name找到了 -> 注入成功*/public void setOneBeanObject(OneBeanObject oneBeanObject) {this.oneBeanObject = oneBeanObject;}/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配*///    public void setOneBeanObject123(OneBeanObject oneBeanObject) {//        this.oneBeanObject = oneBeanObject;//    }/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean*/public void setNoBeanObject(NoBeanObject noBeanObject) {this.noBeanObject = noBeanObject;}/*** 根据name没找到 -> 注入失败(属性为null,不报错)---> 存在bean,但是set后面的name不匹配*/public void setColor(Color color) {this.color = color;}/*** 根据name找到了 -> 注入成功*/
//    public void setBlue(Color color) {
//        this.color = color;
//    }}

3)BY_TYPE(了解)

@Configuration
public class BeanInjectConfig {/*** autowire = Autowire.BY_TYPE    根据方法的参数类型注入(必须是set开头的方法)* 1、没有set开头的方法 -> 注入失败(属性为null,不报错)* 2、根据type没找到 -> 注入失败(属性为null,不报错)* 3、根据type找到一个 -> 注入成功* 4、根据type找到多个 -> 报错 NoUniqueBeanDefinitionException*/@Bean(autowire = Autowire.BY_TYPE)public BeanInjectService beanInjectService() {return new BeanInjectService();}}
public class BeanInjectService {private OneBeanObject oneBeanObject;private NoBeanObject noBeanObject;private Color color;/*** 根据type找到一个 -> 注入成功(必须是set开头的方法)*/public void set123(OneBeanObject oneBeanObject) {this.oneBeanObject = oneBeanObject;}/*** 根据type没找到 -> 注入失败(属性为null,不报错)---> 本来就没有bean*/public void setNoBeanObject(NoBeanObject noBeanObject) {this.noBeanObject = noBeanObject;}/*** 根据type找到一个 -> 注入成功(必须是set开头的方法)*/public void setBlue(Blue color) {this.color = color;}/*** 根据type找到多个 -> 报错 NoUniqueBeanDefinitionException*/
//    public void setColor(Color color) {
//        this.color = color;
//    }}

6、自动注入 - @RequiredArgsConstructor

@RequiredArgsConstructorLombok 提供的一个注解

  • 当一个类中包含 final@NonNull 注解修饰的字段时,这个注解会为该类自动生成一个包含这些字段的构造函数。

1)基本功能

@RequiredArgsConstructor
public class Person {@NonNullprivate String name;private final Integer age;private String address;
}

上面的代码使用 @RequiredArgsConstructor 注解后,Lombok 会自动生成以下代码:

public class Person {private @NonNull String name;private final Integer age;private String address;public Person(final @NonNull String name, final Integer age) {if (name == null) {throw new NullPointerException("name is marked non-null but is null");} else {this.name = name;this.age = age;}}
}

2)注入功能

在 Spring4.3 之后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入(可省略@Autowired)

因此,@RequiredArgsConstructor 可以实现构造器注入,而且可以避免冗余的构造方法。

@RequiredArgsConstructor
@Component
public class OrderService {// 待注入的属性private DependencyA dependencyA;private DependencyB dependencyB;private DependencyC dependencyC;
}

注意,使用@RequiredArgsConstructor 注入时,最好别再写其他构造方法,避免造成影响(参考「构造器注入 案例分析」)

六、Bean 的生命周期

1、Bean的创建

单例bean:只会创建一次

  • BeanFactory:延迟加载(什么时候使用,什么时候创建)
  • ApplicationContext:饥饿加载(创建Spring容器时,就创建bean,不管是否使用)

多例bean:每次使用都创建一个新的bean

2、Bean的生命周期

Bean在Spring容器中从创建到销毁经历了若干个阶段:

在这里插入图片描述

# 实例化1. Spring对bean进行实例化推断构造方法/工厂方法,基于 BeanDefinition 对bean进行实例化,实例化的Instance封装在 BeanWrapper 对象中。# 属性填充2. Spring将值和依赖的bean填充到bean对应的属性中根据 BeanDefinition 获取 PropertyValues,填充属性到 BeanWrapper 中的 Instance。# 初始化3. 如果bean实现了 BeanNameAware 接口-> Spring会调用 setBeanName(String beanName) 方法4. 如果bean实现了 BeanClassLoaderAware 接口-> Spring会调用 setBeanClassLoader(ClassLoader classLoader) 方法4. 如果bean实现了 BeanFactoryAware 接口-> Spring会调用 setBeanFactory(BeanFactory beanFactory) 方法5. 如果bean实现了 ApplicationContextAware 接口-> Spring会调用 setApplicationContext(ApplicationContext applicationContext) 方法6. 如果bean实现了 BeanPostProcessor 接口-> Spring会调用 postProcessBeforeInitialization(Object bean, String beanName) 方法7. 如果bean实现了 InitializingBean 接口-> Spring会调用 afterPropertiesSet() 方法8. 如果bean定义时声明了初始化方法 initMethod()-> Spring会执行 自定义的初始化方法 initMethod()9. 如果bean实现了 BeanPostProcessor 接口-> Spring会调用 postProcessAfterInitialization(Object bean, String beanName) 方法# 销毁10. 如果bean实现了DisposableBean接口-> Spring会调用 destroy() 方法,进行bean的销毁。11. 如果bean定义时声明了销毁方法 destroyMethod()-> Spring会执行 自定义的销毁方法 destroyMethod()

3、案例演示

@Configuration
public class CycleConfig {/*** 指定 自定义初始化 & 自定义销毁 方法*/@Bean(initMethod = "customInit", destroyMethod = "customDestroy")public BeanCycle beanCycle() {return new BeanCycle();}}
/*** Bean的生命周期*/
public class BeanLifecycle implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,ApplicationContextAware, BeanPostProcessor, InitializingBean, DisposableBean  {public BeanLifecycle() {System.out.println("执行 构造方法");}@Overridepublic void setBeanName(String name) {System.out.println("执行 BeanNameAware 的 setBeanName 方法");}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("执行 BeanClassLoaderAware 的 setBeanClassLoader 方法");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("执行 BeanFactoryAware 的 setBeanFactory 方法");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("执行 ApplicationContextAware 的 setApplicationContext 方法");}@PostConstructvoid postConstruct() {System.out.println("执行 @PostConstruct 标记的方法");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// System.out.println("执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法");return bean;}@Overridepublic void afterPropertiesSet() {System.out.println("执行 InitializingBean 的 afterPropertiesSet 方法");}public void customInit() {System.out.println("执行 自定义的初始化方法 customInit");}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// System.out.println("执行 BeanPostProcessor 的 postProcessAfterInitialization 方法");return bean;}@PreDestroyvoid preDestroy() {System.out.println("执行 @PreDestroy 标记的方法");}@Overridepublic void destroy() {System.out.println("执行 DisposableBean 的 destroy 方法");}public void customDestroy() {System.out.println("执行 自定义的销毁方法 customDestroy");}}
// 启动服务执行 构造方法
执行 BeanNameAware 的 setBeanName 方法
执行 BeanClassLoaderAware 的 setBeanClassLoader 方法
执行 BeanFactoryAware 的 setBeanFactory 方法
执行 ApplicationContextAware 的 setApplicationContext 方法
执行 @PostConstruct 标记的方法
执行 InitializingBean 的 afterPropertiesSet 方法
执行 自定义的初始化方法 customInit// 关闭服务执行 @PreDestroy 标记的方法
执行 DisposableBean 的 destroy 方法
执行 自定义的销毁方法 customDestroy

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

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

相关文章

Web3 基金会推出去中心化之声计划:投入高额 DOT 和 KSM ,助力去中心化治理

作者&#xff1a;Web3 Foundation Team 编译&#xff1a;OneBlock 原文&#xff1a;https://medium.com/web3foundation/decentralized-voices-program-93623c27ae43 Web3 基金会为 Polkadot 和 Kusama 创建了去中心化之声计划&#xff08;Decentralized Voices Program&…

软考39-上午题-【数据库】-关系代数运算1-传统的集合运算

一、笛卡尔积 二、关系代数 关系代数是施加于关系之上的集合代数运算。 关系代数包含&#xff1a; 传统的集合运算专门的关系运算 2-1、传统的集合运算 1、关系的并 示例&#xff1a; 2、关系的差 示例&#xff1a; 3、关系的交 示例&#xff1a; 关系的并、差、交&#xf…

LeetCode 448.找到所有数组中消失的数字

目录 1.题目 2.代码及思路 3.进阶 3.1题目 3.2代码及思路 1.题目 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 示例 1&#xff1a; 输入&am…

【数学建模入门】

数学建模入门 数学建模需要的学科知识怎么学习数学模型如何读好一篇优秀论文数学建模赛题常见类别数学建模常见问题数学建模组队和分工数学建模准备工作 数学建模需要的学科知识 怎么学习数学模型 &#x1f4a6;推荐阅读书籍&#xff1a; 《数学建模算法与应用》&#xff0c;…

车规级MCU的行业走向

1 主要厂家 车规级MCU&#xff08;车用微控制器单元&#xff09;的主要厂家包括&#xff1a; NXP半导体&#xff1a;NXP是全球领先的车规级MCU提供商之一&#xff0c;提供广泛的产品用于汽车控制和管理系统。英飞凌科技&#xff1a;作为汽车半导体的领导者之一&#xff0c;英飞…

fly-barrage 前端弹幕库(1):项目介绍

fly-barrage 是我写的一个前端弹幕库&#xff0c;由于经常在 Bilibili 上看视频&#xff0c;所以对网页的弹幕功能一直蛮感兴趣的&#xff0c;所以做了这个库&#xff0c;可以帮助前端快速的实现弹幕功能。 项目官网地址&#xff1a;https://fly-barrage.netlify.app/&#xff…

旅游分享系列之:福建旅游攻略

旅游分享系列之&#xff1a;福建旅游攻略 一、漳州1.福建土楼2.云水谣3.四菜一汤景点 二、厦门1.园林博览苑2.海上自行车道3.山海步道4.海滩5.闽南菜6.落日 三、泉州1.衙口沙滩2.海上日出3.珞珈寺4.海滩烟花 一、漳州 游玩2个景点&#xff1a;云水谣&#xff0c;四菜一汤可以住…

《图解HTTP》笔记2:http的构成

目录 1&#xff0c;查看浏览器上面一个具体的http请求 2&#xff0c;HTTP报文的具体构成 2.1&#xff0c;http的报文结构 2.2&#xff0c;http的请求报文例子 2.3&#xff0c;http的响应报文例子 1&#xff0c;查看浏览器上面一个具体的http请求 浏览器地址栏输入网址&…

ES6内置对象 - Set

Set&#xff08;es6提供的一种数据结构&#xff0c;类似数组&#xff0c;是一个集合&#xff0c;可以存储任何类型的元素且唯一、不重复&#xff0c;so,多用于元素去重&#xff09; 如上图&#xff0c;Set数据结构自带一些方法 1.Set对象创建 let a new Set([1,2,3,3,1,2,4,…

spring boot3登录开发-2(1图形验证码接口实现)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 内容简介 图形验证码接口实现 导入糊涂工具依赖 接口分析 编写验证码接口 测试验证码接口 前置条件 …

浅浅的画一个STDP的图像吧

stdp最重要的是两个窗口函数 根据这个方程我们刻画出他的轨迹&#xff0c;代码如下 import numpy as np import matplotlib.pyplot as plt# 定义STDP参数 tau_pos 30 # 正向突触权重变化的时间常数 tau_neg 30 # 负向突触权重变化的时间常数 A_pos 0.1 # 正向突触权重变…

都说了别用BeanUtils.copyProperties,这不翻车了吧

分享是最有效的学习方式。 博客&#xff1a;https://blog.ktdaddy.com/ 故事 新年新气象&#xff0c;小猫也是踏上了新年新征程&#xff0c;自从小猫按照老猫给的建议【系统梳理大法】完完整整地梳理完毕系统之后&#xff0c;小猫对整个系统的把控可谓又是上到可一个新的高度。…

[计算机网络]--IP协议

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、IP协议…

大数据构建知识图谱:从技术到实战的完整指南

文章目录 大数据构建知识图谱&#xff1a;从技术到实战的完整指南一、概述二、知识图谱的基础理论定义与分类核心组成历史与发展 三、知识获取与预处理数据源选择数据清洗实体识别 四、知识表示方法知识表示模型RDFOWL属性图模型 本体构建关系提取与表示 五、知识图谱构建技术图…

低功耗设计——门控时钟

1. 前言 芯片功耗组成中&#xff0c;有高达40%甚至更多是由时钟树消耗掉的。这个结果的原因也很直观&#xff0c;因为这些时钟树在系统中具有最高的切换频率&#xff0c;而且有很多时钟buffer&#xff0c;而且为了最小化时钟延时&#xff0c;它们通常具有很高的驱动强度。此外&…

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(九)NodeJS入门——http模块

060_http模块_网页URL之绝对路径 hello&#xff0c;大家好&#xff0c;这一个小题的话我们来补充一个之前学习过的内容&#xff0c;就是网页当中的URL&#xff0c;咱们这个小题的话主要是来说一下绝对路径&#xff0c;有同学可能会说&#xff0c;这这这&#xff0c;不对劲&…

yolov8学习笔记(二)模型训练

目录 yolov8的模型训练 1、制作数据集&#xff08;标记数据集&#xff09; 2、模型训练&#xff08;标记数据集、参数设置、跟踪模型随时间的性能变化&#xff09; 2.1、租服务器训练 2.2、加训练参数 2.3、看训练时的参数&#xff08;有条件&#xff0c;就使用TensorBoard&…

nginx设置缓存时间

一、设置缓存时间 当网页数据返回给客户端后&#xff0c;可针对静态网页设置缓存时间&#xff0c;在配置文件内的http段内server段添加location&#xff0c;更改字段expires 1d来实现&#xff1a;避免重复请求&#xff0c;加快访问速度 第一步&#xff1a;修改主配置文件 #修…

vue基础操作(vue基础)

想到多少写多少把&#xff0c;其他的想起来了在写。也写了一些css的 input框的双向数据绑定 html <input value"123456" type"text" v-model"account" input"accou" class"bottom-line bottom" placeholder"请输入…

Vue 卸载eslint

卸载依赖 npm uninstall eslint --save 然后 进入package.json中&#xff0c;删除残留信息。 否则在执行卸载后&#xff0c;运行会报错。 之后再起项目。