1. 程式人生 > >Spring原始碼解讀之核心容器下節

Spring原始碼解讀之核心容器下節

上一篇我們通過ClassPathXmlApplicationContext載入xml檔案,通過BeanFactory獲取例項bean的demo程式碼去解讀了Spring Core Container中的spring-beans,spring-core,spring-context三個元件之間的一些具體類的實現。從載入XML、構造BeanFactory、到初始化Bean,已經有了一個全貌的瞭解。今天繼續前一節,我們來說一下註解方式是如何實現bean是如何實現自動化裝配和依賴載入的。

註解demo

package com.ckmike.dao.impl;

import com.ckmike.beans.Employee;
import com.ckmike.dao.EmployeeMapper;
import org.springframework.stereotype.Repository;

/**
 * EmployeeDao 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 下午12:27
 * @copyright ckmike
 **/
@Repository(value = "employeeDao")
public class EmployeeDao implements EmployeeMapper {
    public Employee getEmployeeById() {
        return new Employee();
    }
}

package com.ckmike.service.impl;

import com.ckmike.beans.Employee;
import com.ckmike.dao.EmployeeMapper;
import com.ckmike.service.EmployeeService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * EmployeeServiceImpl 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 上午10:38
 * @copyright ckmike
 **/
@Service(value = "employeeService")
public class EmployeeServiceImpl implements EmployeeService {

    @Resource(name = "employeeDao")
    private EmployeeMapper employeeMapper;

    public Employee getEmployee() {
        return employeeMapper.getEmployeeById();
    }
}

import com.ckmike.beans.Employee;
import com.ckmike.config.SpringConfig;
import com.ckmike.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4Cla***unner;

import javax.annotation.Resource;

/**
 * SpringCoreAnnotationTest 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 下午12:19
 * @copyright ckmike
 **/
@RunWith(value = SpringJUnit4Cla***unner.class)
@ContextConfiguration(classes={SpringConfig.class})
public class SpringCoreAnnotationTest {

    @Resource(name="employeeService")
    private EmployeeService employeeService;

    @Test
    public void annotationTest() {
        Employee employee = employeeService.getEmployee();
        System.out.println(employee.toString());
    }

}

pom.xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>Spring-Core-Demo</groupId>
    <artifactId>com.ckmike.springcore.demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Spring的核心模組 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <!-- Beans模組 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <!-- 生命週期管理 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <!-- SPel模組 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Spring原始碼解讀之核心容器下節
這種方式是實際開發測試的流程,一般我們在Spring專案中會使用註解進行bean註冊,這種方式和XML方式有什麼不同呢?那種方式比較好呢?
註解方式:方便開發和使用,只要通過註解自動注入物件,解放了開發使用者。但是對於維護而言,各個註解是散落在各個類上的,如果你需要知道整個專案中bean的全貌,那麼你需要頻繁的切換到各個類。
XML管理方式:把所有的bean管理聚集在一起,維護查詢起來非常方便。但是對於開發和使用者需要一起維護這個XML檔案,對於開發使用者不太友好。
綜合考慮:一般我們開發一個專案都會約定一些規則與共識,所以這兩種方式都有存在的意義,具體選擇結合專案人員、專案場景做出考量即可。

自動裝配

從上面的註解方式自動裝配方式,我們看@ContextConfiguration(classes={SpringConfig.class}),它依然是通過初始化Context來初始化Bean容器,只是這個時候我們不是從xml初始容器,而是通過掃描註解的方式自動註冊bean到容器中。我們看下SpringConfig這個類做了什麼:

/**
 * SpringConfig 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 下午12:34
 * @copyright ckmike
 **/
@Configuration
@ComponentScan(basePackages={"com.ckmike"})
public class SpringConfig {

}

@ComponentScan指定掃描的根路徑,掃描該路徑下所有的註解,自動註冊到Bean容器當中。我們繼續看下@ComponentScan:

/*
 * Copyright 2002-2016 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.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.type.filter.TypeFilter;

/**
 * Configures component scanning directives for use with @{@link Configuration} classes.
 * Provides support parallel with Spring XML's {@code <context:component-scan>} element.
 *
 * <p>Either {@link #basePackageClasses} or {@link #basePackages} (or its alias
 * {@link #value}) may be specified to define specific packages to scan. If specific
 * packages are not defined, scanning will occur from the package of the
 * class that declares this annotation.
 *
 * <p>Note that the {@code <context:component-scan>} element has an
 * {@code annotation-config} attribute; however, this annotation does not. This is because
 * in almost all cases when using {@code @ComponentScan}, default annotation config
 * processing (e.g. processing {@code @Autowired} and friends) is assumed. Furthermore,
 * when using {@link AnnotationConfigApplicationContext}, annotation config processors are
 * always registered, meaning that any attempt to disable them at the
 * {@code @ComponentScan} level would be ignored.
 *
 * <p>See {@link Configuration @Configuration}'s Javadoc for usage examples.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.1
 * @see Configuration
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

    /**
     * Alias for {@link #basePackages}.
     * <p>Allows for more concise annotation declarations if no other attributes
     * are needed &mdash; for example, {@code @ComponentScan("org.my.pkg")}
     * instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
     */
    @AliasFor("basePackages")
    String[] value() default {};

    /**
     * Base packages to scan for annotated components.
     * <p>{@link #value} is an alias for (and mutually exclusive with) this
     * attribute.
     * <p>Use {@link #basePackageClasses} for a type-safe alternative to
     * String-based package names.
     */
    @AliasFor("value")
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages} for specifying the packages
     * to scan for annotated components. The package of each class specified will be scanned.
     * <p>Consider creating a special no-op marker class or interface in each package
     * that serves no purpose other than being referenced by this attribute.
     */
    Class<?>[] basePackageClasses() default {};

    /**
     * The {@link BeanNameGenerator} class to be used for naming detected components
     * within the Spring container.
     * <p>The default value of the {@link BeanNameGenerator} interface itself indicates
     * that the scanner used to process this {@code @ComponentScan} annotation should
     * use its inherited bean name generator, e.g. the default
     * {@link AnnotationBeanNameGenerator} or any custom instance supplied to the
     * application context at bootstrap time.
     * @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
     */
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    /**
     * The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
     */
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    /**
     * Indicates whether proxies should be generated for detected components, which may be
     * necessary when using scopes in a proxy-style fashion.
     * <p>The default is defer to the default behavior of the component scanner used to
     * execute the actual scan.
     * <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
     * @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
     */
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    /**
     * Controls the class files eligible for component detection.
     * <p>Consider use of {@link #includeFilters} and {@link #excludeFilters}
     * for a more flexible approach.
     */
    String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

    /**
     * Indicates whether automatic detection of classes annotated with {@code @Component}
     * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
     */
    boolean useDefaultFilters() default true;

    /**
     * Specifies which types are eligible for component scanning.
     * <p>Further narrows the set of candidate components from everything in {@link #basePackages}
     * to everything in the base packages that matches the given filter or filters.
     * <p>Note that these filters will be applied in addition to the default filters, if specified.
     * Any type under the specified base packages which matches a given filter will be included,
     * even if it does not match the default filters (i.e. is not annotated with {@code @Component}).
     * @see #resourcePattern()
     * @see #useDefaultFilters()
     */
    Filter[] includeFilters() default {};

    /**
     * Specifies which types are not eligible for component scanning.
     * @see #resourcePattern
     */
    Filter[] excludeFilters() default {};

    /**
     * Specify whether scanned beans should be registered for lazy initialization.
     * <p>Default is {@code false}; switch this to {@code true} when desired.
     * @since 4.1
     */
    boolean lazyInit() default false;

    /**
     * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
     * include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    @interface Filter {

        /**
         * The type of filter to use.
         * <p>Default is {@link FilterType#ANNOTATION}.
         * @see #classes
         * @see #pattern
         */
        FilterType type() default FilterType.ANNOTATION;

        /**
         * Alias for {@link #classes}.
         * @see #classes
         */
        @AliasFor("classes")
        Class<?>[] value() default {};

        /**
         * The class or classes to use as the filter.
         * <p>The following table explains how the classes will be interpreted
         * based on the configured value of the {@link #type} attribute.
         * <table border="1">
         * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
         * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
         * <td>the annotation itself</td></tr>
         * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
         * <td>the type that detected components should be assignable to</td></tr>
         * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
         * <td>an implementation of {@link TypeFilter}</td></tr>
         * </table>
         * <p>When multiple classes are specified, <em>OR</em> logic is applied
         * &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
         * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
         * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
         * their respective methods will be called prior to {@link TypeFilter#match match}:
         * <ul>
         * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
         * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
         * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
         * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
         * </ul>
         * <p>Specifying zero classes is permitted but will have no effect on component
         * scanning.
         * @since 4.2
         * @see #value
         * @see #type
         */
        @AliasFor("value")
        Class<?>[] classes() default {};

        /**
         * The pattern (or patterns) to use for the filter, as an alternative
         * to specifying a Class {@link #value}.
         * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
         * this is an AspectJ type pattern expression. If {@link #type} is
         * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
         * for the fully-qualified class names to match.
         * @see #type
         * @see #classes
         */
        String[] pattern() default {};

    }

}

