博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码解读之核心容器下节
阅读量:5901 次
发布时间:2019-06-19

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

上一篇我们通过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 简要描述 * 

TODO:描述该类职责

* * @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 简要描述 *

TODO:描述该类职责

* * @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 简要描述 *

TODO:描述该类职责

* * @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文件

4.0.0
Spring-Core-Demo
com.ckmike.springcore.demo
1.0-SNAPSHOT
org.springframework
spring-core
5.1.3.RELEASE
org.springframework
spring-beans
5.1.3.RELEASE
org.springframework
spring-context
5.1.3.RELEASE
org.springframework
spring-expression
5.1.3.RELEASE
junit
junit
4.12
test
org.springframework
spring-test
5.1.2.RELEASE
test

Spring源码解读之核心容器下节

这种方式是实际开发测试的流程,一般我们在Spring项目中会使用注解进行bean注册,这种方式和XML方式有什么不同呢?那种方式比较好呢?
注解方式:方便开发和使用,只要通过注解自动注入对象,解放了开发使用者。但是对于维护而言,各个注解是散落在各个类上的,如果你需要知道整个项目中bean的全貌,那么你需要频繁的切换到各个类。
XML管理方式:把所有的bean管理聚集在一起,维护查找起来非常方便。但是对于开发和使用者需要一起维护这个XML文件,对于开发使用者不太友好。
综合考虑:一般我们开发一个项目都会约定一些规则与共识,所以这两种方式都有存在的意义,具体选择结合项目人员、项目场景做出考量即可。

自动装配

从上面的注解方式自动装配方式,我们看@ContextConfiguration(classes={SpringConfig.class}),它依然是通过初始化Context来初始化Bean容器,只是这个时候我们不是从xml初始容器,而是通过扫描注解的方式自动注册bean到容器中。我们看下SpringConfig这个类做了什么:

/** * SpringConfig 简要描述 * 

TODO:描述该类职责

* * @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 
} element. * *

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. * *

Note that the {@code

} 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. * *

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}. *

Allows for more concise annotation declarations if no other attributes * are needed — 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. *

{@link #value} is an alias for (and mutually exclusive with) this * attribute. *

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. *

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. *

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

nameGenerator() default BeanNameGenerator.class; /** * The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components. */ Class
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. *

The default is defer to the default behavior of the component scanner used to * execute the actual scan. *

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. *

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. *

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. *

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. *

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. *

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. *

The following table explains how the classes will be interpreted * based on the configured value of the {@link #type} attribute. *

*
*
*
*
*
*
*
*
{@code FilterType} Class Interpreted As
{@link FilterType#ANNOTATION ANNOTATION} the annotation itself
{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE} the type that detected components should be assignable to
{@link FilterType#CUSTOM CUSTOM} an implementation of {@link TypeFilter}
*

When multiple classes are specified, OR logic is applied * — for example, "include types annotated with {@code @Foo} OR {@code @Bar}". *

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}: *

    *
  • {@link org.springframework.context.EnvironmentAware EnvironmentAware}
  • *
  • {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware} *
  • {@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware} *
  • {@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware} *
*

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}. *

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}. * * 

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. * *

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: * *

com.xyz.FooServiceImpl -> fooServiceImpl
* * @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
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
metaAnnotationTypes, @Nullable Map
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. *

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. *

The default implementation simply builds a decapitalized version * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao". *

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. * * 

For a more complete discussion of exactly what a scoped proxy is, see the * section of the Spring reference documentation entitled 'Scoped beans as * dependencies'. * * @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. *

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 all 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. 

* * 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 name * element or defaulted, is a name that is local to the application * component using the resource. (It's a name in the JNDI * java:comp/env 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 * global JNDI name, but may be a name of any form.

* * 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. * * 

Only one constructor (at max) of any given bean class may declare 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 * 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. * *

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. Such config methods do not have to be public. * *

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. * *

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. * *

Note that actual injection is performed through a * {@link org.springframework.beans.factory.config.BeanPostProcessor * BeanPostProcessor} which in turn means that you cannot * 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)@Documentedpublic @interface Autowired { /** * Declares whether the annotated dependency is required. *

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 简要描述 * 

TODO:描述该类职责

* * @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 简要描述 *

TODO:描述该类职责

* * @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 简要描述 *

TODO:描述该类职责

* * @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的实现代码,和大家一起分享底层实现原理。上面有哪里不对的地方希望你能留言告诉我,我会第一时间修改,以防止误导大家,谢谢。

转载于:https://blog.51cto.com/4837471/2330873

你可能感兴趣的文章
linux编程原理之——多线程编程的同步互斥
查看>>
redis的安装
查看>>
java Collection 常用遍历
查看>>
我的友情链接
查看>>
转向全闪存数据中心可行性分析
查看>>
黑客盯上了Google相册漏洞
查看>>
汉诺塔游戏(经典递归)
查看>>
Windows Server 2016-DNS服务端新增和改进功能
查看>>
极速理解设计模式系列:4.原型模式(Prototype Pattern)
查看>>
ASP.net+jQuery+div遮罩
查看>>
EF MVC 在线代码生成器
查看>>
Jfianl学习笔记Model中toString()的重写
查看>>
Liferay 用权限控制视图
查看>>
Composer 报错 proc_open(): fork failed errors#
查看>>
什么是算法
查看>>
android layout_weight
查看>>
serialize提交form数据
查看>>
centos7源码塔建lnmp+redis环境
查看>>
饿了么是如何做技术运营的
查看>>
Enter-PSSession开启远程PS会话失败
查看>>