您的位置:首页 > 其它

跟踪activiti创建流程引擎源码

2014-11-13 12:07 351 查看
默认情况下activiti是通过XML文件activiti.cfg.xml来配置Activiti流程引擎,当然如果整合了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风格的资源才没有过滤吧。

通过阅读源码,有很多值得我们学习的地方,首先是编写可阅读,可维护的代码。比如上述的类及方法,每个方法基本只做属于自己的事情-单一职能,方法尽量短小。返回接口或抽象类等等;其次是记录认为处理比较好的方法,比如加载并解析文件等功能。

要勤于思考,勤于总结。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: