Spring如何扫描class和配置文件

标签: spring class 文件 | 发表时间:2015-09-20 12:43 | 作者:manzhizhen
出处:http://www.iteye.com

       前几天由于公司项目架构调整,想将以前代码开发为主改成配置文件配置为主,即所有的外部服务调用由配置文件组织,因为必须高效,所以涉及包括调用顺序,并发调用等,但配置文件的缺陷是只能实现简单的业务逻辑,所以我们还用了jeval表达式Jar包。

       废话不多说,由于服务配置文件是放在Maven项目下的一个子模块的classpath下,该子模块在eclipse下运行是以用文件系统路径来扫描到并解析的,但在线上环境,该子模块是会被打成Jar包,就是说线上环境是需要解析该子模块的Jar包才能取到配置文件的。

       Jar包本质上是压缩文件,以前也做个在压缩文件中解析配置文件,但感觉不太专业,由于时间赶,不想在网上捞资料,而且靠不靠谱也不一定,于是想到了 借鉴Spring中的扫描和解析配置文件的功能代码

     (转载请注明出处: http://manzhizhen.iteye.com/blog/2244806

       我们经常用如下Spring配置来解析资源文件和扫描class:

       <context: component-scan        base-package="com.manzhizhen.server.service,com.manzhizhen.server.aop" />

       <bean
        class="org.springframework.beans.factory.config. PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:conf/resource1.properties</value>
            </list>
        </property>
    </bean>

        我本地已经有Spring4的源码,于是我直接在源码中搜索 base-package关键字,于是定位到 ComponentScanBeanDefinitionParser类:

 

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
 然后我搜索哪些类用到了BASE_PACKAGE_ATTRIBUTE,于是找到了 ComponentScanBeanDefinitionParser类:

 

 

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
     ... ...
	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(   BASE_PACKAGE_ATTRIBUTE),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// Actually scan for bean definitions and register them.
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}
 ComponentScanBeanDefinitionParser类的作用就是将解析来的xml元素转换成Bean定义,并将他们注册到上下文中,所以我可以从这里开始追踪Spring是如何根据我们定义的class路径去扫描class文件的。
       其中,element.getAttribute(BASE_PACKAGE_ATTRIBUTE)的值就是我们配置的"com.manzhizhen.server.service,com.manzhizhen.server.aop",而ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS只是在Spring中预定义的配置路径分隔符而已,比如“.;\t\n”,最后经过分隔,得到的String[] basePackages就是com.manzhizhen.server.service和com.manzhizhen.server.aop组成的字符串列表了。

 

        我们发现,代码 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);就已经把我们配置的两个package下的所有class解析出来了,所以我决定看看scanner.doScan(basePackages)里面到底做了什么,于是我们来到了 ClassPathBeanDefinitionScanner#doScan

 

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
			   Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}						
		}
		return beanDefinitions;
	}
 由于上面加黑的代码就已经将class扫描出来了,于是去看看findCandidateComponents方法是怎么实现的:

 

	/**
	 * Scan the class path for candidate components.
	 * @param basePackage the package to check for annotated classes
	 * @return a corresponding Set of autodetected bean definitions
	 */
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + "/" + this.resourcePattern;
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

 代码 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + "/" + this.resourcePattern;将我们的包路径组装成Spring中能识别的格式,如把 “com.manzhizhen.server.service” 变成 " classpath*:com.manzhizhen.server.service /**/*.class",对,就是对前后做了补充, 给后面的统一解析操作提供必要的指引。我们发现代码 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); 就已经将所有的class扫描出来了,于是我们看看里面做了些什么,于是追踪到了 GenericApplicationContext# getResources

	/**
	 * This implementation delegates to this context's ResourceLoader if it
	 * implements the ResourcePatternResolver interface, falling back to the
	 * default superclass behavior else.
	 * @see #setResourceLoader
	 */
	@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		if (this.resourceLoader instanceof ResourcePatternResolver) {
			return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
		}
		return    super.getResources(locationPattern);
	}
 加黑部分,发现它是调了父类的方法 AbstractApplicationContext# getResources

 

 

	public Resource[] getResources(String locationPattern) throws IOException {
		return    this.resourcePatternResolver.getResources(locationPattern);
	}
 this.resourcePatternResolver PathMatchingResourcePatternResolver类的对象,我们看看它的 getResources 方法:
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");
		if (   locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// a class path resource (multiple resources for same name possible)
			if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
				// a class path resource pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// all class path resources with the given name
				return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
			}
		}
		else {
			// Only look for a pattern after a prefix here
			// (to not get fooled by a pattern symbol in a strange prefix).
			int prefixEnd = locationPattern.indexOf(":") + 1;
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// a file pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// a single resource with the given name
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}
 CLASSPATH_ALL_URL_PREFIX 是 PathMatchingResourcePatternResolver 的实现接口 ResourcePatternResolver 中定义的常量:
	/**
	 * Pseudo URL prefix for all matching resources from the class path: "classpath*:"
	 * This differs from ResourceLoader's classpath URL prefix in that it
	 * retrieves all matching resources for a given name (e.g. "/beans.xml"),
	 * for example in the root of all deployed JAR files.
	 * @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
	 */
	String    CLASSPATH_ALL_URL_PREFIX = "classpath*:";
 其值就是前面Spring给包路径加的前缀。
        我们回到 PathMatchingResourcePatternResolver#getResources 的那段代码,继续往下看, getPathMatcher() 返回的是 AntPathMatcher 类的对象,咱们看看它的 isPattern 方法:
	public boolean isPattern(String path) {
		return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
	}
 由于前面Spring对包路径的加工,我们很幸运的就匹配上了,于是我们进入了下面的 findPathMatchingResources(locationPattern); 方法,我们看看实现:
	/**
	 * Find all resources that match the given location pattern via the
	 * Ant-style PathMatcher. Supports resources in jar files and zip files
	 * and in the file system.
	 * @param locationPattern the location pattern to match
	 * @return the result as Resource array
	 * @throws IOException in case of I/O errors
	 * @see #doFindPathMatchingJarResources
	 * @see #doFindPathMatchingFileResources
	 * @see org.springframework.util.PathMatcher
	 */
	protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
		String rootDirPath = determineRootDir(locationPattern);
		String subPattern = locationPattern.substring(rootDirPath.length());
		Resource[] rootDirResources = getResources(rootDirPath);
		Set<Resource> result = new LinkedHashSet<Resource>(16);
		for (Resource rootDirResource : rootDirResources) {
			rootDirResource = resolveRootDirResource(rootDirResource);
			if (isJarResource(rootDirResource)) {
				result.addAll(   doFindPathMatchingJarResources(rootDirResource, subPattern));
			}
			else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
				result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
			}
			else {
				result.addAll(   doFindPathMatchingFileResources(rootDirResource, subPattern));
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
		}
		return result.toArray(new Resource[result.size()]);
	}
 第一行 String rootDirPath = determineRootDir(locationPattern);  得到的 rootDirPath 值为“classpath*:com/kuaidadi/liangjian/allconfig/server/service/”, 即资源文件根目录。第二行 String subPattern = locationPattern.substring(rootDirPath.length()); 得到的 subPattern “**/*.class”,即需要扫描的资源文件类型。接下来的 Resource[] rootDirResources = getResources(rootDirPath); 将该资源根路径解析成Spring中的资源对象。 其实 getResources findPathMatchingResources 之间会相互调用。请看上面代码我对两个方法进行了加黑: doFindPathMatchingJarResourcesdoFindPathMatchingFileResources,这两个方法分别完成Jar包和文件系统资源的扫描工作,doFindPathMatchingFileResources方法实现比较简单,文件系统的读取大家都会,咱们看看Spring是如何解析Jar包中的资源的, doFindPathMatchingJarResources 方法源码如下:
	/**
	 * Find all resources in jar files that match the given location pattern
	 * via the Ant-style PathMatcher.
	 * @param rootDirResource the root directory as Resource
	 * @param subPattern the sub pattern to match (below the root directory)
	 * @return the Set of matching Resource instances
	 * @throws IOException in case of I/O errors
	 * @see java.net.JarURLConnection
	 * @see org.springframework.util.PathMatcher
	 */
	protected Set<Resource> doFindPathMatchingJarResources(Resource rootDirResource, String subPattern)
			throws IOException {

		URLConnection con = rootDirResource.getURL().openConnection();
		JarFile jarFile;
		String jarFileUrl;
		String rootEntryPath;
		boolean newJarFile = false;

		if (con instanceof JarURLConnection) {
			// Should usually be the case for traditional JAR files.
			JarURLConnection jarCon = (JarURLConnection) con;
			jarCon.setUseCaches(false);
			jarFile = jarCon.getJarFile();
			jarFileUrl = jarCon.getJarFileURL().toExternalForm();
			JarEntry jarEntry = jarCon.getJarEntry();
			rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
		}
		else {
			// No JarURLConnection -> need to resort to URL file parsing.
			// We'll assume URLs of the format "jar:path!/entry", with the protocol
			// being arbitrary as long as following the entry format.
			// We'll also handle paths with and without leading "file:" prefix.
			String urlFile = rootDirResource.getURL().getFile();
			int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
			if (separatorIndex != -1) {
				jarFileUrl = urlFile.substring(0, separatorIndex);
				rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
				jarFile = getJarFile(jarFileUrl);
			}
			else {
				jarFile = new JarFile(urlFile);
				jarFileUrl = urlFile;
				rootEntryPath = "";
			}
			newJarFile = true;
		}

		try {
			if (logger.isDebugEnabled()) {
				logger.debug("Looking for matching resources in jar file [" + jarFileUrl + "]");
			}
			if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
				// Root entry path must end with slash to allow for proper matching.
				// The Sun JRE does not return a slash here, but BEA JRockit does.
				rootEntryPath = rootEntryPath + "/";
			}
			Set<Resource> result = new LinkedHashSet<Resource>(8);
			for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
				JarEntry entry = entries.nextElement();
				String entryPath = entry.getName();
				if (entryPath.startsWith(rootEntryPath)) {
					String relativePath = entryPath.substring(rootEntryPath.length());
					if (getPathMatcher().match(subPattern, relativePath)) {
						result.add(rootDirResource.createRelative(relativePath));
					}
				}
			}
			return result;
		}
		finally {
			// Close jar file, but only if freshly obtained -
			// not from JarURLConnection, which might cache the file reference.
			if (newJarFile) {
				jarFile.close();
			}
		}
	}
 这就拿到了我想要的代码的,我 定义了一个 ResourceTool  类,其中做了简化处理:
   public class ResourceTool {
    /**
     * 获取默认的类加载器
     * 
     * @return
     */
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = ClassUtils.class.getClassLoader();
        }

        return cl;
    }

    /**
     * 获取配置文件资源对象
     * 
     * @param location
     * @return
     * @throws IOException
     */
    public static List<URL> findAllClassPathResources(String location) throws IOException {
        String path = location;
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        Enumeration<URL> resourceUrls = getDefaultClassLoader().getResources(location);
        List<URL> result = Lists.newArrayList();
        while (resourceUrls.hasMoreElements()) {

            result.add(resourceUrls.nextElement());
        }

        return result;
    }

    /**
     * 获取指定路径下的指定文件列表
     * 
     * @param rootFile           文件路径
     * @param extensionName 文件扩展名
     * @return
     */
    public static List<File> getFiles(File rootFile, String extensionName) {
        List<File> fileList = Lists.newArrayList();

        String tail = null;
        if (extensionName == null) {
            tail = "";
        } else {
            tail = "." + extensionName;
        }

        if (rootFile == null) {
            return fileList;

        } else if (rootFile.isFile() && rootFile.getName().endsWith(tail)) {
            fileList.add(rootFile);
            return fileList;

        } else if (rootFile.isDirectory()) {
            File[] files = rootFile.listFiles();
            for (File file : files) {
                if (file.isFile() && file.getName().endsWith(tail)) {
                    fileList.add(file);

                } else if (file.isDirectory()) {
                    fileList.addAll(getFiles(file, extensionName));
                }
            }
        }

        return fileList;
    }

    public static List<URL>    getJarUrl(URL rootUrl, String extensionName) throws IOException {
        List<URL> result = Lists.newArrayList();

        if (rootUrl == null || !"jar".equals(rootUrl.getProtocol())) {
            return result;
        }

        if (StringUtils.isNotBlank(extensionName)) {
            extensionName = "." + extensionName;
        }

        if (extensionName == null) {
            extensionName = "";
        }

        URLConnection con = rootUrl.openConnection();

        JarURLConnection jarCon = (JarURLConnection) con;
        jarCon.setUseCaches(false);
        JarFile jarFile = jarCon.getJarFile();
        JarEntry jarEntry = jarCon.getJarEntry();
        String rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");

        if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
            rootEntryPath = rootEntryPath + "/";
        }

        for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
            JarEntry entry = entries.nextElement();
            String entryPath = entry.getName();

            if (entryPath.startsWith(rootEntryPath)) {
                String relativePath = entryPath.substring(rootEntryPath.length());
                if (relativePath.endsWith(".service")) {
                    result.add(createRelative(rootUrl, relativePath));

                }
            }
        }

        return result;

    }

    private static URL createRelative(URL url, String relativePath) throws MalformedURLException {
        if (relativePath.startsWith("/")) {
            relativePath = relativePath.substring(1);
        }

        return new URL(url, relativePath);
    }
}
 使用举例:
    /**
     * 将配置内容转换成内置对象
     * 
     * @return
     */
    private Map<String, ServiceSetting> getServiceSettingList(String path) {
        Map<String, ServiceSetting> map = Maps.newHashMap();

        try {
            List<URL> urlList = ResourceTool.findAllClassPathResources(path);
            for (URL url : urlList) {
                String protocol = url.getProtocol();
                // org.springframework.util.ResourceUtils
                if (ResourceUtils.URL_PROTOCOL_JAR.equals(protocol)) {
                    // 资源文件扩展名为"service"
                    List<URL> result = ResourceTool.getJarUrl(url, "service");
                    for (URL jarUrl : result) {
                        URLConnection connection = jarUrl.openConnection();
                        try {
                            /**
                             * 得到InputStream,即可解析配置文件
                             */
                            ServiceSetting serviceSetting = reloadServiceSetting(connection.getInputStream());

                            /**
                             * 检查服务配置正确性
                             */
                            boolean check = checkServiceSetting(serviceSetting);

                            if (check) {
                                map.put(serviceSetting.getName(), serviceSetting);

                                logger.info("成功加载文件:" + jarUrl.getFile() + ", serviceSetting:"
                                            + JsonUtil.toJson(serviceSetting));

                            }

                        } catch (Exception e) {
                           // TODO:

                        }

                    }

                } else if (ResourceUtils.URL_PROTOCOL_FILE.endsWith(protocol)) {
                    //    org.   springframework.   util.StringUtils
                    File file = new File(
                        new URI(StringUtils.replace(url.toString(), " ", "%20")).getSchemeSpecificPart());
  ////  资源文件扩展名为"service"                   
 List<File> fileList = ResourceTool.getFiles(file, "service");

                    for (File serviceFile : fileList) {
                        ServiceSetting serviceSetting = reloadServiceSetting(new FileInputStream(serviceFile));

                        /**
                         * 检查服务配置正确性
                         */
                        boolean check = checkServiceSetting(serviceSetting);

                        if (check) {
                            map.put(serviceSetting.getName(), serviceSetting);

                            logger.info("成功加载文件:" + serviceFile.getPath() + ", serviceSetting:"
                                        + JsonUtil.toJson(serviceSetting));

                        }
                    }

                }
            }
            return map;
        } catch (Exception e) {
           // TODO:
        }
    }
 