如果不指定路徑,那麼就從classpath下面去掃描,lazyInit預設false,預設是通過BeanNameGenerator去生成bean,然後放入到容器中,其中還有scopedProxy,我們先來看看BeanNameGenerator介面的實現類:

/*
 * Copyright 2002-2017 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.context.annotation;

import java.beans.Introspector;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * {@link org.springframework.beans.factory.support.BeanNameGenerator}
 * implementation for bean classes annotated with the
 * {@link org.springframework.stereotype.Component @Component} annotation
 * or with another annotation that is itself annotated with
 * {@link org.springframework.stereotype.Component @Component} as a
 * meta-annotation. For example, Spring's stereotype annotations (such as
 * {@link org.springframework.stereotype.Repository @Repository}) are
 * themselves annotated with
 * {@link org.springframework.stereotype.Component @Component}.
 *
 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
 * JSR-330's {@link javax.inject.Named} annotations, if available. Note that
 * Spring component annotations always override such standard annotations.
 *
 * <p>If the annotation's value doesn't indicate a bean name, an appropriate
 * name will be built based on the short name of the class (with the first
 * letter lower-cased). For example:
 *
 * <pre class="code">com.xyz.FooServiceImpl -&gt; fooServiceImpl</pre>
 *
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @since 2.5
 * @see org.springframework.stereotype.Component#value()
 * @see org.springframework.stereotype.Repository#value()
 * @see org.springframework.stereotype.Service#value()
 * @see org.springframework.stereotype.Controller#value()
 * @see javax.inject.Named#value()
 */
public class AnnotationBeanNameGenerator implements BeanNameGenerator {

    private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
    }

    /**
     * Derive a bean name from one of the annotations on the class.
     * @param annotatedDef the annotation-aware bean definition
     * @return the bean name, or {@code null} if none is found
     */
    @Nullable
    protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
        AnnotationMetadata amd = annotatedDef.getMetadata();
        Set<String> types = amd.getAnnotationTypes();
        String beanName = null;
        for (String type : types) {
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
            if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String) value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                    "component names: '" + beanName + "' versus '" + strVal + "'");
                        }
                        beanName = strVal;
                    }
                }
            }
        }
        return beanName;
    }

    /**
     * Check whether the given annotation is a stereotype that is allowed
     * to suggest a component name through its annotation {@code value()}.
     * @param annotationType the name of the annotation class to check
     * @param metaAnnotationTypes the names of meta-annotations on the given annotation
     * @param attributes the map of attributes for the given annotation
     * @return whether the annotation qualifies as a stereotype with component name
     */
    protected boolean isStereotypeWithNameValue(String annotationType,
            Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {

        boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
                metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
                annotationType.equals("javax.annotation.ManagedBean") ||
                annotationType.equals("javax.inject.Named");

        return (isStereotype && attributes != null && attributes.containsKey("value"));
    }

    /**
     * Derive a default bean name from the given bean definition.
     * <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.
     * @param definition the bean definition to build a bean name for
     * @param registry the registry that the given bean definition is being registered with
     * @return the default bean name (never {@code null})
     */
    protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return buildDefaultBeanName(definition);
    }

    /**
     * Derive a default bean name from the given bean definition.
     * <p>The default implementation simply builds a decapitalized version
     * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
     * <p>Note that inner classes will thus have names of the form
     * "outerClassName.InnerClassName", which because of the period in the
     * name may be an issue if you are autowiring by name.
     * @param definition the bean definition to build a bean name for
     * @return the default bean name (never {@code null})
     */
    protected String buildDefaultBeanName(BeanDefinition definition) {
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "No bean class name set");
        String shortClassName = ClassUtils.getShortName(beanClassName);
        return Introspector.decapitalize(shortClassName);
    }

}

