An overview,
[1] Note usage
According to the source code for the @AutoWired annotation, you can see that the annotation can be applied to constructors, parameters, methods, and properties, all of which get the value of the parameter component from the container
- The annotation on the method: @bean + method parameter, the parameter is taken from the container, the default is not @autowired effect is the same, can be automatically assembled
- The annotation is on the constructor: if there is only one parameter construct on the component, the @autowired of the parameter construct can be omitted, and the component of the parameter position can still be automatically fetched from the container
- Annotation in parameter position
- The annotation is in the property position
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required(a) default true;
}
Copy the code
Related notes:
- Autowired: Assembly by type by default. If we want to use assembly by name, we can use it in conjunction with the @qualifier annotation (provided by Spring).
- Qualifier() : Specifies the assembled bean, which can be used in conjunction with @AutoWired when multiple instances are involved
- @primary: annotated when multiple bean candidates are present during autowiring
@Primary
The bean will be the preferred- @resource: Assembly by name by default, assembly by type if no bean matching the name can be found (not supported
@Primary
and@Autowired(required = false)
Functionality, provided by JDK)- Inject: package that needs to be imported into Javax. Inject, same as Autowired functionality, but not
required=false
Features (provided by the JDK)
[2] Automatic assembly
Spring uses dependency injection (DI) to assign dependencies to individual components in the IOC container
@Autowired
Automatic injection (provided by Spring) :
- Default in accordance with the priority to the container to find the corresponding components: applicationContext. GetBean ()
- If multiple components of the same type are found, the name of the property is used as the component ID to look up the component in the container
- Qualifier() annotation: This annotation specifies the ID of the component to be assembled, rather than using the attribute name
- By default, autowiring must assign a value to a property. If it does not, an error will be reported
@Autowired(required = false)
Specifying that it is not necessary will not generate an error - @primary Annotation: annotated when multiple bean candidates are present during autowiring
@Primary
The bean will be the preferred, otherwise an exception will be thrown if used@Qualifier()
Specify an assembly bean, and use the bean that explicitly specifies the assembly
@Resource(JSR250) and @Inject(JSR330) (provided by JDK)
@ the Resource:
- By default, assembly is performed by component name, or by name
- When no bean matching the name is found, assembly is done by type
- Does not support
@Primary
and@Autowired(required = false)
function - If both name and Type are specified, a unique matching bean is found from the Spring context and assembled, failing which an exception is thrown.
- If name is specified, the bean whose name (ID) matches is searched from the context for assembly, and an exception is thrown if no bean is found.
- If type is specified, an exception is thrown if a unique bean similar to a match is found from the context and assembled.
- If neither name nor type is specified, byName is automatically used for assembly. If there is no match, it falls back to a primitive type for matching, and if there is a match, it is automatically assembled.
@ Inject:
- The package that needs to import Javax. inject is the same as the Autowired function, but not available
required=false
function
[3] The difference between @autoWired and @Resource annotations
- @autoWired is provided by Spring and injected as byType only; @Resource is provided by J2EE and is automatically injected by byName by default. When no bean matching the name is found, it is assembled by type
- By default, the dependent object must be required to exist. If you want to allow a null value, you can set its required property to false. Name assembly can be used with the @qualifier annotation if desired.
- @resource, by default, the assembly is based on the name. The name can be specified by the name attribute. If the name attribute is not specified, when the annotation is written on the field, the name of the field is used by default for name lookup. If the annotation is written on a setter method, it defaults to the property name for assembly. Assemble by type when no bean matching the name can be found. Note, however, that if the name attribute is specified, it will only be assembled by name.
Ii. Case analysis
Here, only the @AutoWired annotation annotation is instantiated at the attribute location
[1] @autowired annotation
/ / start the class
@Test
public void TestMain(a) {
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println("userService:" + userService);
}
// Service
@Service
public class UserService {
@Autowired(required = false) // Specify optional
@Qualifier("userDao2") // Specify the assembly bean
private UserDao userDao;
@Override
public String toString(a) {
return "UserService{" +
"userDao=" + userDao +
'} '; }}// Dao
@Repository
public class UserDao {
private String label = "1";
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString(a) {
return "UserDao{" +
"label='" + label + '\' ' +
'} '; }}/ / configuration class
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
@Primary // The assembly bean is preferred
@Bean("userDao2")
public UserDao userDao(a){
UserDao userDao = new UserDao();
userDao.setLabel("2");
returnuserDao; }}Copy the code
The output is as follows, since @qualifier (“userDao2”) was used above to specify the bean to be assembled, so the output is label= ‘2’ :
- If you have
@Qualifier("userDao2")
Instead of@Qualifier("userDao")
, the assembly isLabel = '1'
[2] @resource annotation
@Service
public class UserService {
@Resource(name = "userDao2",type = UserDao.class)
private UserDao userDao;
@Override
public String toString(a) {
return "UserService{" +
"userDao=" + userDao +
'} '; }}Copy the code
- By default, assembly is performed by component name, or by name
- When no bean matching the name is found, assembly is done by type
- Does not support
@Primary
and@Autowired(required = false)
function- If both name and Type are specified, a unique matching bean is found from the Spring context and assembled, failing which an exception is thrown.
- If name is specified, the bean whose name (ID) matches is searched from the context for assembly, and an exception is thrown if no bean is found.
- If type is specified, an exception is thrown if a unique bean similar to a match is found from the context and assembled.
- If neither name nor type is specified, byName is automatically used for assembly. If there is no match, it falls back to a primitive type for matching, and if there is a match, it is automatically assembled.
Third, source tracking
Here is the source code analysis of the underlying @Autowired annotation
Reference: blog.csdn.net/topdevelope…
@autowired is used for assembly of the bean, affirmation and bean instantiation, the refresh method after the first, in finishBeanFactoryInitialization getBean method, and then trigger a bean initialization getObject. Bean initialization is a complicated place, in AbstractAutowireCapableBeanFactory# doCreateBean method, first create a BeanWrapper, Its internal member variable wrappedObject stores the instantiated MyService object, and then enters the populateBean method for property injection
Spring for the realization of the autowire annotation logic in class: AutowiredAnnotationBeanPostProcessor# postProcessProperties, — >findAutowiringMetadata — >buildAutowiringMetadata, the core code in buildAutowiringMetadata method inside
private InjectionMetadata buildAutowiringMetadata(Class
clazz) {
if(! AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} else {
List<InjectedElement> elements = new ArrayList();
// The target class to process
Class targetClass = clazz;
do {
List<InjectedElement> currElements = new ArrayList();
// get all the fields of the class via reflection, iterate over each field, and use the findAutowiredAnnotation method to iterate over the used annotations for each field, and return the auotowired attribute if decorated with autowiredReflectionUtils.doWithLocalFields(targetClass, (field) -> { MergedAnnotation<? > ann =this.findAutowiredAnnotation(field);
if(ann ! =null) {
// Verify that the autowired annotation is used on static methods
if (Modifier.isStatic(field.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// Determine whether required is specified
boolean required = this.determineRequiredStatus(ann);
currElements.add(newAutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required)); }});// The same logic as above, but the method of the class is handled by reflection
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if(BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { MergedAnnotation<? > ann =this.findAutowiredAnnotation(bridgedMethod);
if(ann ! =null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
}
boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(newAutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd)); }}});// There may be more than one @autowired annotation, so they are all added to the currElements container and processed together
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while(targetClass ! =null&& targetClass ! = Object.class);returnInjectionMetadata.forElements(elements, clazz); }}Copy the code
- Gets the target class to work with
- Pass in the target class parameter with the doWithLocalFields method, get all the fields of the class with reflection, and iterate over each field, and iterate over the used notation for each field with the findAutowiredAnnotation method, and if decorated with autowired, The auotoWired related property is returned
- Determines whether the Autowired annotation is used on static methods
- If there are multiple @AutoWired annotations, add them to the currElements container and process them together
Finally, return an InjectionMetadata collection containing all the annotations decorated with Autowire, as follows
- TargetClass: targetClass to process
- Elements: All sets of elements obtained by the above method
public InjectionMetadata(Class
targetClass, Collection
elements)
{
this.targetClass = targetClass;
this.injectedElements = elements;
}
Copy the code
Now that we have the target class and all the elements we need to inject, we can implement AutoWired’s dependency injection logic as follows:
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!this.shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList();
PropertyDescriptor[] var6 = pds;
int var7 = pds.length;
for(int var8 = 0; var8 < var7; ++var8) {
PropertyDescriptor pd = var6[var8];
if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if(! invalidProperties.isEmpty()) {throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName)); }}this.validatedBeanNames.add(beanName);
}
return pvs;
}
Copy the code
Call the Inject method defined in InjectionMetadata:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements; Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements ! =null ? checkedElements : this.injectedElements;
if(! ((Collection)elementsToIterate).isEmpty()) { Iterator var6 = ((Collection)elementsToIterate).iterator();while(var6.hasNext()) { InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next(); element.inject(target, beanName, pvs); }}}Copy the code
Iterate, and then call inject method, inject method its implementation logic is as follows:
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field)this.member;
// Brute force method, through reflection technology to instantiate and assign values to objects
ReflectionUtils.makeAccessible(field);
field.set(target, this.getResourceToInject(target, requestingBeanName));
} else {
if (this.checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method)this.member;
ReflectionUtils.makeAccessible(method);
// The name of the injected bean. The function of this method is to fetch the bean based on its name
method.invoke(target, this.getResourceToInject(target, requestingBeanName));
} catch (InvocationTargetException var5) {
throwvar5.getTargetException(); }}}Copy the code
- The use of reflection technology, divided into fields and methods to deal with.
- Methods like makeAccessible, which can be called brute-force cracking, use reflection techniques to instantiate and assign values to objects
- The getResourceToInject method takes the name of the bean to inject. The function of this method is to retrieve the bean based on its name
Four,
@autowired Automatic injection process diagram: