【Spring】SpringIOC基础

目录

4 SpringIOC基础

本章我们将具体学习一下Spring基础,包含Spring常见概念及使用。这一章都是浅显的使用,和概念,适合新手,老手可以直接看第5章。

4.1 SpringIOC和DI

Spring中有3个核心的概念:控制反转(Ioc)、依赖注入(DI)和面向切面编程(AOP)。

再来回想一下什么是IOC(小姐姐)。如果没有Spring,我们创建对象都是自己创建的;但是引入了Spring以后,我们可以把创建对象的控制权交给Sring管理,这个过程就叫做控制反转。

DI是Ioc的一种实现方式,通过依赖注入可以让代码更灵活。

AOP面向切面编程,AOP并不是Spring独有的,Spring只是实现了AOP的部分功能而已。

4.2 Spring容器对象

以下介绍spring容器中具有代表性的几个容器。

4.2.1 BeanFactory接口

org.springframework.beans.factory.BeanFactory

该接口是生产bean的顶级接口

4.2.2 ApplicationContext接口

org.springframework.context.ApplicationContext

该接口继承了ListableBeanFactory,而ListableBeanFactory继承了BeanFactory。

4.3 xml中bean定义详解

4.3.1 配置文件格式

<?xml version="1.0" encoding="UTF-8"?>
<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-4.3.xsd">

    <import resource="引入其他的xml配置文件" />
    <bean id="bean标识" class="玩转类型名称"/>
    <alias name="bean标识" alias="别名" />
</beans>

4.3.2 bean元素

<bean 
id="bean唯一标识" 
name="bean名称" 
class="完整类型名称" 
factory-bean="工厂bean名称" 
factory-method="工厂方法"/>

4.3.3 alias元素

<alias name="需要定义别名的bean" alias="别名" />

4.3.4 import元素

当我们的系统比较大的时候,会分成很多模块,每个模块会对应一个bean xml文件,我们可以在一个总的bean xml中对其他bean xml进行汇总,相当于把多个bean xml的内容合并到一个里面了,可以通过import元素引入其他bean配置文件。

<import resource="其他配置文件的位置" />

4.4 xml中如何创建Bean实例

4.4.1 通过反射调用构造方法创建bean对象

<bean id="bean名称" name="bean名称或者别名" class="bean的完整类型名称">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

4.4.2 通过静态工厂方法创建bean对象

<bean id="bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

4.4.3 过实例工厂方法创建bean对象

<bean id="bean名称" factory-bean="需要调用的实例对象bean名称" factory-method="bean对象中的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名称" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名称" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名称" />
</bean>

4.4.4 通过FactoryBean创建bean对象

<bean id="bean名称" class="FactoryBean接口实现类" />
public interface FactoryBean<T> {

   String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

   @Nullable
   T getObject() throws Exception;

   @Nullable
   Class<?> getObjectType();

   default boolean isSingleton() {
      return true;
   }

}

4.5 Bean作用域详解

<bean id="" class="" scope="作用域" />

4.5.1 singleton

当scope的值设置为singleton的时候,整个spring容器中只会存在一个bean实例,也就是我们常说的单例。

4.5.2 prototype

如果scope被设置为prototype类型的了,表示这个bean是多例的,通过容器每次获取的bean都是不同的实例,每次获取都会重新创建一个bean实例对象,这就是多例。

4.5.3 request

SpringWeb独有作用域。

当一个bean的作用域为request,表示在一次http请求中,一个bean对应一个实例;对每个http请求都会创建一个bean实例,request结束的时候,这个bean也就结束了。

4.5.4 session

SpringWeb独有作用域。

这个和request类似,也是用在web环境中,session级别共享的bean,每个会话会对应一个bean实例,不同的session对应不同的bean实例。

4.5.5 application

SpringWeb独有作用域。

