跟踪activiti创建流程引擎源码
2014-11-13 12:07
351 查看
默认情况下activiti是通过XML文件activiti.cfg.xml来配置Activiti流程引擎,当然如果整合了spring的话,这种做法不太适合了。
获取流程引擎ProcessEngine最简单的方法就是借助工具类 org.activiti.engine.ProcessEngines来创建默认的流程引擎。
首先查找方法getDefaultProcessEngine,看到本方法内调用了类内部方法getProcessEngine来实现。
继续跟踪内部方法getProcessEngine
如果没有初始化
通过类加载器获取activiti.cfg.xml的资源
借助Set的防重性,去掉重复的资源
遍历activiti.cfg.xml资源,调用方法initProcessEnginFromResource初始化流程引擎,待会详细说明此方法
通过类加载器获取activiti-context.xml
遍历资源,调用方法initProcessEngineFromSpringResource,来初始化流程引擎。
设置初始化标识
如果已初始化,log记录。
分析: 从activiti风格的文件activiti.cfg.xml或者spring风格的文件activiti-context.xml来加载流程引擎,两种方式任何一种均可完成。
疑问: 为什么文件activiti.cfg.xml资源需要去重,而文件activiti-context.xml不需要防重。
源码描述如下:// Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups
接着跟踪方法initProcessEnginFromResource和initProcessEngineFromSpringResource
a initProcessEnginFromResource
借助类ProcessEngineConfiguration的方法createProcessEngineConfigurationFromInputStream来完成资源的读取。
从上面的代码可获知,真正读取文件的方式,是使用spring的beanFacotry来完成的。相信大家读到这里已经明白了activiti资源的加载过程。
b spring风格的方式 initProcessEngineFromSpringResource
写到这里,我们来尝试解决刚才提到的疑问。activiti风格的实现并不是程序启动的时候,把所有配置文件加载起来了,更像是调用静态方法,主动加载资源,假设某些类加载器可能对同一个url返回两次,那么主动加载两次就会引起重复加载资源,进而导致流程引擎启动两次。而spring风格的方式是从applicationContext上下文中来获取bean的,是程序启动加载的时候,已经把这些配置文件分析并读取完毕,只等着调用方来使用。另外spring配置的bean默认情况下均是单例,获取的bean也是同一个。所以spring风格的资源才没有过滤吧。
通过阅读源码,有很多值得我们学习的地方,首先是编写可阅读,可维护的代码。比如上述的类及方法,每个方法基本只做属于自己的事情-单一职能,方法尽量短小。返回接口或抽象类等等;其次是记录认为处理比较好的方法,比如加载并解析文件等功能。
要勤于思考,勤于总结。
获取流程引擎ProcessEngine最简单的方法就是借助工具类 org.activiti.engine.ProcessEngines来创建默认的流程引擎。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()跟踪源码,看看内部如何实现的。
首先查找方法getDefaultProcessEngine,看到本方法内调用了类内部方法getProcessEngine来实现。
public static ProcessEngine getDefaultProcessEngine() { return getProcessEngine(NAME_DEFAULT); }
继续跟踪内部方法getProcessEngine
/** obtain a process engine by name. * @param processEngineName is the name of the process engine or null for the default process engine. */ public static ProcessEngine getProcessEngine(String processEngineName) { if (!isInitialized) { init(); } return processEngines.get(processEngineName); }关键方法init
/** Initializes all process engines that can be found on the classpath for * resources <code>activiti.cfg.xml</code> (plain Activiti style configuration) * and for resources <code>activiti-context.xml</code> (Spring style configuration). */初始化所有的在classpath路径上资源(activiti风格的文件activiti.cfg.xml)和资源(spring风格的文件activiti-context.xml)中搜索到的流程引擎
public synchronized static void init() { if (!isInitialized) { if(processEngines == null) { // Create new map to store process-engines if current map is null processEngines = new HashMap<String, ProcessEngine>(); } ClassLoader classLoader = ReflectUtil.getClassLoader(); Enumeration<URL> resources = null; try { resources = classLoader.getResources("activiti.cfg.xml"); } catch (IOException e) { throw new ActivitiException("problem retrieving activiti.cfg.xml resources on the classpath: "+System.getProperty("java.class.path"), e); } // Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups Set<URL> configUrls = new HashSet<URL>(); while (resources.hasMoreElements()) { configUrls.add( resources.nextElement() ); } for (Iterator<URL> iterator = configUrls.iterator(); iterator.hasNext();) { URL resource = iterator.next(); initProcessEnginFromResource(resource); } try { resources = classLoader.getResources("activiti-context.xml"); } catch (IOException e) { throw new ActivitiException("problem retrieving activiti-context.xml resources on the classpath: "+System.getProperty("java.class.path"), e); } while (resources.hasMoreElements()) { URL resource = resources.nextElement(); initProcessEngineFromSpringResource(resource); } isInitialized = true; } else { log.info("Process engines already initialized"); } }
如果没有初始化
通过类加载器获取activiti.cfg.xml的资源
借助Set的防重性,去掉重复的资源
遍历activiti.cfg.xml资源,调用方法initProcessEnginFromResource初始化流程引擎,待会详细说明此方法
通过类加载器获取activiti-context.xml
遍历资源,调用方法initProcessEngineFromSpringResource,来初始化流程引擎。
设置初始化标识
如果已初始化,log记录。
分析: 从activiti风格的文件activiti.cfg.xml或者spring风格的文件activiti-context.xml来加载流程引擎,两种方式任何一种均可完成。
疑问: 为什么文件activiti.cfg.xml资源需要去重,而文件activiti-context.xml不需要防重。
源码描述如下:// Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups
接着跟踪方法initProcessEnginFromResource和initProcessEngineFromSpringResource
a initProcessEnginFromResource
private static ProcessEngineInfo initProcessEnginFromResource(URL resourceUrl) { ProcessEngineInfo processEngineInfo = processEngineInfosByResourceUrl.get(resourceUrl); // if there is an existing process engine info if (processEngineInfo!=null) { // remove that process engine from the member fields processEngineInfos.remove(processEngineInfo); if (processEngineInfo.getException()==null) { String processEngineName = processEngineInfo.getName(); processEngines.remove(processEngineName); processEngineInfosByName.remove(processEngineName); } processEngineInfosByResourceUrl.remove(processEngineInfo.getResourceUrl()); } String resourceUrlString = resourceUrl.toString(); try { log.info("initializing process engine for resource " + resourceUrl); ProcessEngine processEngine = buildProcessEngine(resourceUrl); String processEngineName = processEngine.getName(); log.info("initialised process engine " + processEngineName); processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resourceUrlString, null); processEngines.put(processEngineName, processEngine); processEngineInfosByName.put(processEngineName, processEngineInfo); } catch (Throwable e) { log.log(Level.SEVERE, "Exception while initializing process engine :" + e.getMessage(), e); processEngineInfo = new ProcessEngineInfoImpl(null, resourceUrlString, getExceptionString(e)); } processEngineInfosByResourceUrl.put(resourceUrlString, processEngineInfo); processEngineInfos.add(processEngineInfo); return processEngineInfo; }总体来说比较好理解,先查找缓存map中是否存在此url资源,如果存在就清理掉,然后根据新传来的url来获取流程引擎,并缓存起来。我们可以注意到方法buildProcessEngine(String urlString),
private static ProcessEngine buildProcessEngine(URL resource) { InputStream inputStream = null; try { inputStream = resource.openStream(); ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream); return processEngineConfiguration.buildProcessEngine(); } catch (IOException e) { throw new ActivitiException("couldn't open resource stream: "+e.getMessage(), e); } finally { IoUtil.closeSilently(inputStream); } }
借助类ProcessEngineConfiguration的方法createProcessEngineConfigurationFromInputStream来完成资源的读取。
public static ProcessEngineConfiguration createProcessEngineConfigurationFromInputStream(InputStream inputStream) { return createProcessEngineConfigurationFromInputStream(inputStream, "processEngineConfiguration"); } public static ProcessEngineConfiguration createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName) { return BeansConfigurationHelper.parseProcessEngineConfigurationFromInputStream(inputStream, beanName); }马上就要揭开庐山真面目了。
public static ProcessEngineConfiguration parseProcessEngineConfiguration(Resource springResource, String beanName) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); xmlBeanDefinitionReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); xmlBeanDefinitionReader.loadBeanDefinitions(springResource); ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) beanFactory.getBean(beanName); processEngineConfiguration.setBeans(new SpringBeanFactoryProxyMap(beanFactory)); return processEngineConfiguration; } public static ProcessEngineConfiguration parseProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName) { Resource springResource = new InputStreamResource(inputStream); return parseProcessEngineConfiguration(springResource, beanName); } public static ProcessEngineConfiguration parseProcessEngineConfigurationFromResource(String resource, String beanName) { Resource springResource = new ClassPathResource(resource); return parseProcessEngineConfiguration(springResource, beanName); }
从上面的代码可获知,真正读取文件的方式,是使用spring的beanFacotry来完成的。相信大家读到这里已经明白了activiti资源的加载过程。
b spring风格的方式 initProcessEngineFromSpringResource
protected static void initProcessEngineFromSpringResource(URL resource) { try { Class< ? > springConfigurationHelperClass = ReflectUtil.loadClass("org.activiti.spring.SpringConfigurationHelper"); Method method = springConfigurationHelperClass.getMethod("buildProcessEngine", new Class<?>[]{URL.class}); ProcessEngine processEngine = (ProcessEngine) method.invoke(null, new Object[]{resource}); String processEngineName = processEngine.getName(); ProcessEngineInfo processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resource.toString(), null); processEngineInfosByName.put(processEngineName, processEngineInfo); processEngineInfosByResourceUrl.put(resource.toString(), processEngineInfo); } catch (Exception e) { throw new ActivitiException("couldn't initialize process engine from spring configuration resource "+resource.toString()+": "+e.getMessage(), e); } }
写到这里,我们来尝试解决刚才提到的疑问。activiti风格的实现并不是程序启动的时候,把所有配置文件加载起来了,更像是调用静态方法,主动加载资源,假设某些类加载器可能对同一个url返回两次,那么主动加载两次就会引起重复加载资源,进而导致流程引擎启动两次。而spring风格的方式是从applicationContext上下文中来获取bean的,是程序启动加载的时候,已经把这些配置文件分析并读取完毕,只等着调用方来使用。另外spring配置的bean默认情况下均是单例,获取的bean也是同一个。所以spring风格的资源才没有过滤吧。
通过阅读源码,有很多值得我们学习的地方,首先是编写可阅读,可维护的代码。比如上述的类及方法,每个方法基本只做属于自己的事情-单一职能,方法尽量短小。返回接口或抽象类等等;其次是记录认为处理比较好的方法,比如加载并解析文件等功能。
要勤于思考,勤于总结。
相关文章推荐
- 疯狂Activiti6.0连载(5)流程引擎的创建
- 工作流activiti-02事物控制、流程引擎创建
- Activiti源码跟踪之流程部署
- Activiti 工作流项目源码 引擎 流程审批 后台框架 springmvc SSMjava
- 【activiti】创建activiti流程引擎以及相应表的方式
- activiti 流程部署以及实例(processInstance)的创建
- CloudStack 创建VM 源码流程分析
- Dialog创建流程源码解析
- 关于自学activiti流程引擎的一点点感悟和代码分享
- Activiti-5.2工作流引擎-源码解析(引擎初始化)
- activiti入门2流程引擎的API和服务基础
- CloudStack 创建VM 源码流程分析
- 非典型2D游戏引擎 Orx 源码阅读笔记 完结篇(7) 渲染流程
- activiti创建流程23张表结构
- IronPython 源码剖析系列(2):IronPython 引擎的运作流程
- activiti入门2流程引擎的API和服务基础
- 集成activiti-modeler 到 自己的业务系统(集成流程跟踪-完美支持IE)
- 基于开源流程引擎Activiti5的工作流开发平台BPMX3
- activiti5.0.alpha3之流程引擎建立入口
- IronPython 源码剖析系列(2):IronPython 引擎的运作流程