osgi类加载顺序源码解析

osgi容器中有三种classloader:各个bundle都有自己的classLoader,osgi容器也有一个框架级的classloader,这些classloader也不同于启动osgi容器的classloader(一般是app classloader)。如果需要加载一个类,那么就需要在这三个classloader之间挑出一个合适的classloader来加载类。本文将根据equinox和felix源码肤浅的描述一下osgi类加载顺序。

osgi规范中类加载顺序的定义

首先osgi类加载顺序不是开发osgi框架的人定的,而是osgi规范定的,但是每个框架的实现可能有所不同(规范详见osgi R4.3中的第3.9.4节 《Overall Search Order》):

在类和资源加载过程中,框架必须遵循以下规则。当请求一个bundle类加载器进行类加载或者资源的查找,查找必须按照以下顺序执行:

  1. 如果类或者资源是在包java.*中,那么交由父级类加载器代理完成,否则,搜索过程进入第二步。如果父类级类加载器加载失败,那么查找过程结束,加载失败。
  2. 如果类或者资源在启动代理序列(org.osgi.framework.bootdelegation)中定义,那么交由父级代理完成,此时的父级代理有启动参数org.osgi.framework.bundle.parent指定,默认是引导类加载器(bootstrap class loader),如果找到了类或者资源,那么查找过程结束。
  3. 如果类或者资源所在的包是在Import-Package中指定的,或者是在此之前通过动态导入加载的了,那么将请求转发到导出bundle的类加载器,否则搜索继续进行下一步;如果该包在启动参数org.osgi.framework.system.packages.extra中,则将请求转发给osgi容器外部的类加载器(通常是系统类加载器)。如果将请求交由导出类加载器代理,而类或者资源又没有找到,那么查找过程中止,同时请求失败。
  4. 如果包中类或者和资源所在的包由其他bundle通过是使用Require-Bundle从一个或多个其他bundle进行导入的了,那么请求交由其他那些bundle的类加载器完成,按照根据在bundle的manifest中指定的顺序进行查找进行查找。如果没有找到类或者资源,搜索继续进行。
  5. 使用bundle本身的内部bundle类路径查找完毕之后,。如果类或者资源还没有找到,搜索继续到下一步。
  6. 查找每一个附加的fragment的内部类路径,fragment的查找根据bundle ID顺序升序查找。如果没有找到类或者资源的,查找过程继续下一步。
  7. 如果包中类或者资源所在的包由bundle导出,或者包由bundle导入(使用Import-Package或者Require-Bundle),查找结束,即类或者资源没有找到。
  8. 否则,如果类或者资源所在的包是通过使用DynamicImport-Package进行导入,那么试图进行包的动态导入。导出者exporter必须符合包约束。如果找到了合适的导出者exporter,然后建立连接,以后的包导入就可以通过步骤三进行。如果连接建立失败,那么请求失败。
  9. 如果动态导入建立了,请求交由导出bundle的类加载器代理。如果代理查找失败,那么查找过程中止,请求失败。

下面的非标准流程图展示了查找的过程:

从规范可见,类加载的优先级顺序基本按照如下的原则:父容器classloader(通常是app classloader) –> 其他bundle的classloader –> 当前bundle的classloader –> 动态导入的包所在bundle的classloader。这个原则既可以使相同的类(包名也相同)尽可能只被加载一次,减少虚拟机perm区大小,也正因为如此,不同bundle中的相同的类,委托给同一个classloader加载,才能做到他们的对象和引用可以相互转换。(要知道一个类如果由不同的classloader加载后,其中一个classloader加载的类的对象是不能赋值给另一个classloader加载的类的引用的。)

osgi类加载顺序的源码解析

目前最流行的osgi框架实现有两个:equinoxfelix,下面分别列出equinox和felix在类加载顺序上的实现代码,如果大家在开发过程中发现一些类加载方面的奇怪现象,就可以通过找到如下的代码位置进行调试,从而跟踪出问题的根源,尤其是需要osgi容器内外共享类的时候。

equinox的osgi类加载顺序实现代码

在org.eclipse.osgi.internal.loader.BundleLoader.java的findClass(String name, boolean checkParent)和findClassInternal(String name, boolean checkParent, ClassLoader parentCL)方法中:

Class<!--?--> findClass(String name, boolean checkParent) throws ClassNotFoundException {
	ClassLoader parentCL = getParentClassLoader();
	if (checkParent && parentCL != null && name.startsWith(JAVA_PACKAGE))
		// 1) 如果类的包名以"java."开头,则委托给父容器加载器加载(默认是bootstrap classloader)
		return parentCL.loadClass(name);
	return findClassInternal(name, checkParent, parentCL);
}
private Class<!--?--> findClassInternal(String name, boolean checkParent, ClassLoader parentCL) throws ClassNotFoundException {
	if (Debug.DEBUG_LOADER)
		Debug.println("BundleLoader[" + this + "].loadBundleClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	String pkgName = getPackageName(name);
	boolean bootDelegation = false;
	// follow the OSGi delegation model
	if (checkParent && parentCL != null && bundle.getFramework().isBootDelegationPackage(pkgName))
		// 2) 如果该类在父容器类加载器委托清单中(即osgi容器启动参数org.osgi.framework.bootdelegation的值中)
		// 则委托给父容器类加载器加载(默认是bootstrap classloader)。
		try {
			return parentCL.loadClass(name);
		} catch (ClassNotFoundException cnfe) {
			// we want to continue
			bootDelegation = true;
		}
	Class<!--?--> result = null;
	try {
		result = (Class<!--?-->) searchHooks(name, PRE_CLASS);
	} catch (ClassNotFoundException e) {
		throw e;
	} catch (FileNotFoundException e) {
		// will not happen
	}
	if (result != null)
		return result;
	// 3) 在Import-Package中查找该类来自哪个bundle
	PackageSource source = findImportedSource(pkgName, null);
	if (source != null) {
		// 3) 由查找到的bundle加载这个类
		result = source.loadClass(name);
		if (result != null)
			return result;
		throw new ClassNotFoundException(name);
	}
	// 4) 在Require-Bundle中查找哪一个bundle中有这个类
	source = findRequiredSource(pkgName, null);
	if (source != null)
		// 4) attempt to load from source but continue on failure
		result = source.loadClass(name);
	// 5) 在当前的bundle中查找和加载该类
	if (result == null)
		result = findLocalClass(name);
	if (result != null)
		return result;
	// 6) 在DynamicImport-Package中查找并加载
	if (source == null) {
		source = findDynamicSource(pkgName);
		if (source != null) {
			result = source.loadClass(name);
			if (result != null)
				return result;
			// must throw CNFE if dynamic import source does not have the class
			throw new ClassNotFoundException(name);
		}
	}
	if (result == null)
		try {
			result = (Class<!--?-->) searchHooks(name, POST_CLASS);
		} catch (ClassNotFoundException e) {
			throw e;
		} catch (FileNotFoundException e) {
			// will not happen
		}
	// do buddy policy loading
	if (result == null && policy != null)
		result = policy.doBuddyClassLoading(name);
	if (result != null)
		return result;
	// hack to support backwards compatibiility for bootdelegation
	// or last resort; do class context trick to work around VM bugs
	if (parentCL != null && !bootDelegation && ((checkParent && bundle.getFramework().compatibiltyBootDelegation) || isRequestFromVM()))
		// we don't need to continue if a CNFE is thrown here.
		try {
			return parentCL.loadClass(name);
		} catch (ClassNotFoundException e) {
			// we want to generate our own exception below
		}
	throw new ClassNotFoundException(name);
}

felix的osgi类加载顺序实现代码

在org.apache.felix.framework.BundleWiringImpl.java类的findClassOrResourceByDelegation(String name, boolean isClass) 方法中:

private Object findClassOrResourceByDelegation(String name, boolean isClass)
    throws ClassNotFoundException, ResourceNotFoundException
{
    Object result = null;
 
    Set requestSet = (Set) m_cycleCheck.get();
    if (requestSet == null)
    {
        requestSet = new HashSet();
        m_cycleCheck.set(requestSet);
    }
    if (requestSet.add(name))
    {
        try
        {
            // Get the package of the target class/resource.
            String pkgName = (isClass)
                ? Util.getClassPackage(name)
                : Util.getResourcePackage(name);
 
            // 如果该类在父容器类加载器委托清单中(即osgi容器启动参数org.osgi.framework.bootdelegation的值中)
            if (shouldBootDelegate(pkgName))
            {
                try
                {
                    // 根据osgi容器启动参数org.osgi.framework.bundle.parent的值来获得适当的父类加载器
                    // 默认是bootstrap classloader
                    ClassLoader bdcl = getBootDelegationClassLoader();
                    result = (isClass)
                        ? (Object) bdcl.loadClass(name)
                        : (Object) bdcl.getResource(name);
                    // If this is a java.* package, then always terminate the
                    // search; otherwise, continue to look locally if not found.
                    if (pkgName.startsWith("java.") || (result != null))
                    {
                        return result;
                    }
                }
                catch (ClassNotFoundException ex)
                {
                    // If this is a java.* package, then always terminate the
                    // search; otherwise, continue to look locally if not found.
                    if (pkgName.startsWith("java."))
                    {
                        throw ex;
                    }
                }
            }
 
            // 在Import-Package和Require-Bundle中查找是否有该类,有则委托给相应的加载器加载,详见下面searchImports方法的代码
            result = searchImports(pkgName, name, isClass);
 
            // 如果还没找到,就在当前bundle的classpath下查找是否有该类,有则加载.
            if (result == null)
            {
                if (isClass)
                {
                    ClassLoader cl = getClassLoaderInternal();
                    if (cl == null)
                    {
                        throw new ClassNotFoundException(
                            "Unable to load class '"
                            + name
                            + "' because the bundle wiring for "
                            + m_revision.getSymbolicName()
                            + " is no longer valid.");
                    }
                    result = (Object) ((BundleClassLoader) cl).findClass(name);
                }
                else
                {
                    result = (Object) m_revision.getResourceLocal(name);
                }
 
                // 如果还是没找到,则在DynamicImport-Package中查找和加载
                if (result == null)
                {
                    result = searchDynamicImports(pkgName, name, isClass);
                }
            }
        }
        finally
        {
            requestSet.remove(name);
        }
    }
    else
    {
        // If a cycle is detected, we should return null to break the
        // cycle. This should only ever be return to internal class
        // loading code and not to the actual instigator of the class load.
        return null;
    }
 
    if (result == null)
    {
        if (isClass)
        {
            throw new ClassNotFoundException(
                name + " not found by " + this.getBundle());
        }
        else
        {
            throw new ResourceNotFoundException(
                name + " not found by " + this.getBundle());
        }
    }
 
    return result;
}
 
// 在Import-Package和Require-Bundle中查找是否有该类,有则委托给相应的加载器加载
private Object searchImports(String pkgName, String name, boolean isClass)
	throws ClassNotFoundException, ResourceNotFoundException
{
	// 从Import-Package中查找和加载.
	BundleRevision provider = m_importedPkgs.get(pkgName);
	if (provider != null)
	{
		// 如果pkgName配置在启动参数org.osgi.framework.system.packages.extra 的值中,
		// 则由启动osgi容器的类加载器来加载这个类或者资源,(一般情况是系统类加载器加载)。
		// 其classloader来自ExtensionManagerWiring的getClassByDelegation 方法,
		// 其实质是ExtensionManagerWiring的getClass().getClassLoader().loadClass(name) 来加载的,
		// 而ExtensionManagerWiring类是由启动osgi容器的类加载器加载;
		// 如果pkgName未配置在启动参数org.osgi.framework.system.packages.extra的清单中,
		// 则用相应的bundle的classloader来加载
		Object result = (isClass)
			? (Object) ((BundleWiringImpl) provider.getWiring()).getClassByDelegation(name)
			: (Object) ((BundleWiringImpl) provider.getWiring()).getResourceByDelegation(name);
		if (result != null)
		{
			return result;
		}
		// If no class or resource was found, then we must throw an exception
		// since the provider of this package did not contain the
		// requested class and imported packages are atomic.
		if (isClass)
		{
			throw new ClassNotFoundException(name);
		}
		throw new ResourceNotFoundException(name);
	}
	// 从Require-Bundle中查找和加载.
	List providers = m_requiredPkgs.get(pkgName);
	if (providers != null)
	{
		for (BundleRevision p : providers)
		{
			// If we find the class or resource, then return it.
			try
			{
				Object result = (isClass)
					? (Object) ((BundleWiringImpl) p.getWiring()).getClassByDelegation(name)
					: (Object) ((BundleWiringImpl) p.getWiring()).getResourceByDelegation(name);
				if (result != null)
				{
					return result;
				}
			}
			catch (ClassNotFoundException ex)
			{
				// Since required packages can be split, don't throw an
				// exception here if it is not found. Instead, we'll just
				// continue searching other required bundles and the
				// revision's local content.
			}
		}
	}
	return null;
}

猜你喜欢

转载自blog.csdn.net/judyjie/article/details/85253238