是不是相当简单?
 
 
 

 

 

 

 

 

 

 

 

 

 
 


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [spring class 文件] 推荐:

Spring如何扫描class和配置文件

- - 企业架构 - ITeye博客
       前几天由于公司项目架构调整,想将以前代码开发为主改成配置文件配置为主,即所有的外部服务调用由配置文件组织,因为必须高效,所以涉及包括调用顺序,并发调用等,但配置文件的缺陷是只能实现简单的业务逻辑,所以我们还用了jeval表达式Jar包.        废话不多说,由于服务配置文件是放在Maven项目下的一个子模块的classpath下,该子模块在eclipse下运行是以用文件系统路径来扫描到并解析的,但在线上环境,该子模块是会被打成Jar包,就是说线上环境是需要解析该子模块的Jar包才能取到配置文件的.

Java的 class文件结构

- - Java - 编程语言 - ITeye博客
Java-class文件结构.        我们都知道我们现在写的源代码计算机是不认识的,我们需要根据指定的编译器进行编译-连接-执行,这样才是我们想要的结果,所以计算机只能认识0或者1 ,那么如何与操作系统或者机器指令无关的程序能执行,那么在操作系统以及机器指令之上的那就是虚拟机了,这样我们编写的代码不再是最终形成二进制本地指令代码,而是一种在操作系统和机器指令之上的虚拟机规定的文件格式.

