博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring源码分析之@Conditional
阅读量:6184 次
发布时间:2019-06-21

本文共 27305 字,大约阅读时间需要 91 分钟。

根源在AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContext,以AnnotationConfigApplicationContext为例:

1.构造方法

/**     * Create a new AnnotationConfigApplicationContext, deriving bean definitions     * from the given annotated classes and automatically refreshing the context.     * @param annotatedClasses one or more annotated classes,     * e.g. {
@link Configuration @Configuration} classes */ public AnnotationConfigApplicationContext(Class
... annotatedClasses) { this(); register(annotatedClasses); refresh(); } /** * Create a new AnnotationConfigApplicationContext, scanning for bean definitions * in the given packages and automatically refreshing the context. * @param basePackages the packages to check for annotated classes */ public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }

一种是注解类方式,一种是扫描方式,殊途同归。以注解类来分析:

/**     * Register one or more annotated classes to be processed.     * 

Note that {

@link #refresh()} must be called in order for the context * to fully process the new classes. * @param annotatedClasses one or more annotated classes, * e.g. {
@link Configuration @Configuration} classes * @see #scan(String...) * @see #refresh() */ public void register(Class
... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); }

2. 实现方法AnnotatedBeanDefinitionReader

public void register(Class
... annotatedClasses) { for (Class
annotatedClass : annotatedClasses) { registerBean(annotatedClass); } }

具体逻辑还是在该类内部:

@SuppressWarnings("unchecked")    public void registerBean(Class
annotatedClass, Class
... qualifiers) { registerBean(annotatedClass, null, qualifiers); } @SuppressWarnings("unchecked") public void registerBean(Class
annotatedClass, String name, Class
... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); if (qualifiers != null) { for (Class
qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }

3.分析逻辑

3.1 ConditionEvaluator

/**     * Determine if an item should be skipped based on {
@code @Conditional} annotations. * @param metadata the meta data * @param phase the phase of the call * @return if the item should be skipped */ public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List
conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if (requiredPhase == null || requiredPhase == phase) { if (!condition.matches(this.context, metadata)) { return true; } } }

3.2 大鱼AnnotationConfigUtils来了

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);        if (qualifiers != null) {            for (Class
qualifier : qualifiers) { if (Primary.class == qualifier) { abd.setPrimary(true); } else if (Lazy.class == qualifier) { abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } }

通用注解(lazy、primary、DependsOn、role、description)实现:

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {        if (metadata.isAnnotated(Lazy.class.getName())) {            abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));        }        else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {            abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));        }        if (metadata.isAnnotated(Primary.class.getName())) {            abd.setPrimary(true);        }        if (metadata.isAnnotated(DependsOn.class.getName())) {            abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));        }        if (abd instanceof AbstractBeanDefinition) {            AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;            if (metadata.isAnnotated(Role.class.getName())) {                absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());            }            if (metadata.isAnnotated(Description.class.getName())) {                absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));            }        }    }

3.3 钓上大鱼,处理注解的相关processor在这里注册:

/**     * Register all relevant annotation post processors in the given registry.     * @param registry the registry to operate on     * @param source the configuration source element (already extracted)     * that this registration was triggered from. May be {
@code null}. * @return a Set of BeanDefinitionHolders, containing all bean definitions * that have actually been registered by this call */ public static Set
registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set
beanDefs = new LinkedHashSet<>(4); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; } private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(beanName, definition); return new BeanDefinitionHolder(definition, beanName); }

3.3.1 ConfigurationClassPostProcessor

/** * {
@link BeanFactoryPostProcessor} used for bootstrapping processing of * {
@link Configuration @Configuration} classes. * *

Registered by default when using {

@code
} or * {
@code
}. Otherwise, may be declared manually as * with any other BeanFactoryPostProcessor. * *

This post processor is {

@link Ordered#HIGHEST_PRECEDENCE} as it is important * that any {
@link Bean} methods declared in Configuration classes have their * respective bean definitions registered before any other BeanFactoryPostProcessor * executes. * * @author Chris Beams * @author Juergen Hoeller * @author Phillip Webb * @since 3.0 */public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {}