全局web应用级别的作用于,也是在web环境中使用的,一个web应用程序对应一个bean实例,通常情况下和singleton效果类似的,不过也有不一样的地方,singleton是每个spring容器中只有一个bean实例,一般我们的程序只有一个spring容器,但是,一个应用程序中可以创建多个spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的时候,不管应用中有多少个spring容器,这个应用中同名的bean只有一个。

4.5.6 自定义scope

第1步:实现Scope接口

第2步:将自定义的scope注册到容器

org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope

第3步:使用自定义的作用域

4.6 依赖注入

4.6.1 手动注入

4.6.1.1 通过构造器注入
4.6.1.1.1 根据构造器参数索引注入
<constructor-arg index="0" value="参数值"/>
4.6.1.1.2 根据构造器参数类型注入
<constructor-arg type="参数类型" value="参数值"/>
4.6.1.1.3 根据构造器参数名称注入
<constructor-arg name="参数类型" value="参数值"/>
4.6.1.2 setter注入
<bean id="" class="">
    <property name="属性名称" value="属性值" />
    ...
    <property name="属性名称" value="属性值" />
</bean>
4.6.1.3 注入容器的bean
4.6.1.3.1 ref属性方式
<constructor-arg ref="需要注入的bean的名称"/>
<property name="属性名称" ref="需要注入的bean的名称" />
4.6.1.3.2 内置bean的方式
<constructor-arg>
    <bean class=""/>
</constructor-arg>
<property name="属性名称">
    <bean class=""/>
</property>
4.6.1.4 其他类型注入
<list>
    <value>Spring</value>
    或
    <ref bean="bean名称"/>
    或
    <list></list>
    或
    <bean></bean>
    或
    <array></array>
    或
    <map></map>
</list>
<set>
    <value>Spring</value>
    或
    <ref bean="bean名称"/>
    或
    <list></list>
    或
    <bean></bean>
    或
    <array></array>
    或
    <map></map>
</set>
<map>
    <entry key="key" value="30" key-ref="key引用的bean名称" value-ref="value引用的bean名称"/>
    或
    <entry>
        <key>
            value对应的值,可以为任意类型
        </key>
        <value>
            value对应的值,可以为任意类型
        </value>
    </entry>
</map>
<array>
    数组中的元素
</array>

4.6.2 自动注入

自动注入是采用约定大约配置的方式来实现的,程序和spring容器之间约定好,遵守某一种都认同的规则,来实现自动注入。

<bean id="" class="" autowire="byType|byName|constructor|default" />
4.6.2.1 按照名称进行注入

spring容器会按照set属性的名称去容器中查找同名的bean对象,然后将查找到的对象通过set方法注入到对应的bean中,未找到对应名称的bean对象则set方法不进行注入

需要注入的set属性的名称和被注入的bean的名称必须一致。

4.6.2.2 按类型进行注入

spring容器会遍历x类中所有的set方法,会在容器中查找和set参数类型相同的bean对象,将其通过set方法进行注入,未找到对应类型的bean对象则set方法不进行注入。

需要注入的set属性的类型和被注入的bean的类型需要满足isAssignableFrom关系。

按照类型自动装配的时候,如果按照类型找到了多个符合条件的bean,系统会报错。

4.6.2.3 按照构造方法进行注入

spring会找到x类中所有的构造方法(一个类可能有多个构造方法),然后将这些构造方法进行排序(先按修饰符进行排序,public的在前面,其他的在后面,如果修饰符一样的,会按照构造函数参数数量倒叙,也就是采用贪婪的模式进行匹配,spring容器会尽量多注入一些需要的对象)得到一个构造函数列表,会轮询这个构造器列表,判断当前构造器所有参数是否在容器中都可以找到匹配的bean对象,如果可以找到就使用这个构造器进行注入,如果不能找到,那么就会跳过这个构造器,继续采用同样的方式匹配下一个构造器,直到找到一个合适的为止。

4.6.2.4 默认注入方式

