The way we often inject is similar to this
@Service public class HelloService { @Autowired private BeanFactory beanFactory; @Autowired public HelloService(ApplicationContext applicationContext) { } @Autowired public void setEnvironment(Environment environment) { } }
Whether constructor injection or attribute injection, we can call it explicit injection.
Let's take another look at our commonly used annotation @ Bean
public @interface Bean { @AliasFor("name") String[] value() default {}; @AliasFor("value") String[] name() default {}; @Deprecated Autowire autowire() default Autowire.NO; boolean autowireCandidate() default true; String initMethod() default ""; String destroyMethod() default AbstractBeanDefinition.INFER_METHOD; }
Although the annotation of the attribute autowire we are concerned about has been abandoned, it does not prevent us from understanding it.
- NO is the default value, which means it will not be injected automatically
- BY_NAME, automatic injection through beanName
- BY_TYPE, automatic injection by parameter type
Autowire.NO
@Configuration public class Config { @Bean(autowire = Autowire.NO) public HelloService helloService() { return new HelloService(); } } public class HelloService { public void setEnvironment(Environment environment) { System.out.println("invoke " + environment); } }
We use the default configuration without automatic injection of HelloService. There is no doubt that setEnvironment will not be invoked by the Spring framework.
Autowire.BY_NAME
We changed the above code to
@Bean(autowire = Autowire.BY_NAME)
You will find that setEnvironment is called and the parameter environment is the standardservlet environment in Spring
In this step, if you are impressed with the Spring bean acquisition process Spring acquiring singleton process (3) You can know that it is called in AbstractAutowireCapableBeanFactory#populateBean
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Put the automatically injected value into newPvs if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } .......... if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); }
The corresponding code is also very simple
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { // Obtain the main beanName and explain the setter method according to the setter method String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { // If it is not in BeanFactory, it will be ignored directly and no error will be reported if (containsBean(propertyName)) { Object bean = getBean(propertyName); // Add to pvs pvs.add(propertyName, bean); registerDependentBean(propertyName, beanName); } else { // If the bean does not exist, no error will be reported and ignored directly } } }
You can see BY_NAME is obtained by interpreting the setter method name. If beanName does not exist in BeanFactory, it is ignored directly, similar to @ Autowired(required=false). Not mandatory injection
However, your method name must start with setXxx, comply with certain naming rules, and be similar to the rules of BeanInfo( Java introspection ), but it is looser than it. There is no limit here. The return value must be void
Autowire.BY_TYPE
autowireByType
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { ....... Set<String> autowiredBeanNames = new LinkedHashSet<>(4); String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); // Iterate over the setter method interpretation to get its property name for (String propertyName : propertyNames) { try { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); if (Object.class != pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); // AutowireByTypeDependencyDescriptor is a key class, and its returned getDependencyName is always null DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); } autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); } } }
With BY_NAME is very similar. If the object I automatically inject does not exist in BeanFactory, will it not throw exceptions? The answer is yes. This is mainly because the class AutowireByTypeDependencyDescriptor is used
private static class AutowireByTypeDependencyDescriptor extends DependencyDescriptor { public AutowireByTypeDependencyDescriptor(MethodParameter methodParameter, boolean eager) { super(methodParameter, false, eager); } @Override public String getDependencyName() { return null; } } // Parent class public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) { super(methodParameter); ....... this.required = required; ....... }
The first key point is that required is false. When the dependent bean does not exist BeanFactory, false will not throw an exception.
The second key point is that if there are two identical beans and neither bean declares Primary or priorityorded, it will throw an exception instead of matching and filtering out the appropriate beans according to beanName, because getDependencyName always returns null
This situation is different from @ Autowired because BY_TYPE does not depend on beanName
@Nullable protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback for (Map.Entry<String, Object> entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); // resolvableDependencies if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || // BY_ Type matchsbeanname returned false matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; }
Key value stored in resolvable dependencies
summary
Compared with explicit injection, automatic injection is less practical in actual scenarios, but understanding the process will still let you gain some relevant knowledge and processes
Cheer for yourself and all the students who see this article ~!!
WeChat official account: CoderLi