Spring-IoC

文章目录

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容器是否有这个名字的bean
    boolean 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 {
   

    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    // 用于进行手动的 Bean 注入和依赖注入
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}

1)HierarchicalBeanFactory

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

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

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

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

public interface HierarchicalBeanFactory extends BeanFactory {
   
    @Nullable
    BeanFactory getParentBeanFactory();

    boolean containsLocalBean(String name);
}

2)ListableBeanFactory

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

public interface ListableBeanFactory extends BeanFactory {
   
    // 获取 Bean 的定义信息
    boolean containsBeanDefinition(String beanName);
    int getBeanDefinitionCount();
    String[] getBeanDefinitionNames();
    
    // 通过 类型 获取 Bean
    String[] 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);
    
    // 通过 注解 获取Bean
    String[] 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是否支持给定profile
    boolean 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);

    @Nullable
    ClassLoader 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 {
   
    @Bean
    public User user() {
   
        return new User();
    }
}

2)搭配 @Component

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

3)搭配 ApplicationContext

public class Config {
   
    @Bean
    public 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`的时候才会调用(懒加载)
     */
    @Override
    public FactoryBeanObject getObject() {
   
        return new FactoryBeanObject();
    }

    @Override
    public 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> {
   

    @Override
    public SmartFactoryBeanObject getObject() {
   
        System.out.println("调用了SmartFactoryBeanImpl的getObject()");
        return new SmartFactoryBeanObject();
    }

    @Override
    public Class<?> getObjectType() {
   
        return SmartFactoryBeanObject.class;
    }

    /**
     * 是否在Spring容器启动时就调用getObject()方法
     */
    @Override
    public 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>() {
   
            @Override
            public 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;	// singleton
    String 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";

    @Nullable
    ServletContext 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")
    @CustomPrototype
    public 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
     */
    @Autowired
    private 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 {
   
    @Autowired
    private DependencyA dependencyA;

    @Autowired
    private DependencyB dependencyB;

    @Autowired
    private DependencyC dependencyC;
}
【2】Setter方法注入
@Component
public class OrderService {
   
    // 待注入的属性
    private DependencyA dependencyA;
    private DependencyB dependencyB;
    private DependencyC dependencyC;

    // 参数是 要注入的对象
    @Autowired
    public void setDependencyA(DependencyA dependencyA) {
   
        this.dependencyA = dependencyA;
    }

    @Autowired
    public void setDependencyB(DependencyB dependencyB) {
   
        this.dependencyB = dependencyB;
    }

    @Autowired
    public void setDependencyC(DependencyC dependencyC) {
   
        this.dependencyC = dependencyC;
    }
}
【3】构造器注入(推荐)
@Component
public class OrderService {
   
    // 待注入的属性
    private DependencyA dependencyA;
    private DependencyB dependencyB;
    private DependencyC dependencyC;

    // 参数是 要注入的对象
    @Autowired
    public 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;

    @Autowired
    public 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 {
   

    // 是否会被注入??
    @Autowired
    public UserService userService;

}

@Component
public class OrderService extends BaseService {
   

    private void test() {
   
        System.out.println(userService); // 会被注入!
    }
    
}

static修饰的属性不会被注入

@Component
public class OrderService {
   

    // 是否会被注入??
    @Autowired
    private 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
     */
    @Resource
    private 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 {
   
    @Deprecated
	Autowire 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,而不是每次都对其进行实际的调用。
     */
    @Bean
    public OrderService orderService() {
   
        return new OrderService();
    }

    @Bean
    public 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 {
   
    @Bean
    public OrderService orderService() {
   
        return new OrderService();
    }
}
@Configuration
public class Config2 {
   
    // 创建UserService的bean时,会自动装配一个OrderService
    @Bean
    public 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 {
   
    @NonNull
    private 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)
		对于ApplicationContext容器,容器初始化就会加载bean(饥饿加载)
		在创建容器的过程中,通过获取BeanDefinition对象中的信息,实例化所有的bean。
		
		对于BeanFactory容器,在使用对象时才会加载bean(延迟加载/懒加载)
		当客户向容器请求一个尚未初始化的bean 或 初始化bean的时候需要注入另一个尚未初始化的bean时,
		BeanFactory容器就会调用createBean进行bean实例化。	

# 属性填充

2. Spring将值和依赖的bean填充到bean对应的属性中
		实例化后的对象被封装在BeanWrapper对象中。
		Spring根据BeanDefinition中的信息,通过BeanWrapper提供的设置属性的接口完成属性填充。

# 初始化

3. 如果bean实现了 BeanNameAware 接口
		-> Spring会调用 setBeanName() 方法,并传入bean的name
		
4. 如果bean实现了 BeanFactoryAware 接口
		-> Spring会调用 setBeanFactory() 方法,并传入BeanFactory容器实例
		
5. 如果bean实现了 ApplicationContextAware 接口
		-> Spring会调用 setApplicationContext() 方法,并传入ApplicationContext实例

6. 如果bean实现了 BeanPostProcessor 接口
		-> Spring会调用 postProcessBeforeInitialization() 方法,在初始化之前执行一些操作
	
7. 如果bean实现了 InitializingBean 接口
		-> Spring会调用 afterPropertiesSet() 方法,在属性设置之后执行一些操作。(如检查某些属性是否为空)

8. 如果bean定义时声明了初始化方法 initMethod()
		-> Spring会执行 自定义的初始化方法 initMethod()

9. 如果bean实现了 BeanPostProcessor 接口
		-> Spring会调用 postProcessAfterInitialization() 方法,在初始化之后执行一些操作


# 销毁

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, InitializingBean, DisposableBean, BeanPostProcessor {
   

    public BeanLifecycle() {
   
        System.out.println("执行 构造方法");
    }

    @Override
    public void setBeanName(String name) {
   
        System.out.println("执行 BeanNameAware 的 setBeanName 方法");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
   
        System.out.println("执行 BeanClassLoaderAware 的 setBeanClassLoader 方法");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
   
        System.out.println("执行 BeanFactoryAware 的 setBeanFactory 方法");
    }

    @PostConstruct
    void postConstruct() {
   
        System.out.println("执行 @PostConstruct 标记的方法");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   
        // System.out.println("执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法");
        return bean;
    }

    @Override
    public void afterPropertiesSet() {
   
        System.out.println("执行 InitializingBean 的 afterPropertiesSet 方法");
    }

    public void customInit() {
   
        System.out.println("执行 自定义的初始化方法 customInit");
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   
        // System.out.println("执行 BeanPostProcessor 的 postProcessAfterInitialization 方法");
        return bean;
    }

    @PreDestroy
    void preDestroy() {
   
        System.out.println("执行 @PreDestroy 标记的方法");
    }

    @Override
    public void destroy() {
   
        System.out.println("执行 DisposableBean 的 destroy 方法");
    }

    public void customDestroy() {
   
        System.out.println("执行 自定义的销毁方法 customDestroy");
    }

}
// 启动服务

执行 构造方法
执行 BeanNameAware 的 setBeanName 方法
执行 BeanClassLoaderAware 的 setBeanClassLoader 方法
执行 BeanFactoryAware 的 setBeanFactory 方法
执行 @PostConstruct 标记的方法
执行 InitializingBean 的 afterPropertiesSet 方法
执行 自定义的初始化方法 customInit
    
// 关闭服务

执行 @PreDestroy 标记的方法
执行 DisposableBean 的 destroy 方法
执行 自定义的销毁方法 customDestroy

相关推荐

  1. Spring IOC

    2024-02-21 18:00:02       40 阅读
  2. spring-ioc

    2024-02-21 18:00:02       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-21 18:00:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-21 18:00:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-21 18:00:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-21 18:00:02       20 阅读

热门阅读

  1. Spring的事务(@Transactional)

    2024-02-21 18:00:02       27 阅读
  2. LeetCode 2656.K个元素的最大和

    2024-02-21 18:00:02       33 阅读
  3. 【蓝桥杯】考试技巧(自用)

    2024-02-21 18:00:02       30 阅读
  4. 算法刷题 DAY54

    2024-02-21 18:00:02       29 阅读
  5. SpringBoot3整合MinIO

    2024-02-21 18:00:02       33 阅读
  6. 粉笔规范词积累(绿色发展) 权利寻租

    2024-02-21 18:00:02       27 阅读
  7. org.apache.catalina.connector.Request 中 getParameterMap

    2024-02-21 18:00:02       26 阅读
  8. goland debug断点失效

    2024-02-21 18:00:02       34 阅读
  9. Go 语言一些常用语法编写和优化指南

    2024-02-21 18:00:02       28 阅读
  10. P1162 填涂颜色

    2024-02-21 18:00:02       30 阅读
  11. Nginx 的基本介绍和使用

    2024-02-21 18:00:02       28 阅读
  12. SpringBoot使用MQTT详解含完整代码(值得珍藏)

    2024-02-21 18:00:02       26 阅读