4.7 bean的创建和销毁顺序

bean定义顺序创建顺序销毁顺序
bean3bean3bean1
bean2bean2bean2
bean1bean1bean3

通过depend-on干预bean创建和销毁顺序

4.8 解决多个Bean存在的问题

4.8.1 primary

指定候选者bean

<bean id="" class="" primary="true"/>

4.8.2 autowire-candidate

设置当前bean在被其他对象作为自动注入对象的时候,是否作为候选bean,默认值是true。

4.9 Bean初始化

4.9.1 实时初始化

默认实时初始化,即在容器启动过程中被创建

4.9.2 延迟初始化

<bean lazy-init="是否是延迟初始化" />

4.10 使用继承简化bean配置

第一步:

<bean id="baseService" abstract="true">
</bean>

第二步:

<bean id="" class="" parent="baseService"/>

4.12 Spring注解解析

4.12.1 @Configuration

@Configuration这个注解可以加在类上,让这个类的功能等同于一个bean xml配置文件,通过AnnotationConfigApplicationContext容器来加@Configuration注解修饰的类。

4.12.1 @Bean

@Configuration在类上使用@Configuration注解,通过AnnotationConfigApplicationContext容器来加@Configuration注解修饰的类。

被@Configuration修饰的类,spring容器中会通过cglib给这个类创建一个代理,代理会拦截所有被@Bean修饰的方法,默认情况(bean为单例)下确保这些方法只被调用一次,从而确保这些bean是同一个bean,即单例的。

4.12.1 @ComponentScan

@ComponentScan用于批量注册bean。

这个注解会让spring去扫描某些包及其子包中所有的类,然后将满足一定条件的类作为bean注册到spring容器容器中。

4.12.1 @Component、@Repository、@Service、@Controller

@Component(组件)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}

从定义中可以看出,这个注解可以用在任何类型上面。
通常情况下将这个注解用在类上面,标注这个类为一个组件,默认情况下,被扫描的时候会被作为bean注册到容器中。

@Repository(仓储)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Repository上面有@Component注解。
value参数上面有@AliasFor(annotation = Component.class),设置value参数的时候,也相当于给@Component注解中的value设置值 。

其他两个注解@Service、@Controller源码和@Repository源码类似。

这4个注解本质上是没有任何差别,都可以用在类上面,表示这个类被spring容器扫描的时候,可以作为一个bean组件注册到spring容器中。

4.12.1 @Import

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

@Import可以用来批量导入任何普通的组件、配置类,将这些类中定义的所有bean注册到容器中

4.12.1 @Conditional

@Conditional注解是从spring4.0才有的,可以用在任何类型或者方法上面,通过@Conditional注解可以配置一些条件判断,当所有条件都满足的时候,被@Conditional标注的目标才会被spring容器处理。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}
@FunctionalInterface
public interface Condition {
    /**
     * 判断条件是否匹配
     * context:条件判断上下文
     */
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
public interface ConditionContext {

    /**
     * 返回bean定义注册器,可以通过注册器获取bean定义的各种配置信息
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * 返回ConfigurableListableBeanFactory类型的bean工厂,相当于一个ioc容器对象
     */
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 返回当前spring容器的环境配置信息对象
     */
    Environment getEnvironment();

    /**
     * 返回资源加载器
     */
    ResourceLoader getResourceLoader();

    /**
     * 返回类加载器
     */
    @Nullable
    ClassLoader getClassLoader();

}

@Conditional使用的3步骤

  1. 自定义一个类,实现Condition或ConfigurationCondition接口,实现matches方法
  2. 在目标对象上使用@Conditional注解,并指定value的指为自定义的Condition类型
  3. 启动spring容器加载资源,此时@Conditional就会起作用了

4.12.1 @ConfigurationCondition

public interface ConfigurationCondition extends Condition {