扫描Class文件的方法

- - Java - 编程语言 - ITeye博客
看了别人的代码多了,总能够学习一些东西的,把自己学的东西都点滴都记录下来.. 今天自己以前写的一个简单的工具包,用来扫描类,可以扫描jar文件和包里的文件,说明如下:. 1.扫描是某子类的Class,可以是包及其子包下的Class包括jar文件里的Class. 2.扫描类上含有某(可以是多个)注解的Class,可以是包及其子包下的Class包括jar文件里的Class.

分析一个Java Class文件

- - CSDN博客推荐文章
Java源码文件TestClass.java:. 展示这个Class文件的16进制内容:. 00 00 00 34 : 版本号是1.8.0;. 00 16: 说明常量池有21个常量,1-21, index留做他用;接下来就是分别这21个常量的描述:. 07/00 02 :CONSTANT_Class_info 常量,类名索引是该常量池的第2项;.

class 文件与dex文件区别 (dvm与jvm区别)及Android DVM介绍

- - CSDN博客推荐文章
区别一:dvm执行的是.dex格式文件  jvm执行的是.class文件   android程序编译完之后生产.class文件,然后,dex工具会把.class文件处理成.dex文件,然后把资源文件和.dex文件等打包成.apk文件. apk就是android package的意思. jvm执行的是.class文件.