從這裡我們就很容易發現,他是通過註解(@Controller,@Service,@Component,@Repository)掃描value生成beanName並且要求是唯一的,這樣我們在呼叫註解注入的時候也就可以自動的獲取到bean例項,實現自動裝配了。
我們再來看下ScopedProxyMode

package org.springframework.context.annotation;

/**
 * Enumerates the various scoped-proxy options.
 *
 * <p>For a more complete discussion of exactly what a scoped proxy is, see the
 * section of the Spring reference documentation entitled '<em>Scoped beans as
 * dependencies</em>'.
 *
 * @author Mark Fisher
 * @since 2.5
 * @see ScopeMetadata
 */
public enum ScopedProxyMode {

    /**
     * Default typically equals {@link #NO}, unless a different default
     * has been configured at the component-scan instruction level.
     */
    DEFAULT,

    /**
     * Do not create a scoped proxy.
     * <p>This proxy-mode is not typically useful when used with a
     * non-singleton scoped instance, which should favor the use of the
     * {@link #INTERFACES} or {@link #TARGET_CLASS} proxy-modes instead if it
     * is to be used as a dependency.
     */
    NO,

    /**
     * Create a JDK dynamic proxy implementing <i>all</i> interfaces exposed by
     * the class of the target object.
     */
    INTERFACES,

    /**
     * Create a class-based proxy (uses CGLIB).
     */
    TARGET_CLASS;

}

這是一個列舉類,可選值JDK動態代理、CGlib動態代理,或者NO,預設是default。每一種值的說明也非常清晰說明了。

說道這裡我們其實就已經很大致清楚註解注入,自動裝配的這個過程了。通過掃描註解value生成唯一BeanName,註冊Bean,初始化Context容器,然後在使用的地方通過註解注入指定 name的Bean。

@Autowired與@Resource

spring不但支援自己定義的@Autowired註解,還支援幾個由JSR-250規範定義的註解,它們分別是@Resource、@PostConstruct以及@PreDestroy。通常我們實際上不會使用@Resource註解進行注入,通常都是通過@Autowired進自動裝配,那麼他們有什麼區別呢?

@Resource是JAVA在javax.annotation下提供的註解。
@Autowired是Spring-beans下org.springframework.beans.factory.annotation提供的自定義註解。

我們先來看下Resource和Autowired:

package javax.annotation;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