    /**
     * 条件判断的阶段,是在解析配置类的时候过滤还是在创建bean的时候过滤
     */
    ConfigurationPhase getConfigurationPhase();





    /**
     * 表示阶段的枚举:2个值
     */
    enum ConfigurationPhase {

        /**
         * 配置类解析阶段,如果条件为false,配置类将不会被解析
         */
        PARSE_CONFIGURATION,

        /**
         * bean注册阶段,如果为false,bean将不会被注册
         */
        REGISTER_BEAN
    }

}

ConfigurationCondition接口相对于Condition接口多了一个getConfigurationPhase方法,用来指定条件判断的阶段,是在解析配置类的时候过滤还是在创建bean的时候过滤。

4.12.1 @Autowired、@Resource、@Primary、@Qulifier

@Autowired查找候选者可以简化为下面这样

按类型找->通过限定符@Qualifier过滤->@Primary->@Priority->根据名称找(字段名称或者方法名称)

概括为:先按类型找,然后按名称找
@Resource查找候选者可以简化为

先按Resource的name值作为bean名称找->按名称(字段名称、方法名称、set属性名称)找->按类型找->通过限定符@Qualifier过滤->@Primary->@Priority->根据名称找(字段名称或者方法参数名称)

概括为:先按名称找,然后按类型找
@Qulifier

可以在依赖注入查找候选者的过程中对候选者进行过滤。

@Primary

设置为主要候选者

4.12.1 @Scope、@DependsOn、@ImportResource、@Lazy

@Scope:指定bean的作用域

@DependsOn:指定当前bean依赖的bean

@ImportResource:配置类中导入bean定义的配置文件

@Lazy:延迟初始化

4.12.1 @Value

动态@Value实现的关键是@Scope中proxyMode参数,值为ScopedProxyMode.DEFAULT,会生成一个代理,通过这个代理来实现@Value动态刷新的效果,这个地方是关键。

以下是具体代码的实现

public class BeanRefreshScope implements Scope {

    public static final String SCOPE_REFRESH = "refresh";

    private static final BeanRefreshScope INSTANCE = new BeanRefreshScope();

    //来个map用来缓存bean
    private ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>(); //@1

    private BeanRefreshScope() {
    }

    public static BeanRefreshScope getInstance() {
        return INSTANCE;
    }

    /**
     * 清理当前
     */
    public static void clean() {
        INSTANCE.beanMap.clear();
    }

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object bean = beanMap.get(name);
        if (bean == null) {
            bean = objectFactory.getObject();
            beanMap.put(name, bean);
        }
        return bean;
    }

    @Nullable
    @Override
    public Object remove(String name) {
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Nullable
    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Nullable
    @Override
    public String getConversationId() {
        return null;
    }

}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope(BeanRefreshScope.SCOPE_REFRESH)
@Documented
public @interface RefreshScope {
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
@Component
@RefreshScope
public class MailConfig {

    @Value("${mail.username}")
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "MailConfig{" +
                "username='" + username + '\'' +
                '}';
    }
}
@Component
public class MailService {
    @Autowired
    private MailConfig mailConfig;

    @Override
    public String toString() {
        return "MailService{" +
                "mailConfig=" + mailConfig +
                '}';
    }
}
public class RefreshConfigUtil {
    /**
     * 模拟改变数据库中配置信息
     */
    public static void updateDbConfig(AbstractApplicationContext context) {
        //更新context中的mailPropertySource配置信息
        refreshMailPropertySource(context);

        //清空BeanRefreshScope中所有bean的缓存
        BeanRefreshScope.getInstance().clean();
    }