3.3.2 AutowiredAnnotationBeanPostProcessor

/** * {
@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that autowires annotated fields, setter methods and arbitrary config methods. * Such members to be injected are detected through a Java 5 annotation: by default, * Spring's {
@link Autowired @Autowired} and {
@link Value @Value} annotations. * *

Also supports JSR-330's {

@link javax.inject.Inject @Inject} annotation, * if available, as a direct alternative to Spring's own {
@code @Autowired}. * *

Only one constructor (at max) of any given bean class may carry this * annotation with the 'required' parameter set to {

@code true}, * indicating the constructor to autowire when used as a Spring bean. * If multiple non-required constructors carry the annotation, they * will be considered as candidates for autowiring. The constructor with * the greatest number of dependencies that can be satisfied by matching * beans in the Spring container will be chosen. If none of the candidates * can be satisfied, then a default constructor (if present) will be used. * An annotated constructor does not have to be public. * *

Fields are injected right after construction of a bean, before any * config methods are invoked. Such a config field does not have to be public. * *

Config methods may have an arbitrary name and any number of arguments; each of * those arguments will be autowired with a matching bean in the Spring container. * Bean property setter methods are effectively just a special case of such a * general config method. Config methods do not have to be public. * *

Note: A default AutowiredAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom AutowiredAnnotationBeanPostProcessor bean definition. *

NOTE: Annotation injection will be performed before XML injection; * thus the latter configuration will override the former for properties wired through * both approaches. * *

In addition to regular injection points as discussed above, this post-processor * also handles Spring's {

@link Lookup @Lookup} annotation which identifies lookup * methods to be replaced by the container at runtime. This is essentially a type-safe * version of {
@code getBean(Class, args)} and {
@code getBean(String, args)}, * See {
@link Lookup @Lookup's javadoc} for details. * * @author Juergen Hoeller * @author Mark Fisher * @author Stephane Nicoll * @since 2.5 * @see #setAutowiredAnnotationType * @see Autowired * @see Value */public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {}

3.3.3 RequiredAnnotationBeanPostProcessor

/** * {
@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that enforces required JavaBean properties to have been configured. * Required bean properties are detected through a Java 5 annotation: * by default, Spring's {
@link Required} annotation. * *

The motivation for the existence of this BeanPostProcessor is to allow * developers to annotate the setter properties of their own classes with an * arbitrary JDK 1.5 annotation to indicate that the container must check * for the configuration of a dependency injected value. This neatly pushes * responsibility for such checking onto the container (where it arguably belongs), * and obviates the need (in part) for a developer to code a method that * simply checks that all required properties have actually been set. * *

Please note that an 'init' method may still need to implemented (and may * still be desirable), because all that this class does is enforce that a * 'required' property has actually been configured with a value. It does * not check anything else... In particular, it does not check that a * configured value is not {

@code null}. * *

Note: A default RequiredAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom RequiredAnnotationBeanPostProcessor bean definition. * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 * @see #setRequiredAnnotationType * @see Required */public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {}

3.3.4 CommonAnnotationBeanPostProcessor