/**
 * The Resource annotation marks a resource that is needed
 * by the application.  This annotation may be applied to an
 * application component class, or to fields or methods of the
 * component class.  When the annotation is applied to a
 * field or method, the container will inject an instance
 * of the requested resource into the application component
 * when the component is initialized.  If the annotation is
 * applied to the component class, the annotation declares a
 * resource that the application will look up at runtime. <p>
 *
 * Even though this annotation is not marked Inherited, deployment
 * tools are required to examine all superclasses of any component
 * class to discover all uses of this annotation in all superclasses.
 * All such annotation instances specify resources that are needed
 * by the application component.  Note that this annotation may
 * appear on private fields and methods of superclasses; the container
 * is required to perform injection in these cases as well.
 *
 * @since Common Annotations 1.0
 */
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    /**
     * The JNDI name of the resource.  For field annotations,
     * the default is the field name.  For method annotations,
     * the default is the JavaBeans property name corresponding
     * to the method.  For class annotations, there is no default
     * and this must be specified.
     */
    String name() default "";

    /**
     * The name of the resource that the reference points to. It can
     * link to any compatible resource using the global JNDI names.
     *
     * @since Common Annotations 1.1
     */

    String lookup() default "";

    /**
     * The Java type of the resource.  For field annotations,
     * the default is the type of the field.  For method annotations,
     * the default is the type of the JavaBeans property.
     * For class annotations, there is no default and this must be
     * specified.
     */
    Class<?> type() default java.lang.Object.class;

    /**
     * The two possible authentication types for a resource.
     */
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }

    /**
     * The authentication type to use for this resource.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    /**
     * Indicates whether this resource can be shared between
     * this component and other components.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    boolean shareable() default true;

    /**
     * A product specific name that this resource should be mapped to.
     * The name of this resource, as defined by the <code>name</code>
     * element or defaulted, is a name that is local to the application
     * component using the resource.  (It's a name in the JNDI
     * <code>java:comp/env</code> namespace.)  Many application servers
     * provide a way to map these local names to names of resources
     * known to the application server.  This mapped name is often a
     * <i>global</i> JNDI name, but may be a name of any form. <p>
     *
     * Application servers are not required to support any particular
     * form or type of mapped name, nor the ability to use mapped names.
     * The mapped name is product-dependent and often installation-dependent.
     * No use of a mapped name is portable.
     */
    String mappedName() default "";

    /**
     * Description of this resource.  The description is expected
     * to be in the default language of the system on which the
     * application is deployed.  The description can be presented
     * to the Deployer to help in choosing the correct resource.
     */
    String description() default "";
}

@Autowired

/*
 * Copyright 2002-2018 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.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Marks a constructor, field, setter method or config method as to be autowired by
 * Spring's dependency injection facilities. This is an alternative to the JSR-330
 * {@link javax.inject.Inject} annotation, adding required-vs-optional semantics.
 *
 * <p>Only one constructor (at max) of any given bean class may declare this annotation
 * with the 'required' parameter set to {@code true}, indicating <i>the</i> constructor
 * to autowire when used as a Spring bean. If multiple <i>non-required</i> constructors
 * declare 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 primary/default constructor (if present) will be used.
 * If a class only declares a single constructor to begin with, it will always be used,
 * even if not annotated. An annotated constructor does not have to be public.
 *
 * <p>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.
 *
 * <p>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. Such config methods do not have to be public.
 *
 * <p>In the case of a multi-arg constructor or method, the 'required' parameter is
 * applicable to all arguments. Individual parameters may be declared as Java-8-style
 * {@link java.util.Optional} or, as of Spring Framework 5.0, also as {@code @Nullable}
 * or a not-null parameter type in Kotlin, overriding the base required semantics.
 *
 * <p>In case of a {@link java.util.Collection} or {@link java.util.Map} dependency type,
 * the container autowires all beans matching the declared value type. For such purposes,
 * the map keys must be declared as type String which will be resolved to the corresponding
 * bean names. Such a container-provided collection will be ordered, taking into account
 * {@link org.springframework.core.Ordered}/{@link org.springframework.core.annotation.Order}
 * values of the target components, otherwise following their registration order in the
 * container. Alternatively, a single matching target bean may also be a generally typed
 * {@code Collection} or {@code Map} itself, getting injected as such.
 *
 * <p>Note that actual injection is performed through a
 * {@link org.springframework.beans.factory.config.BeanPostProcessor
 * BeanPostProcessor} which in turn means that you <em>cannot</em>
 * use {@code @Autowired} to inject references into
 * {@link org.springframework.beans.factory.config.BeanPostProcessor
 * BeanPostProcessor} or
 * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
 * types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
 * class (which, by default, checks for the presence of this annotation).
 *
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @since 2.5
 * @see AutowiredAnnotationBeanPostProcessor
 * @see Qualifier
 * @see Value
 */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

    /**
     * Declares whether the annotated dependency is required.
     * <p>Defaults to {@code true}.
     */
    boolean required() default true;

}