    public static void refreshMailPropertySource(AbstractApplicationContext context) {
        Map<String, Object> mailInfoFromDb = DbUtil.getMailInfoFromDb();
        //将其丢在MapPropertySource中(MapPropertySource类是spring提供的一个类,是PropertySource的子类)
        MapPropertySource mailPropertySource = new MapPropertySource("mail", mailInfoFromDb);
        context.getEnvironment().getPropertySources().addFirst(mailPropertySource);
    }

}
@Test
public void test4() throws InterruptedException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.getBeanFactory().registerScope(BeanRefreshScope.SCOPE_REFRESH, BeanRefreshScope.getInstance());
    context.register(MainConfig4.class);
    //刷新mail的配置到Environment
    RefreshConfigUtil.refreshMailPropertySource(context);
    context.refresh();

    MailService mailService = context.getBean(MailService.class);
    System.out.println("配置未更新的情况下,输出3次");
    for (int i = 0; i < 3; i++) {
        System.out.println(mailService);
        TimeUnit.MILLISECONDS.sleep(200);
    }

    System.out.println("模拟3次更新配置效果");
    for (int i = 0; i < 3; i++) {
        RefreshConfigUtil.updateDbConfig(context);
        System.out.println(mailService);
        TimeUnit.MILLISECONDS.sleep(200);
    }
}

4.13 Bean 生命周期详解

4.13.1 Spring bean生命周期13个环节

阶段1:Bean元信息配置阶段

阶段2:Bean元信息解析阶段

阶段3:将Bean注册到容器中

阶段4:BeanDefinition合并阶段

阶段5:Bean Class加载阶段

阶段6:Bean实例化阶段(2个小阶段)

* Bean实例化前阶段
* Bean实例化阶段

阶段7:合并后的BeanDefinition处理
阶段8:属性赋值阶段(3个小阶段)

* Bean实例化后阶段
* Bean属性赋值前阶段
* Bean属性赋值阶段

阶段9:Bean初始化阶段(5个小阶段)

* Bean Aware接口回调阶段
* Bean初始化前阶段
* Bean初始化阶段
* Bean初始化后阶段
* Bean初始化完成操作

阶段10:所有单例bean初始化完成后阶段
阶段11:Bean的使用阶段

阶段12:Bean销毁前阶段

阶段13:Bean销毁阶段

4.13.2 代码入口

SpringApplication
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 准备此上下文以进行刷新.
      prepareRefresh();
      // 告诉子类刷新内部bean工厂.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 允许在上下文子类中对 bean 工厂进行后处理.
      prepareBeanFactory(beanFactory);
      try {
         // 允许在上下文子类中对 bean 工厂进行后处理.
         postProcessBeanFactory(beanFactory);
         // 调用在上下文中注册为 bean 的工厂处理器.
         invokeBeanFactoryPostProcessors(beanFactory);
         // 注册拦截 bean 创建的 bean 处理器.
         registerBeanPostProcessors(beanFactory);
         // 初始化此上下文的消息源.
         initMessageSource();
         // 为此上下文初始化事件多播器.
         initApplicationEventMulticaster();
         // 初始化特定上下文子类中的其他特殊bean.
         onRefresh();
         // 检查侦听器 bean 并注册它们.
         registerListeners();
         // 实例化所有剩余的(非延迟初始化)单例.
         finishBeanFactoryInitialization(beanFactory);
         // 最后一步:发布对应的事件.
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // 销毁已经创建的单例以避免悬空资源.
         destroyBeans();
         // 重置“活动”标志.
         cancelRefresh(ex);
         // 将异常传播给调用者.
         throw ex;
      }
      finally {
         // 重置 Spring 核心中的常见内省缓存,因为我们可能不再需要单例 bean 的元数据......
         resetCommonCaches();
      }
   }
}

4.13.3 Spring bean生命周期流程图

在这里插入图片描述

4.14 spring 事件机制详解

其实说白了就是广播/订阅发布者模式

4.14.1 事件模式中的几个概念

事件源:事件的触发者。

事件:描述发生了什么事情的对象。

事件监听器:监听到事件发生的时候,做一些处理。

4.14.2 同步监听

@ComponentScan
public class MainConfig4 {
}
/**
 * 用户注册事件
 */