/** * {
@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that supports common Java annotations out of the box, in particular the JSR-250 * annotations in the {
@code javax.annotation} package. These common Java * annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2), * as well as in Java 6's JAX-WS. * *

This post-processor includes support for the {

@link javax.annotation.PostConstruct} * and {
@link javax.annotation.PreDestroy} annotations - as init annotation * and destroy annotation, respectively - through inheriting from * {
@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types. * *

The central element is the {

@link javax.annotation.Resource} annotation * for annotation-driven injection of named beans, by default from the containing * Spring BeanFactory, with only {
@code mappedName} references resolved in JNDI. * The {
@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups * equivalent to standard Java EE 5 resource injection for {
@code name} references * and default names as well. The target beans can be simple POJOs, with no special * requirements other than the type having to match. * *

The JAX-WS {

@link javax.xml.ws.WebServiceRef} annotation is supported too, * analogous to {
@link javax.annotation.Resource} but with the capability of creating * specific JAX-WS service endpoints. This may either point to an explicitly defined * resource by name or operate on a locally specified JAX-WS service class. Finally, * this post-processor also supports the EJB 3 {
@link javax.ejb.EJB} annotation, * analogous to {
@link javax.annotation.Resource} as well, with the capability to * specify both a local bean name and a global JNDI name for fallback retrieval. * The target beans can be plain POJOs as well as EJB 3 Session Beans in this case. * *

The common annotations supported by this post-processor are available in * Java 6 (JDK 1.6) as well as in Java EE 5/6 (which provides a standalone jar for * its common annotations as well, allowing for use in any Java 5 based application). * *

For default usage, resolving resource names as Spring bean names, * simply define the following in your application context: * *

 * <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>
* * For direct JNDI access, resolving resource names as JNDI resource references * within the Java EE application's "java:comp/env/" namespace, use the following: * *
 * <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"> *   <property name="alwaysUseJndiLookup" value="true"/> * </bean>
* * {
@code mappedName} references will always be resolved in JNDI, * allowing for global JNDI names (including "java:" prefix) as well. The * "alwaysUseJndiLookup" flag just affects {
@code name} references and * default names (inferred from the field name / property name). * *

NOTE: A default CommonAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom CommonAnnotationBeanPostProcessor bean definition! *

NOTE: Annotation injection will be performed before XML injection; thus * the latter configuration will override the former for properties wired through * both approaches. * * @author Juergen Hoeller * @since 2.5 * @see #setAlwaysUseJndiLookup * @see #setResourceFactory * @see org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor */@SuppressWarnings("serial")public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {}

3.3.5 PersistenceAnnotationBeanPostProcessor