Spring两种加载创建spring bean配置文件的方式

- - ITeye博客
基于spring做bean的管理,基于web开发时有两种加载配置文件创建bean的方式. 第一种基于DispatcherServlet的init-param:. 第二种基于ContextLoaderListener:. PS:如果两种方式都采用了那么spring容器中会维护两套bean,如果有调度,定时任务等那么会重复执行.

Django class-based view 基础

- Ken - python.cn(jobs, news)
自从Django在1.3中新增了class-based view以来,还没有仔细研究它,开始感觉这个东西是否有点多余. 因为Django已经有了Generic veiws了啊, 可是仔细看过class-based veiw之后, 这种想法打消了, 因为你完全可以用类方法实现你所有的视图, 而代码阅读起来却更容易!.

Django class-based view 深入

- Ken - python.cn(jobs, news)
上一篇我们粗略介绍了Django中的class-based view基础知识, 本篇我们继续来看关于class-based view的高级应用.. 我们继续沿用上篇中的model:. 我们来看看如何对一个Book实例进行更新, 我们要做的只是在视图类中更新 :.     template_name = 'updatebook.html'  #这里是你的模板文件名.

标签?ID?还是CLASS?

- - 前端观察
想谈一下几个基本的HTML问题,都是围绕着应该怎样使用HTML. 多用有语义的标签,少用div和span,避免使用没有class的div和span. 设想一下HTML的世界最初只有div和span这两个标签,其实网页依然可以写得出来. 更多标签的出现,其实是为了替代利用率高但不好书写的 
 和  来的.

偏好的 JavaScript Class 實作『基本款』

- Alu - Fred&#39;s blog
最近有一些想法,便和伙伴在空閒時間寫一些 Prototype 的專案,既然是嘗試形態的專案開發,就不必考慮穩定性和熟悉度,也不用顧慮失敗的問題,可以盡情玩弄新的,或是過去不常使用的技術. 所以,這次使用了 nodejs + express + HTML5 來開發,大玩 JavaScript. JavaScript 雖然看起來像 Java,但畢竟它不是真的 Java,很多功能考量以易於使用為優先,所以並不嚴謹,一些功能也因此被拿掉.