Spring中反射相关 BeanUtils.java 源码
来源:互联网 发布:c md5解密算法代码 编辑:程序博客网 时间:2024/05/16 09:38
- /**
- * Copyright 2002-2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.springframework.beans;
- import java.beans.PropertyDescriptor;
- import java.beans.PropertyEditor;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.net.URI;
- import java.net.URL;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Date;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.WeakHashMap;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.core.MethodParameter;
- import org.springframework.util.Assert;
- import org.springframework.util.ClassUtils;
- import org.springframework.util.ReflectionUtils;
- import org.springframework.util.StringUtils;
- /***
- * Static convenience methods for JavaBeans: for instantiating beans,
- * checking bean property types, copying bean properties, etc.
- *
- * <p>Mainly for use within the framework, but to some degree also
- * useful for application classes.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- * @author Rob Harrop
- * @author Sam Brannen
- */
- public abstract class BeanUtils {
- private static final Log logger = LogFactory.getLog(BeanUtils.class);
- private static final Map<Class<?>, Boolean> unknownEditorTypes =
- Collections.synchronizedMap(new WeakHashMap<Class<?>, Boolean>());
- /***
- * Convenience method to instantiate a class using its no-arg constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
- * @param clazz class to instantiate
- * @return the new instance
- * @throws BeanInstantiationException if the bean cannot be instantiated
- */
- public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException {
- Assert.notNull(clazz, "Class must not be null");
- if (clazz.isInterface()) {
- throw new BeanInstantiationException(clazz, "Specified class is an interface");
- }
- try {
- return clazz.newInstance();
- }
- catch (InstantiationException ex) {
- throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex);
- }
- catch (IllegalAccessException ex) {
- throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex);
- }
- }
- /***
- * Convenience method to instantiate a class using its no-arg constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
- * <p>Note that this method tries to set the constructor accessible
- * if given a non-accessible (that is, non-public) constructor.
- * @param clazz class to instantiate
- * @return the new instance
- * @throws BeanInstantiationException if the bean cannot be instantiated
- */
- public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
- Assert.notNull(clazz, "Class must not be null");
- if (clazz.isInterface()) {
- throw new BeanInstantiationException(clazz, "Specified class is an interface");
- }
- try {
- return instantiateClass(clazz.getDeclaredConstructor());
- }
- catch (NoSuchMethodException ex) {
- throw new BeanInstantiationException(clazz, "No default constructor found", ex);
- }
- }
- /***
- * Convenience method to instantiate a class using the given constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
- * <p>Note that this method tries to set the constructor accessible
- * if given a non-accessible (that is, non-public) constructor.
- * @param ctor the constructor to instantiate
- * @param args the constructor arguments to apply
- * @return the new instance
- * @throws BeanInstantiationException if the bean cannot be instantiated
- */
- public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
- Assert.notNull(ctor, "Constructor must not be null");
- try {
- ReflectionUtils.makeAccessible(ctor);
- return ctor.newInstance(args);
- }
- catch (InstantiationException ex) {
- throw new BeanInstantiationException(ctor.getDeclaringClass(),
- "Is it an abstract class?", ex);
- }
- catch (IllegalAccessException ex) {
- throw new BeanInstantiationException(ctor.getDeclaringClass(),
- "Is the constructor accessible?", ex);
- }
- catch (IllegalArgumentException ex) {
- throw new BeanInstantiationException(ctor.getDeclaringClass(),
- "Illegal arguments for constructor", ex);
- }
- catch (InvocationTargetException ex) {
- throw new BeanInstantiationException(ctor.getDeclaringClass(),
- "Constructor threw exception", ex.getTargetException());
- }
- }
- /***
- * Find a method with the given method name and the given parameter types,
- * declared on the given class or one of its superclasses. Prefers public methods,
- * but will return a protected, package access, or private method too.
- * <p>Checks <code>Class.getMethod</code> first, falling back to
- * <code>findDeclaredMethod</code>. This allows to find public methods
- * without issues even in environments with restricted Java security settings.
- * @param clazz the class to check
- * @param methodName the name of the method to find
- * @param paramTypes the parameter types of the method to find
- * @return the Method object, or <code>null</code> if not found
- * @see java.lang.Class#getMethod
- * @see #findDeclaredMethod
- */
- public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
- try {
- return clazz.getMethod(methodName, paramTypes);
- }
- catch (NoSuchMethodException ex) {
- return findDeclaredMethod(clazz, methodName, paramTypes);
- }
- }
- /***
- * Find a method with the given method name and the given parameter types,
- * declared on the given class or one of its superclasses. Will return a public,
- * protected, package access, or private method.
- * <p>Checks <code>Class.getDeclaredMethod</code>, cascading upwards to all superclasses.
- * @param clazz the class to check
- * @param methodName the name of the method to find
- * @param paramTypes the parameter types of the method to find
- * @return the Method object, or <code>null</code> if not found
- * @see java.lang.Class#getDeclaredMethod
- */
- public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) {
- try {
- return clazz.getDeclaredMethod(methodName, paramTypes);
- }
- catch (NoSuchMethodException ex) {
- if (clazz.getSuperclass() != null) {
- return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
- }
- return null;
- }
- }
- /***
- * Find a method with the given method name and minimal parameters (best case: none),
- * declared on the given class or one of its superclasses. Prefers public methods,
- * but will return a protected, package access, or private method too.
- * <p>Checks <code>Class.getMethods</code> first, falling back to
- * <code>findDeclaredMethodWithMinimalParameters</code>. This allows for finding public
- * methods without issues even in environments with restricted Java security settings.
- * @param clazz the class to check
- * @param methodName the name of the method to find
- * @return the Method object, or <code>null</code> if not found
- * @throws IllegalArgumentException if methods of the given name were found but
- * could not be resolved to a unique method with minimal parameters
- * @see java.lang.Class#getMethods
- * @see #findDeclaredMethodWithMinimalParameters
- */
- public static Method findMethodWithMinimalParameters(Class<?> clazz, String methodName)
- throws IllegalArgumentException {
- Method targetMethod = findMethodWithMinimalParameters(clazz.getMethods(), methodName);
- if (targetMethod == null) {
- targetMethod = findDeclaredMethodWithMinimalParameters(clazz, methodName);
- }
- return targetMethod;
- }
- /***
- * Find a method with the given method name and minimal parameters (best case: none),
- * declared on the given class or one of its superclasses. Will return a public,
- * protected, package access, or private method.
- * <p>Checks <code>Class.getDeclaredMethods</code>, cascading upwards to all superclasses.
- * @param clazz the class to check
- * @param methodName the name of the method to find
- * @return the Method object, or <code>null</code> if not found
- * @throws IllegalArgumentException if methods of the given name were found but
- * could not be resolved to a unique method with minimal parameters
- * @see java.lang.Class#getDeclaredMethods
- */
- public static Method findDeclaredMethodWithMinimalParameters(Class<?> clazz, String methodName)
- throws IllegalArgumentException {
- Method targetMethod = findMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
- if (targetMethod == null && clazz.getSuperclass() != null) {
- targetMethod = findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName);
- }
- return targetMethod;
- }
- /***
- * Find a method with the given method name and minimal parameters (best case: none)
- * in the given list of methods.
- * @param methods the methods to check
- * @param methodName the name of the method to find
- * @return the Method object, or <code>null</code> if not found
- * @throws IllegalArgumentException if methods of the given name were found but
- * could not be resolved to a unique method with minimal parameters
- */
- public static Method findMethodWithMinimalParameters(Method[] methods, String methodName)
- throws IllegalArgumentException {
- Method targetMethod = null;
- int numMethodsFoundWithCurrentMinimumArgs = 0;
- for (Method method : methods) {
- if (method.getName().equals(methodName)) {
- int numParams = method.getParameterTypes().length;
- if (targetMethod == null || numParams < targetMethod.getParameterTypes().length) {
- targetMethod = method;
- numMethodsFoundWithCurrentMinimumArgs = 1;
- }
- else {
- if (targetMethod.getParameterTypes().length == numParams) {
- // Additional candidate with same length.
- numMethodsFoundWithCurrentMinimumArgs++;
- }
- }
- }
- }
- if (numMethodsFoundWithCurrentMinimumArgs > 1) {
- throw new IllegalArgumentException("Cannot resolve method '" + methodName +
- "' to a unique method. Attempted to resolve to overloaded method with " +
- "the least number of parameters, but there were " +
- numMethodsFoundWithCurrentMinimumArgs + " candidates.");
- }
- return targetMethod;
- }
- /***
- * Parse a method signature in the form <code>methodName[([arg_list])]</code>,
- * where <code>arg_list</code> is an optional, comma-separated list of fully-qualified
- * type names, and attempts to resolve that signature against the supplied <code>Class</code>.
- * <p>When not supplying an argument list (<code>methodName</code>) the method whose name
- * matches and has the least number of parameters will be returned. When supplying an
- * argument type list, only the method whose name and argument types match will be returned.
- * <p>Note then that <code>methodName</code> and <code>methodName()</code> are <strong>not</strong>
- * resolved in the same way. The signature <code>methodName</code> means the method called
- * <code>methodName</code> with the least number of arguments, whereas <code>methodName()</code>
- * means the method called <code>methodName</code> with exactly 0 arguments.
- * <p>If no method can be found, then <code>null</code> is returned.
- * @param signature the method signature as String representation
- * @param clazz the class to resolve the method signature against
- * @return the resolved Method
- * @see #findMethod
- * @see #findMethodWithMinimalParameters
- */
- public static Method resolveSignature(String signature, Class<?> clazz) {
- Assert.hasText(signature, "'signature' must not be empty");
- Assert.notNull(clazz, "Class must not be null");
- int firstParen = signature.indexOf("(");
- int lastParen = signature.indexOf(")");
- if (firstParen > -1 && lastParen == -1) {
- throw new IllegalArgumentException("Invalid method signature '" + signature +
- "': expected closing ')' for args list");
- }
- else if (lastParen > -1 && firstParen == -1) {
- throw new IllegalArgumentException("Invalid method signature '" + signature +
- "': expected opening '(' for args list");
- }
- else if (firstParen == -1 && lastParen == -1) {
- return findMethodWithMinimalParameters(clazz, signature);
- }
- else {
- String methodName = signature.substring(0, firstParen);
- String[] parameterTypeNames =
- StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen));
- Class<?>[] parameterTypes = new Class[parameterTypeNames.length];
- for (int i = 0; i < parameterTypeNames.length; i++) {
- String parameterTypeName = parameterTypeNames[i].trim();
- try {
- parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader());
- }
- catch (Throwable ex) {
- throw new IllegalArgumentException("Invalid method signature: unable to resolve type [" +
- parameterTypeName + "] for argument " + i + ". Root cause: " + ex);
- }
- }
- return findMethod(clazz, methodName, parameterTypes);
- }
- }
- /***
- * Retrieve the JavaBeans <code>PropertyDescriptor</code>s of a given class.
- * @param clazz the Class to retrieve the PropertyDescriptors for
- * @return an array of <code>PropertyDescriptors</code> for the given class
- * @throws BeansException if PropertyDescriptor look fails
- */
- public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
- CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
- return cr.getPropertyDescriptors();
- }
- /***
- * Retrieve the JavaBeans <code>PropertyDescriptors</code> for the given property.
- * @param clazz the Class to retrieve the PropertyDescriptor for
- * @param propertyName the name of the property
- * @return the corresponding PropertyDescriptor, or <code>null</code> if none
- * @throws BeansException if PropertyDescriptor lookup fails
- */
- public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName)
- throws BeansException {
- CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
- return cr.getPropertyDescriptor(propertyName);
- }
- /***
- * Find a JavaBeans <code>PropertyDescriptor</code> for the given method,
- * with the method either being the read method or the write method for
- * that bean property.
- * @param method the method to find a corresponding PropertyDescriptor for
- * @return the corresponding PropertyDescriptor, or <code>null</code> if none
- * @throws BeansException if PropertyDescriptor lookup fails
- */
- public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException {
- Assert.notNull(method, "Method must not be null");
- PropertyDescriptor[] pds = getPropertyDescriptors(method.getDeclaringClass());
- for (PropertyDescriptor pd : pds) {
- if (method.equals(pd.getReadMethod()) || method.equals(pd.getWriteMethod())) {
- return pd;
- }
- }
- return null;
- }
- /***
- * Find a JavaBeans PropertyEditor following the 'Editor' suffix convention
- * (e.g. "mypackage.MyDomainClass" -> "mypackage.MyDomainClassEditor").
- * <p>Compatible to the standard JavaBeans convention as implemented by
- * {@link java.beans.PropertyEditorManager} but isolated from the latter's
- * registered default editors for primitive types.
- * @param targetType the type to find an editor for
- * @return the corresponding editor, or <code>null</code> if none found
- */
- public static PropertyEditor findEditorByConvention(Class<?> targetType) {
- if (targetType == null || targetType.isArray() || unknownEditorTypes.containsKey(targetType)) {
- return null;
- }
- ClassLoader cl = targetType.getClassLoader();
- if (cl == null) {
- try {
- cl = ClassLoader.getSystemClassLoader();
- if (cl == null) {
- return null;
- }
- }
- catch (Throwable ex) {
- // e.g. AccessControlException on Google App Engine
- if (logger.isDebugEnabled()) {
- logger.debug("Could not access system ClassLoader: " + ex);
- }
- return null;
- }
- }
- String editorName = targetType.getName() + "Editor";
- try {
- Class<?> editorClass = cl.loadClass(editorName);
- if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
- if (logger.isWarnEnabled()) {
- logger.warn("Editor class [" + editorName +
- "] does not implement [java.beans.PropertyEditor] interface");
- }
- unknownEditorTypes.put(targetType, Boolean.TRUE);
- return null;
- }
- return (PropertyEditor) instantiateClass(editorClass);
- }
- catch (ClassNotFoundException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("No property editor [" + editorName + "] found for type " +
- targetType.getName() + " according to 'Editor' suffix convention");
- }
- unknownEditorTypes.put(targetType, Boolean.TRUE);
- return null;
- }
- }
- /***
- * Determine the bean property type for the given property from the
- * given classes/interfaces, if possible.
- * @param propertyName the name of the bean property
- * @param beanClasses the classes to check against
- * @return the property type, or <code>Object.class</code> as fallback
- */
- public static Class<?> findPropertyType(String propertyName, Class<?>[] beanClasses) {
- if (beanClasses != null) {
- for (Class<?> beanClass : beanClasses) {
- PropertyDescriptor pd = getPropertyDescriptor(beanClass, propertyName);
- if (pd != null) {
- return pd.getPropertyType();
- }
- }
- }
- return Object.class;
- }
- /***
- * Obtain a new MethodParameter object for the write method of the
- * specified property.
- * @param pd the PropertyDescriptor for the property
- * @return a corresponding MethodParameter object
- */
- public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
- if (pd instanceof GenericTypeAwarePropertyDescriptor) {
- return new MethodParameter(
- ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodParameter());
- }
- else {
- return new MethodParameter(pd.getWriteMethod(), 0);
- }
- }
- /***
- * Check if the given type represents a "simple" property:
- * a primitive, a String or other CharSequence, a Number, a Date,
- * a URI, a URL, a Locale, a Class, or a corresponding array.
- * <p>Used to determine properties to check for a "simple" dependency-check.
- * @param clazz the type to check
- * @return whether the given type represents a "simple" property
- * @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
- * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
- */
- public static boolean isSimpleProperty(Class<?> clazz) {
- Assert.notNull(clazz, "Class must not be null");
- return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType()));
- }
- /***
- * Check if the given type represents a "simple" value type:
- * a primitive, a String or other CharSequence, a Number, a Date,
- * a URI, a URL, a Locale or a Class.
- * @param clazz the type to check
- * @return whether the given type represents a "simple" value type
- */
- public static boolean isSimpleValueType(Class<?> clazz) {
- return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
- CharSequence.class.isAssignableFrom(clazz) ||
- Number.class.isAssignableFrom(clazz) ||
- Date.class.isAssignableFrom(clazz) ||
- clazz.equals(URI.class) || clazz.equals(URL.class) ||
- clazz.equals(Locale.class) || clazz.equals(Class.class);
- }
- /***
- * Copy the property values of the given source bean into the target bean.
- * <p>Note: The source and target classes do not have to match or even be derived
- * from each other, as long as the properties match. Any bean properties that the
- * source bean exposes but the target bean does not will silently be ignored.
- * <p>This is just a convenience method. For more complex transfer needs,
- * consider using a full BeanWrapper.
- * @param source the source bean
- * @param target the target bean
- * @throws BeansException if the copying failed
- * @see BeanWrapper
- */
- public static void copyProperties(Object source, Object target) throws BeansException {
- copyProperties(source, target, null, null);
- }
- /***
- * Copy the property values of the given source bean into the given target bean,
- * only setting properties defined in the given "editable" class (or interface).
- * <p>Note: The source and target classes do not have to match or even be derived
- * from each other, as long as the properties match. Any bean properties that the
- * source bean exposes but the target bean does not will silently be ignored.
- * <p>This is just a convenience method. For more complex transfer needs,
- * consider using a full BeanWrapper.
- * @param source the source bean
- * @param target the target bean
- * @param editable the class (or interface) to restrict property setting to
- * @throws BeansException if the copying failed
- * @see BeanWrapper
- */
- public static void copyProperties(Object source, Object target, Class<?> editable)
- throws BeansException {
- copyProperties(source, target, editable, null);
- }
- /***
- * Copy the property values of the given source bean into the given target bean,
- * ignoring the given "ignoreProperties".
- * <p>Note: The source and target classes do not have to match or even be derived
- * from each other, as long as the properties match. Any bean properties that the
- * source bean exposes but the target bean does not will silently be ignored.
- * <p>This is just a convenience method. For more complex transfer needs,
- * consider using a full BeanWrapper.
- * @param source the source bean
- * @param target the target bean
- * @param ignoreProperties array of property names to ignore
- * @throws BeansException if the copying failed
- * @see BeanWrapper
- */
- public static void copyProperties(Object source, Object target, String[] ignoreProperties)
- throws BeansException {
- copyProperties(source, target, null, ignoreProperties);
- }
- /***
- * Copy the property values of the given source bean into the given target bean.
- * <p>Note: The source and target classes do not have to match or even be derived
- * from each other, as long as the properties match. Any bean properties that the
- * source bean exposes but the target bean does not will silently be ignored.
- * @param source the source bean
- * @param target the target bean
- * @param editable the class (or interface) to restrict property setting to
- * @param ignoreProperties array of property names to ignore
- * @throws BeansException if the copying failed
- * @see BeanWrapper
- */
- private static void copyProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties)
- throws BeansException {
- Assert.notNull(source, "Source must not be null");
- Assert.notNull(target, "Target must not be null");
- Class<?> actualEditable = target.getClass();
- if (editable != null) {
- if (!editable.isInstance(target)) {
- throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
- "] not assignable to Editable class [" + editable.getName() + "]");
- }
- actualEditable = editable;
- }
- PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
- List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
- for (PropertyDescriptor targetPd : targetPds) {
- if (targetPd.getWriteMethod() != null &&
- (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
- PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
- if (sourcePd != null && sourcePd.getReadMethod() != null) {
- try {
- Method readMethod = sourcePd.getReadMethod();
- if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
- readMethod.setAccessible(true);
- }
- Object value = readMethod.invoke(source);
- Method writeMethod = targetPd.getWriteMethod();
- if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
- writeMethod.setAccessible(true);
- }
- writeMethod.invoke(target, value);
- }
- catch (Throwable ex) {
- throw new FatalBeanException("Could not copy properties from source to target", ex);
- }
- }
- }
- }
- }
- }
0 0
- Spring中反射相关 BeanUtils.java 源码
- Spring » BeanUtils.java 源码
- Java反射 BeanUtils
- 记一次神奇的优化过程:Java反射 VS Apache BeanUtils VS Spring BeanUtils
- Spring中BeanUtils的用法
- java反射-BeanUtils.getFieldValue(object,property)
- Java反射的工具类BeanUtils
- Java的反射,内省,beanUtils工具
- java中BeanUtils的概述
- (12)java 反射 ----- 反射相关
- Spring中BeanUtils.copyProperties方法测试
- Java反射相关问题
- Java反射相关
- java 反射相关
- Java 反射相关
- java反射相关
- java 反射method 相关
- java反射相关知识
- 牌 九 出 老 千 秘 诀【加威-信13535353835】牌 九 出 老 千 秘 诀
- 牌 九 搞 鬼 方 法 大 全【加威-信13535353835】牌 九 搞 鬼 方 法 大 全
- 牌 九 搞 鬼 手 段【加威-信13535353835】牌 九 搞 鬼 手 段
- 牌 九 如 何 简 单 出 千【加威-信13535353835】牌 九 如 何 简 单 出 千
- hdu-1429-胜利大逃亡(续)-bfs+状态压缩
- Spring中反射相关 BeanUtils.java 源码
- 牌 九 偷 牌 技 巧【加威-信13535353835】牌 九 偷 牌 技 巧
- 杭 州 东 京 牌 具【加威-信13535353835】杭 州 东 京 牌 具
- 杭 州 胖 子 牌 具【加威-信13535353835】杭 州 胖 子 牌 具
- 《Hadoop基础教程》之初识Hadoop
- 广 州 千 奇 牌 具【加威-信13535353835】广 州 千 奇 牌 具
- 硬件编码工具简介
- 广 州 安 信 牌 具【加威-信13535353835】广 州 安 信 牌 具
- pandoc格式转换示例