续
上一篇我们通过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项目中会使用注解进行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 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.
*
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)
@Documented
public @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());
}
}
这个时候我们发现报错了:'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装配顺序
- 如果同时指定了name和type,则从Spring context中找到唯一匹配的bean进行装配,找不到则抛出异常。
- 如果指定了name,则从context中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定了type,则从context中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常。
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配,否则抛出异常。
@Autowired是通过byType进行装配的,如果没有找到或者找到多个bean会抛出异常,如果可以为null,那么需要设置require = false,默认情况是不能为null的。可以结合@Qualifier(value = "employeeDao")与@Resource就相似了。
相同点:两者都可以放置在成员属性上,setter方法上。
那么到这里我们对Spring Core Container的XMl方式、注解方式都说完了,看一遍肯定是很难完全掌握的,我们需要多看多写多总结,最终你一定会慢慢熟悉并掌握这部分内容的。
其实自动注解装配是得益于JAVA注解机制的,更多知识请查看JAVA注解机制,学以致用才是学习的王道。打完收工。后续我还会继续阅读AOP的实现代码,和大家一起分享底层实现原理。上面有哪里不对的地方希望你能留言告诉我,我会第一时间修改,以防止误导大家,谢谢。