最近在重构公司代码,考虑使用事件式驱动编程将增值代码逻辑都提取出来,但是看了一些其他人写的代码,总感觉不符合我要求。正好最近在研究spring和mybatis源码,于是参考mybatis源码自己实现一套吧。
目标
既然是有实现目标的,那先从制定期望实现的效果开始吧
(1)既然是事件机制,那总有个事件触发器吧。触发器就应该简单明了,一个接口就行了,方法参数可以随便定
public interface OrderFinishTrigger {
void process(String orderId, String empId);
}
(2)既然有触发器,那肯定有监听器,监听器便是业务代码实现的地方。1.通过implements触发器接口来和触发器进行绑定,这样简单明了,2.一个触发器当然可以有多个监听器,3.因为是基于spring的,且监听器是实现业务用的,所以正常的依赖注入功能应该还是得够支持的。
//订单完成时给服务人员发工资
@EventListener
public class TestOrderFinishDealGiveMoneyListener implements OrderFinishTrigger {
@Override
public void process(String orderId, String empId) {
System.out.println("订单完成发工钱啦:orderId:"+orderId+";empId:"+empId);
}
}
//订单完成时,发送短信
@EventListener
public class TestOrderFinishDealSendSMSListener implements OrderFinishTrigger {
@Autowired()
private TestService testService;
@Override
public void process(String orderId, String empId) {
System.out.println("订单完成发短信啦"+orderId+";empId:"+empId);
testService.showMe(orderId);
}
}
//其他的普通业务类
@Service
public class TestService {
public void showMe(String param){
System.out.println("chuancan:"+param);
}
}
(3)既然是事件机制,那总有个地方可以主动触发事件吧,就取名为事件提供者吧。为了简单起见,事件提供者只需要是一个接口,类似于mybatis的mapper一样,只需要指定触发器类和触发器方法即可。因此,事件提供者便需要采用动态代理来实现
@EventProvider
public interface TestEventProvider {
//调用这个方法可以触发订单完成事件
@EventTrigger(triggerClass = OrderFinishTrigger.class,method = "process")
void orderFinished(String orderId, String empId);
}
先把结果放出来吧
测试类
只需要简单调用TestEventProvider接口的方法,就可以触发事件
@RunWith(SpringRunner.class)
@SpringBootTest
public class EventApplicationTests {
@Autowired
TestEventProvider testEventProvider;
@Test
public void eventTest() {
/*
balabala各种业务代码
*/
//触发一下订单完成事件
testEventProvider.orderFinished("aaaa","bbbb");
}
}
执行结果:
开始实现吧
几个主要注解
(1)EventProvider,只是用来标明是事件提供者,所以不需要添加其他东西
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
public @interface EventProvider {
}
(2)EventTrigger的话则需要指明触发器类和方法
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface EventTrigger {
Class<?> triggerClass();
String method();
}
(3)监听器则只需要指明监听方式即可
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
public @interface EventListener {
ListenerTriggeriOpportunityEnum type() default ListenerTriggeriOpportunityEnum.SYNCHRONIZE; // 默认同步触发
}
枚举
public enum ListenerTriggeriOpportunityEnum {
SYNCHRONIZE, //同步
ASYNCHRONOUS; //异步
}
事件类的扫描和注入spring
因为我们加的这几个注解都没有声明@Component,所以spring自然不会为我们初始化这些类。因此事件提供者和监听者的扫描以及注入spring的工作就要我们亲自动手了。
首先定义一个扫描工具类,继承自ClassPathScanningCandidateComponentProvider,这个类是spring内部扫描用的工具类,如果不了解的话自行百度。
public class EventBeanScanner extends ClassPathScanningCandidateComponentProvider {
public EventBeanScanner(){
super(false);
addIncludeFilter(new AnnotationTypeFilter(EventProvider.class));
addIncludeFilter(new AnnotationTypeFilter(EventListener.class));
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return true;//返回true则listener和provider都能扫描进来了
}
}
接下来就得考虑EventProvider作为一个接口,应该注入什么对象到spring容器中呢。答案很明确了,动态代理呗。不清楚动态代理的小伙伴先去了解下jdk的动态代理是什么鬼吧,这里就不展开讲了 https://blog.csdn.net/bestkilly/article/details/82141802
贴上代理类代码。其中manager在下文讲,是本项目的关键类,在代理类中通过使用manager的execute方法执行所有监听器。invoke方法是真正执行的方法,需要排除Object中定义的方法。
public class EventProviderProxy<T> implements InvocationHandler {
private EventInitializationManager providerMethod;
public EventProviderProxy(EventInitializationManager manager) {
providerMethod = manager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())){
return method.invoke(this,args);
}else if (isDefaultMethod(method)){
return invokeDefaultMethod(proxy,method,args);
}
}catch (Throwable throwable){
throwable.printStackTrace();
}
EventTrigger eventTrigger = method.getAnnotation(EventTrigger.class);
if (eventTrigger == null)
return null;
//使用代理方法执行
providerMethod.execute(eventTrigger.triggerClass(),method,args);
return null;
}
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
}
接下来是代理工厂,用来创建返回代理对象
public class EventProxyFactory<T> {
private final Class<T> providerInterface;
public EventProxyFactory(Class<T> providerInterface) {
this.providerInterface = providerInterface;
}
//获得动态代理对象
@SuppressWarnings("unchecked")
public T newInstance(EventInitializationManager manager){
return (T) Proxy.newProxyInstance(providerInterface.getClassLoader(),new Class[]{providerInterface},new EventProviderProxy<T>(manager));
}
public Class<T> getProviderInterface() {
return providerInterface;
}
}
关键是在什么时候进行扫描和注入呢?看过ioc源码的小伙伴应该知道,BeanFactoryPostProcessor会在单例bean初始化前调用,在这个时候我们可以直接注入bean或者bean的定义信息到spring容器中。
下面是EventInitializationManager的代码,ApplicationListener<ContextRefreshedEvent>的作用过会儿再讲,现在先讲BeanFactoryPostProcessor和ResourceLoaderAware
ResourceLoaderAware就不详细说了,只是用来拿到ResourceLoader协助扫描
BeanFactoryPostProcessor需要实现postProcessBeanFactory(),在这里可以拿到beanFactory,利用这个就可以往spring容器中塞入bean或者bean的定义信息。通过循环需要扫描的包packages(这个属性是什么时候赋值的过会儿讲),扫描出所有的EventListener和EventProvider,其中EventProvider直接注入对象,而EventListener注入bean的定义信息(因为可能需要注入业务代码,因此初始化工作交给spring来完成)
/**
* @author yhxst
* @date 2019-02-22
* 事件的管理类,职责为在spring初始化单实例bean之前
* 主要职责为
* 1.把provider动态代理对象和listener对象扫描出来并注册到spring容器中
* 2.在容器初始化完成后,构建listener,trigger的映射关系
* 3.提供触发事件的统一方法execute()
*/
public class EventInitializationManager implements ApplicationListener<ContextRefreshedEvent>, BeanFactoryPostProcessor, ResourceLoaderAware {
private ApplicationContext applicationContext;
//保存格式为:trigger接口类-->{trigger触发执行的method-->[listener对象]}
private Map<Class<?>, Map<Method,Set<Object>>> PROVIDER_LISTENER_SYNC_MAP = new HashMap<>(); //同步执行队列
private Map<Class<?>, Map<Method,Set<Object>>> PROVIDER_LISTENER_ASYNC_MAP = new HashMap<>(); //异步执行队列
private ResourceLoader resourceLoader;
// @EventProvider接口类所在的包
private String[] packages = {};
//保存所有事件提供者类
private List<Class<?>> eventProviders = new ArrayList<>();
//保存所有监听者类
private List<Class<?>> eventListeners = new ArrayList<>();
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Set<BeanDefinition> beans = scanInterfaceDef();
DefaultListableBeanFactory bf = (DefaultListableBeanFactory)beanFactory;
registerProxyBean(beans, bf);
}
/**
* 将扫描到的provider和listener注册到spring容器中
* @param beans
* @param beanFactory
*/
private void registerProxyBean(Set<BeanDefinition> beans, DefaultListableBeanFactory beanFactory){
for (BeanDefinition bd : beans){
Class<?> clazz;
try {
clazz = Class.forName(bd.getBeanClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
continue;
}
if (clazz.isAnnotationPresent(EventProvider.class)){
eventProviders.add(clazz);
String beanName = getBeanName(clazz);
//直接实例化并注册上provider的FactoryBean
EventProviderFactoryBean obj = new EventProviderFactoryBean<>(clazz,this);
beanFactory.registerSingleton(beanName,obj );
}else if (clazz.isAnnotationPresent(EventListener.class)){
eventListeners.add(clazz);
//将listener的信息注册到spring容器中,初始化交给spring来完成
BeanDefinition listenerBeanefinition = new RootBeanDefinition();
listenerBeanefinition.setBeanClassName(clazz.getName());
beanFactory.registerBeanDefinition(getBeanName(clazz),listenerBeanefinition);
}
//有可能是被@Component修饰的其他接口或类
}
}
private String getBeanName(Class<?> clazz) {
String name = clazz.getSimpleName();
//按首字母小写的形式生成bean名
name = name.substring(0,1).toLowerCase() + name.substring(1);
return name;
}
/**
* 扫描各个包下的provider和listener
* @return
*/
private Set<BeanDefinition> scanInterfaceDef() {
ClassPathScanningCandidateComponentProvider componentProvider = new EventBeanScanner();
componentProvider.setResourceLoader(resourceLoader);
Set<BeanDefinition> beans = new LinkedHashSet<>();
for (String pkg:packages){
beans.addAll(componentProvider.findCandidateComponents(pkg));
}
return beans;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* 监听容器初始化完毕时间,开始映射监听
* @param event
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
this.applicationContext = event.getApplicationContext();
System.out.println("读取到所有的事件提供者::"+ eventProviders);
if (eventProviders == null || eventProviders.isEmpty())
return;
for (Class<?> providerClz : eventProviders){
initializationProvider(providerClz);
}
}
private void initializationProvider(Class<?> providerClass) {
Method[] methods = providerClass.getMethods();
for (Method method:methods){
if (!method.isAnnotationPresent(EventTrigger.class))
continue;
List<Object> asynList = new ArrayList<>();
List<Object> synList = new ArrayList<>();
//获得对应的触发器类
EventTrigger eventTrigger = method.getAnnotation(EventTrigger.class);
Class<?> triggerClass = eventTrigger.triggerClass();
String methodName = eventTrigger.method();
//通过反射获取监听器的方法
Method triggerMethod;
try {
triggerMethod = triggerClass.getMethod(methodName,method.getParameterTypes());
} catch (NoSuchMethodException e) {
throw new RuntimeException("没有找到对应的方法,trigger:["+triggerClass+"],method:["+methodName+"],param:["+method.getParameterTypes()+"]");
}
System.out.println(eventListeners);
for (Class<?> listenerClz : eventListeners){
Object listener = applicationContext.getBean(listenerClz);
Class<?> listenerClass = listener.getClass();
if (!isImplOf(listenerClass,triggerClass)){
continue;
}
//根据监听器注解配置的执行方法,放入相应同步异步的map
EventListener eventListener = listenerClass.getAnnotation(EventListener.class);
if (ListenerTriggeriOpportunityEnum.ASYNCHRONOUS.equals(eventListener.type())){
asynList.add(listener);
}else {
synList.add(listener);
}
}
addListenerToMap(triggerClass,triggerMethod,asynList,synList);
}
}
/**
* 将所有的映射关系都交给Manager管理
* @param trigger
* @param asynList
* @param synList
*/
private void addListenerToMap(Class<?> trigger, Method triggerMethod, List<Object> asynList, List<Object> synList) {
Map<Method,Set<Object>> asynListenerMap = PROVIDER_LISTENER_ASYNC_MAP.get(trigger);
Map<Method,Set<Object>> synListenerMap = PROVIDER_LISTENER_SYNC_MAP.get(trigger);
if (asynListenerMap == null){
asynListenerMap = new HashMap<>();
PROVIDER_LISTENER_ASYNC_MAP.put(trigger,asynListenerMap);
}
if (synListenerMap == null){
synListenerMap = new HashMap<>();
PROVIDER_LISTENER_SYNC_MAP.put(trigger,synListenerMap);
}
Set<Object> asynListeners = asynListenerMap.get(triggerMethod);
Set<Object> synListeners = synListenerMap.get(triggerMethod);
if (asynListeners == null){
asynListeners = new HashSet<>();
asynListenerMap.put(triggerMethod,asynListeners);
}
if (synListeners == null){
synListeners = new HashSet<>();
synListenerMap.put(triggerMethod,synListeners);
}
asynListeners.addAll(asynList);
synListeners.addAll(synList);
}
/**
* provider 统一触发的方法
* @param triggerClass
* @param param
*/
public void execute(Class<?> triggerClass,Method providerTriggerMethod, Object... param) throws NoSuchMethodException {
// 注意,异步执行目前是直接new Thread方式的,请用线程池或其他异步通知方式实现后再使用异步触发
String methodName = providerTriggerMethod.getAnnotation(EventTrigger.class).method();
Method method = triggerClass.getMethod(methodName, providerTriggerMethod.getParameterTypes());
Map<Method,Set<Object>> asynListeners = PROVIDER_LISTENER_ASYNC_MAP.get(triggerClass);
if (asynListeners != null && !asynListeners.isEmpty()){
Set<Object> asynListenerList = asynListeners.get(method);
new Thread(new Runnable() {
@Override
public void run() {
runListener(asynListenerList,method,param);
}
}).start();
}
Map<Method,Set<Object>> syncListeners = PROVIDER_LISTENER_SYNC_MAP.get(triggerClass);
if (syncListeners != null && !syncListeners.isEmpty()){
Set<Object> syncListenerList = syncListeners.get(method);
runListener(syncListenerList,method,param);
}
}
private void runListener(Set<Object> listeners,Method method,Object... param){
if (listeners == null)
return;
for (Object listener : listeners){
try {
method.invoke(listener,param);
}catch (Exception e){
e.printStackTrace();
}
}
}
private boolean isImplOf(Class<?> listenerClass, Class<?> triggerClass) {
Class<?>[] classes = listenerClass.getInterfaces();
for (Class<?> c:classes){
if (c.equals(triggerClass))
return true;
}
return false;
}
/**
* 创建Manager对象时赋值packages用
* @param packages
*/
public void setPackages(String[] packages) {
this.packages = packages;
}
}
EventProviderFactoryBean
public class EventProviderFactoryBean<T> implements FactoryBean<T> {
private Class<T> clz;
private EventInitializationManager manager;
public EventProviderFactoryBean(Class<T> clz, EventInitializationManager manager) {
this.clz = clz;
this.manager = manager;
}
@Override
public T getObject() throws Exception {
//使用代理工厂获取对象
return (T) new EventProxyFactory<T>(clz).newInstance(manager);
}
@Override
public Class<?> getObjectType() {
return clz;
}
@Override
public boolean isSingleton() {
return true;
}
}
触发器和订阅者的绑定
到这里,事件类的扫描和注入工作已经完成了,接下来只需要把触发器和订阅者的映射关系绑定上就ok了。
储存的数据结构
//保存格式为:trigger接口类-->{trigger触发执行的method-->[listener对象]}
private Map<Class<?>, Map<Method,Set<Object>>> PROVIDER_LISTENER_SYNC_MAP = new HashMap<>(); //同步执行队列
private Map<Class<?>, Map<Method,Set<Object>>> PROVIDER_LISTENER_ASYNC_MAP = new HashMap<>(); //异步执行队列
触发事件的时候进行调用
根据方法名和参数获得相应的Method,从上面的map拿到需要执行的Listener,循环执行就可以了,目前异步方法简单的使用new Thread()来实现,如果要在项目里用的话,请务必使用线程池或者其他异步通知方法改写后再用
public void execute(Class<?> triggerClass,Method providerTriggerMethod, Object... param) throws NoSuchMethodException {
String methodName = providerTriggerMethod.getAnnotation(EventTrigger.class).method();
Method method = triggerClass.getMethod(methodName, providerTriggerMethod.getParameterTypes());
Map<Method,Set<Object>> asynListeners = PROVIDER_LISTENER_ASYNC_MAP.get(triggerClass);
if (asynListeners != null && !asynListeners.isEmpty()){
Set<Object> asynListenerList = asynListeners.get(method);
new Thread(new Runnable() {
@Override
public void run() {
runListener(asynListenerList,method,param);
}
}).start();
}
Map<Method,Set<Object>> syncListeners = PROVIDER_LISTENER_SYNC_MAP.get(triggerClass);
if (syncListeners != null && !syncListeners.isEmpty()){
Set<Object> syncListenerList = syncListeners.get(method);
runListener(syncListenerList,method,param);
}
}
private void runListener(Set<Object> listeners,Method method,Object... param){
if (listeners == null)
return;
for (Object listener : listeners){
try {
method.invoke(listener,param);
}catch (Exception e){
e.printStackTrace();
}
}
}
接下来就是触发器和监听器的绑定了,因为需要拿到监听器的示例对象,因此可以考虑在容器初始化完成时来进行绑定。实现ApplicationListener<ContextRefreshedEvent>接口便会在容器初始化后调用相应的方法。具体代码在上方的manager中,毕竟数据都有,实现起来也比较简单,因此也就不详细讲了。
配置扫描包和注入EventInitializationManager
到这里为止,基本骨架都已经搭好了。现在只剩下两个问题,EventInitializationManager应该如何注入,如何配置需要扫描的路径。
不说废话,直接上注解。
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
@Import({EventCannerBeanDefinitionRegister.class})
public @interface EventScan {
String[] value();
}
@Import就不在这解释了,我只要在项目中添加上这个注解,便可以把EventCannerBeanDefinitionRegister类注册到spring容器中,而这个也相当于一个开关,是否开启该事件框架。相应的,需要扫描的包也可以在注解上声明
@SpringBootApplication
@EventScan({"com.st.event.test"})
public class EventApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(EventApplication.class, args);
TestEventProvider bean = context.getBean(TestEventProvider.class);
System.out.println(bean);
}
}
接下来开始贴EventCannerBeanDefinitionRegister代码。通过实现ImportBeanDefinitionRegistrar接口,便拥有了往容器中加入bean的能力。EventCannerBeanDefinitionRegister无非也就干了一件事,把EventInitializationManager定义信息(包括packages属性)放入spring中。
public class EventCannerBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EventScan.class.getName(), false);
if(annotationAttributes != null) {
BeanDefinition beanDefinition = getBeanDefinition((String[])annotationAttributes.get("value"));
registry.registerBeanDefinition("com.st.event.eventInitializationManage", beanDefinition);
}
}
private BeanDefinition getBeanDefinition(String[] values) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(EventInitializationManager.class);
beanDefinition.setSource(null);
beanDefinition.getPropertyValues().add("packages",values);
beanDefinition.setRole(2);
return beanDefinition;
}
}
总结
到这里,这个事件框架的实现也完成了,这里是项目源码 https://github.com/yhxst/event 如果看了文章还不是很明白的同学可以去下载下来试试看。