我們可以新增一個EmployeeDao2進行測試,分表使用@Autowired、@Resource注入EmployeeServiceImpl中測試。

package com.ckmike.dao.impl;

import com.ckmike.beans.Employee;
import com.ckmike.dao.EmployeeMapper;
import org.springframework.stereotype.Repository;

/**
 * EmployeeDao2 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 下午4:15
 * @copyright ckmike
 **/
@Repository(value = "employeeDao2")
public class EmployeeDao2 implements EmployeeMapper {
    public Employee getEmployeeById() {
        System.out.println("EmployeeDao2");
        return new Employee();
    }
}

package com.ckmike.service.impl;

import com.ckmike.beans.Employee;
import com.ckmike.dao.EmployeeMapper;
import com.ckmike.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * EmployeeServiceImpl 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 上午10:38
 * @copyright ckmike
 **/
@Service(value = "employeeService")
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    public Employee getEmployee() {
        return employeeMapper.getEmployeeById();
    }
}

import com.ckmike.beans.Employee;
import com.ckmike.config.SpringConfig;
import com.ckmike.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4Cla***unner;

import javax.annotation.Resource;

/**
 * SpringCoreAnnotationTest 簡要描述
 * <p> TODO:描述該類職責 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-15 下午12:19
 * @copyright ckmike
 **/
@RunWith(value = SpringJUnit4Cla***unner.class)
@ContextConfiguration(classes={SpringConfig.class})
public class SpringCoreAnnotationTest {

    @Resource(name = "employeeService")
    private EmployeeService employeeService;

    @Test
    public void annotationTest() {
        Employee employee = employeeService.getEmployee();
        System.out.println(employee.toString());
    }

}

Spring原始碼解讀之核心容器下節
這個時候我們發現報錯了:'com.ckmike.dao.EmployeeMapper' available: expected single matching bean but found 2: employeeDao,employeeDao2。發現有兩個實現了EmployeeMapper的bean,都符合型別匹配,這個時候報錯了。如果使用@Resource不指定名字也一樣會找到兩個類匹配的bean,@Resource可以通過name來指定,而@Autowired則需要結合@Qualifier(value = "employeeDao")指定bean.

總結:
@Resource ,它預設是byName來匹配尋找bean的,@Resource有兩個屬性是比較重要的,分是name和type,Spring將@Resource註解的name屬性解析為bean的名字,而type屬性則解析為bean的型別。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略。
@Resource裝配順序

  1. 如果同時指定了name和type,則從Spring context中找到唯一匹配的bean進行裝配,找不到則丟擲異常。
  2. 如果指定了name,則從context中查詢名稱(id)匹配的bean進行裝配,找不到則丟擲異常。
  3. 如果指定了type,則從context中找到型別匹配的唯一bean進行裝配,找不到或者找到多個,都會丟擲異常。
  4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始型別進行匹配,如果匹配則自動裝配,否則丟擲異常。

@Autowired是通過byType進行裝配的,如果沒有找到或者找到多個bean會丟擲異常,如果可以為null,那麼需要設定require = false,預設情況是不能為null的。可以結合@Qualifier(value = "employeeDao")與@Resource就相似了。

相同點:兩者都可以放置在成員屬性上,setter方法上。

那麼到這裡我們對Spring Core Container的XMl方式、註解方式都說完了,看一遍肯定是很難完全掌握的,我們需要多看多寫多總結,最終你一定會慢慢熟悉並掌握這部分內容的。

其實自動註解裝配是得益於JAVA註解機制的,更多知識請檢視JAVA註解機制,學以致用才是學習的王道。打完收工。後續我還會繼續閱讀AOP的實現程式碼,和大家一起分享底層實現原理。上面有哪裡不對的地方希望你能留言告訴我,我會第一時間修改,以防止誤導大家,謝謝。