目录
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定义顺序 | 创建顺序 | 销毁顺序 |
---|---|---|
bean3 | bean3 | bean1 |
bean2 | bean2 | bean2 |
bean1 | bean1 | bean3 |
通过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步骤
- 自定义一个类,实现Condition或ConfigurationCondition接口,实现matches方法
- 在目标对象上使用@Conditional注解,并指定value的指为自定义的Condition类型
- 启动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");
}