/** * BeanPostProcessor that processes {
@link javax.persistence.PersistenceUnit} * and {
@link javax.persistence.PersistenceContext} annotations, for injection of * the corresponding JPA resources {
@link javax.persistence.EntityManagerFactory} * and {
@link javax.persistence.EntityManager}. Any such annotated fields or methods * in any Spring-managed object will automatically be injected. * *

This post-processor will inject sub-interfaces of {

@code EntityManagerFactory} * and {
@code EntityManager} if the annotated fields or methods are declared as such. * The actual type will be verified early, with the exception of a shared ("transactional") * {
@code EntityManager} reference, where type mismatches might be detected as late * as on the first actual invocation. * *

Note: In the present implementation, PersistenceAnnotationBeanPostProcessor * only supports {

@code @PersistenceUnit} and {
@code @PersistenceContext} * with the "unitName" attribute, or no attribute at all (for the default unit). * If those annotations are present with the "name" attribute at the class level, * they will simply be ignored, since those only serve as deployment hint * (as per the Java EE specification). * *

This post-processor can either obtain EntityManagerFactory beans defined * in the Spring application context (the default), or obtain EntityManagerFactory * references from JNDI ("persistence unit references"). In the bean case, * the persistence unit name will be matched against the actual deployed unit, * with the bean name used as fallback unit name if no deployed name found. * Typically, Spring's {

@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} * will be used for setting up such EntityManagerFactory beans. Alternatively, * such beans may also be obtained from JNDI, e.g. using the {
@code jee:jndi-lookup} * XML configuration element (with the bean name matching the requested unit name). * In both cases, the post-processor definition will look as simple as this: * *

 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
* * In the JNDI case, specify the corresponding JNDI names in this post-processor's * {
@link #setPersistenceUnits "persistenceUnits" map}, typically with matching * {
@code persistence-unit-ref} entries in the Java EE deployment descriptor. * By default, those names are considered as resource references (according to the * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace. * For example: * *
 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> *   <property name="persistenceUnits"> *     <map/gt; *       <entry key="unit1" value="persistence/unit1"/> *       <entry key="unit2" value="persistence/unit2"/> *     </map/gt; *   </property> * </bean>
* * In this case, the specified persistence units will always be resolved in JNDI * rather than as Spring-defined beans. The entire persistence unit deployment, * including the weaving of persistent classes, is then up to the Java EE server. * Persistence contexts (i.e. EntityManager references) will be built based on * those server-provided EntityManagerFactory references, using Spring's own * transaction synchronization facilities for transactional EntityManager handling * (typically with Spring's {
@code @Transactional} annotation for demarcation * and {
@link org.springframework.transaction.jta.JtaTransactionManager} as backend). * *

If you prefer the Java EE server's own EntityManager handling, specify entries * in this post-processor's {

@link #setPersistenceContexts "persistenceContexts" map} * (or {
@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map}, * typically with matching {
@code persistence-context-ref} entries in the * Java EE deployment descriptor. For example: * *

 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> *   <property name="persistenceContexts"> *     <map/gt; *       <entry key="unit1" value="persistence/context1"/> *       <entry key="unit2" value="persistence/context2"/> *     </map/gt; *   </property> * </bean>
* * If the application only obtains EntityManager references in the first place, * this is all you need to specify. If you need EntityManagerFactory references * as well, specify entries for both "persistenceUnits" and "persistenceContexts", * pointing to matching JNDI locations. * *

NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans, * i.e. do not use {

@code @PersistenceContext} with type {
@code EXTENDED} in * Spring beans defined with scope 'singleton' (Spring's default scope).
* Extended EntityManagers are not thread-safe, hence they must not be used * in concurrently accessed beans (which Spring-managed singletons usually are). * *

Note: A default PersistenceAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition. * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see javax.persistence.PersistenceUnit * @see javax.persistence.PersistenceContext */@SuppressWarnings("serial")public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {

3.3.6 EventListenerMethodProcessor

/** * Register {
@link EventListener} annotated method as individual {
@link ApplicationListener} * instances. * * @author Stephane Nicoll * @author Juergen Hoeller * @since 4.2 */public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware {}

3.3.7 DefaultEventListenerFactory

/** * Default {
@link EventListenerFactory} implementation that supports the * regular {
@link EventListener} annotation. *

Used as "catch-all" implementation by default. * * @author Stephane Nicoll * @since 4.2 */public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {}

扯远了,就此打住!

 

转载地址:http://rdsda.baihongyu.com/

你可能感兴趣的文章
WebRequest和WebResponse, 对指定的URI发出请求以及接收响应(转)
查看>>
codeforces732F Tourist Reform(边双连通分量)
查看>>
windows命令行下简单使用javac、java、javap详细演示
查看>>
空间如何通过伪静态实现301重定向
查看>>
自适应滤波:维纳滤波器——LCMV及MVDR实现
查看>>
Column 'id' in field list is ambiguous
查看>>
判断是否纯IE浏览器 JS
查看>>
BestCoder Round #75
查看>>
Codeforces Round #349
查看>>
最大流增广路(KM算法) HDOJ 1853 Cyclic Tour
查看>>
Vue.js - Day3
查看>>
windows签名证书流程
查看>>
javascript中的数据类型
查看>>
Poemscape|Beta总体规划
查看>>
【WEBAPI】How WebAPI does Parameter Binding
查看>>
12月9日 敏捷之旅上海站“敏捷嘉年华”:来参与,来体验,来学习 转自Shining在letagilefly宣传...
查看>>
监控工具 zxbbix
查看>>
【洛谷4587】 [FJOI2016]神秘数(主席树)
查看>>
java读取properties文件总结
查看>>
Mybatis表关联多对一
查看>>