业务重构时用事件驱动模式

前言

需求:当新用户注册时,需要给用户发放各种礼品、积分、短信、邀请人奖励等。

常见写法
  1. 直接将上述后续操作堆到注册方法里,搞的注册方法又臭又长;当有其他注册如app注册、小程序注册、第三方注册时,然后将同样的发放逻辑复制的到处都是,可读性、维护性极差

  2. 优化:可以将发放逻辑抽成一个方法,然后供调用,这个应该是最常用的;

    但随着业务的扩张,可能不止普通用户、还有承包商、供应商等。可能都已经是不同的表了,他们也有不同的发放逻辑。

    然后发放逻辑建好几个方法,将之前的逻辑复制过去改一改;最后这个发放逻辑就会变得很臃肿难以维护。

  3. 再优化:可以套用事件驱动模式肢解该逻辑,当然也可以不使用。一切都要从业务出发,业务不复杂就完全没必要使用。因为不管用到什么设计模式都会建好多个类,虽然在单个类里维护性很强,但找起来可能不方便、整体使用还是不如直接调用方便。

    就是说不能为了使用设计模式而使用设计模式

设计模式优势
  1. 一般来说使用设计模式可以提高代码的三性:
    1. 提高代码的可重用性
    2. 提高代码的可读性
    3. 提高代码的可靠性
事件驱动模式处理
  • 事件源:用户
  • 事件:用户注册
  • 事件监听器:监听到用户注册时调用奖励发放功能

步骤

1. 建UserRegisterEvent类继承ApplicationEvent
/**
 * 用户注册事件
 *
 * @author MinWeikai
 * @date 2021-11-01 17:42:33
 */
public class UserRegisterEvent extends ApplicationEvent {

    private User user;

    public UserRegisterEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}
2. 用户注册事件发布
/**
 * 用户注册事件发布
 *
 * @author MinWeikai
 * @date 2021-11-01 17:45:06
 */
@Component
public class UserRegisterEventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publish(User user) {
        this.applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
    }
}
3. 用户注册事件监听
/**
 * 用户注册事件监听
 *
 * @author MinWeikai
 * @date 2021-11-01 17:43:09
 */
@Slf4j
@Component
public class UserRegisterEventListener implements ApplicationListener<UserRegisterEvent> {

    @SneakyThrows
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        User user = userRegisterEvent.getUser();
        log.info("用户:[{}] 进行了注册,开始发放奖励", user.getName());
        Thread.sleep(1000);
        log.info("执行发放奖品");
        Thread.sleep(1000);
        log.info("执行发放积分");
        Thread.sleep(1000);
        log.info("执行发送短信");
        Thread.sleep(1000);
        log.info("新用户奖励发放完成");
    }
}
4. 测试执行
/**
 * @author MinWeikai
 * @date 2021-11-01 17:45:43
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRegisterEventPublisherTest {
    @Autowired
    private UserRegisterEventPublisher userRegisterEventPublisher;

    @Test
    public void publish() throws InterruptedException {
        User user = new User();
        user.setName("君莫笑");
        userRegisterEventPublisher.publish(user);
        Thread.sleep(1000*10);
    }
}
5. 当有其他监听者时,如邀请人奖励发放
/**
 * 用户注册事件邀请人监听
 *
 * @author MinWeikai
 * @date 2021-11-01 17:43:09
 */
@Slf4j
@Component
public class UserRegisterEventInviterListener implements ApplicationListener<UserRegisterEvent> {

    @SneakyThrows
    @Override
    public void onApplicationEvent(UserRegisterEvent userRegisterEvent) {
        User user = userRegisterEvent.getUser();
        log.info("用户:[{}] 进行了注册,开始发放邀请人奖励", user.getName());
        Thread.sleep(1000);
        log.info("邀请人奖励发放完成");
    }
}

监听同一事件对象,业务代码相对解耦

原理

  1. 点这个方法进去applicationEventPublisher.publishEvent

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    		Assert.notNull(event, "Event must not be null");
    
    		// Decorate event as an ApplicationEvent if necessary
    		ApplicationEvent applicationEvent;
    		if (event instanceof ApplicationEvent) {
    			applicationEvent = (ApplicationEvent) event;
    		}
    		else {
    			applicationEvent = new PayloadApplicationEvent<>(this, event);
    			if (eventType == null) {
    				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
    			}
    		}
    
    		// Multicast right now if possible - or lazily once the multicaster is initialized
    		if (this.earlyApplicationEvents != null) {
    			this.earlyApplicationEvents.add(applicationEvent);
    		}
    		else {
                // !! 主要看这里面 !!
    			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    		}
    
    		// Publish event via parent context as well...
    		if (this.parent != null) {
    			if (this.parent instanceof AbstractApplicationContext) {
    				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    			}
    			else {
    				this.parent.publishEvent(event);
    			}
    		}
    	}
    
  2. 获取事件的监听器执行

    @Override
    	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
            // 如果初始化异步任务,就会异步执行每个监听器,否则就是同步执行
    		Executor executor = getTaskExecutor();
            //  此处根据事件对象获取它的所有监听器
    		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    			if (executor != null) {
    				executor.execute(() -> invokeListener(listener, event));
    			}
    			else {
    				invokeListener(listener, event);
    			}
    		}
    	}
    
  3. 执行UserRegisterEventListener实现的接口

    @SuppressWarnings({"rawtypes", "unchecked"})
    	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    		try {
    		// 最后就是这里去执行UserRegisterEventListener实现的接口
    			listener.onApplicationEvent(event);
    		}
    		catch (ClassCastException ex) {
    			String msg = ex.getMessage();
    			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
    				// Possibly a lambda-defined listener which we could not resolve the generic event type for
    				// -> let's suppress the exception and just log a debug message.
    				Log logger = LogFactory.getLog(getClass());
    				if (logger.isTraceEnabled()) {
    					logger.trace("Non-matching event type for listener: " + listener, ex);
    				}
    			}
    			else {
    				throw ex;
    			}
    		}
    	}
    

异步

1. 怎么异步使用监听器

可以自己注入一个SimpleApplicationEventMulticaster类,给里面设置上线程即可

/**
 * 自己注入SimpleApplicationEventMulticaster,设置执行线程
 *
 * @author MinWeikai
 * @date 2021/11/3 10:35
 */
@Configuration
public class MySimpleApplicationEventMulticaster {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setAllowCoreThreadTimeOut(true);
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(20);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        taskExecutor.setThreadFactory(new CustomizableThreadFactory("MySimpleApplicationEventMulticaster-pool-"));
        return taskExecutor;
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskExecutor) {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(taskExecutor);
        return multicaster;
    }

}

总结

优点
  1. 从以上步骤中可以看到事件对象、事件发布、事件监听器代码相对解耦
  2. 当有新的业务时如邀请人事件监听,只需要添加一个事件监听器即可,无需修改原有代码。减少对现有业务的影响,相对扩展性较好
  3. 可以更容易开发和维护不可预知的服务或异步服务
缺点
  1. 同一事件的监听器尽量放在同一目录,有可能会出现同一事件的不同监听器散落在不同的模块中,增加业务逻辑的复杂度

代码

上述代码路径

你可能感兴趣的:(设计模式,java)