public class UserRegisterEvent extends ApplicationEvent {
    //用户名
    private String userName;

    public UserRegisterEvent(Object source, String userName) {
        super(source);
        this.userName = userName;
    }

    public String getUserName() {
        return userName;
    }
}
/**
 * 用户注册监听器
 */
@Component
public class UserRegisterListener {
    @EventListener(classes = {UserRegisterEvent.class})
    @Order(1)
    public void sendMail(UserRegisterEvent event) {
        System.out.println(String.format("【%s】,给用户【%s】发送注册成功邮件!", Thread.currentThread(), event.getUserName()));
    }

    @EventListener(classes = {UserRegisterEvent.class})
    @Order(0)
    public void sendCompon(UserRegisterEvent event) {
        System.out.println(String.format("【%s】,给用户【%s】发送优惠券!", Thread.currentThread(), event.getUserName()));
    }
}
/**
 * 用户注册服务
 */
@Component
public class UserRegisterService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    public void registerUser(String userName) {
        //用户注册(将用户信息入库等操作)
        System.out.println(String.format("【%s】,用户【%s】注册成功", Thread.currentThread(), userName));
        //发布注册成功事件
        this.applicationEventPublisher.publishEvent(new UserRegisterEvent(this, userName));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}
public void test4() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig4.class);
    context.refresh();
    //获取用户注册服务
    UserRegisterService userRegisterService =
            context.getBean(UserRegisterService.class);
    //模拟用户注册
    userRegisterService.registerUser("Spring");
}

4.14.3 异步监听

@ComponentScan
@Configuration
public class MainConfig5 {
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        //创建一个事件广播器
        SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster();
        //给广播器提供一个线程池,通过这个线程池来调用事件监听器
        Executor executor = this.applicationEventMulticasterThreadPool().getObject();
        //设置异步执行器
        result.setTaskExecutor(executor);//@1
        return result;
    }

    @Bean
    public ThreadPoolExecutorFactoryBean applicationEventMulticasterThreadPool() {
        ThreadPoolExecutorFactoryBean result = new ThreadPoolExecutorFactoryBean();
        result.setThreadNamePrefix("applicationEventMulticasterThreadPool-");
        result.setCorePoolSize(5);
        return result;
    }
}
/**
 * 给用户各种优惠券
 */
@Component
public class SendCouponListener {
    @EventListener
    public void onApplicationEvent(UserRegisterEvent event) throws InterruptedException {
        System.out.println(
                String.format("当前线程【%s】,给用户【%s】发放一些优惠券!", Thread.currentThread(),
                        event.getUserName()));
    }
}
/**
 * 给用户发送邮件
 */
@Component
public class SendMailListener {
    @EventListener
    public void onApplicationEvent(UserRegisterEvent event) {
        System.out.println(
                String.format("当前线程【%s】,给用户【%s】发送注册成功邮件!", Thread.currentThread(),
                        event.getUserName()));
    }
}
/**
 * 用户注册事件
 */
public class UserRegisterEvent extends ApplicationEvent {
    //用户名
    private String userName;

    public UserRegisterEvent(Object source, String userName) {
        super(source);
        this.userName = userName;
    }

    public String getUserName() {
        return userName;
    }
}
/**
 * 用户注册服务
 */
@Component
public class UserRegisterService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    public void registerUser(String userName) {
        //用户注册(将用户信息入库等操作)
        System.out.println(String.format("当前线程【%s】,用户【%s】注册成功", Thread.currentThread(), userName));
        //发布注册成功事件
        this.applicationEventPublisher.publishEvent(new UserRegisterEvent(this, userName));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}
public void test5() throws InterruptedException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig5.class);
    context.refresh();
    //获取用户注册服务
    UserRegisterService userRegisterService =
            context.getBean(UserRegisterService.class);
    //模拟用户注册
    userRegisterService.registerUser("Spring");
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第七人格

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值