This article has participated in the weekend study plan, click the link to see details: Weekend Study Plan
Accumulate over a long period, constant dripping wears away a stone 😄
preface
I believe the @Autowired annotation has been one of the most used annotations in our actual development. Do you really know all the ways to use it? And I’m going to take you through it.
define
Let’s first look at the definition of the @Autowired annotation
@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(a) default true;
}
Copy the code
Notice that the @target value can be used on constructors, methods, method parameters, field attributes, and annotation classes. There is only one attribute required in the annotation, which defaults to true. This attribute is used to determine whether the Autowired annotated attribute must be injected. A value of true is required and an exception will be thrown if no matching injection item is found. If false, no matching injection item is found, null is assigned to the attribute of the annotation and no exception is thrown.
use
Parameter using
For example, this is definitely the most common way we use, without any of them:
@Component
public class UserService {
@Autowired
OrderService orderService;
public UserService(a){
System.out.println("No-parameter structural injection" + orderService);
}
public void test(a){
System.out.println("test="+orderService); }}@Component
public class OrderService {}@ComponentScan(basePackages = {"com.gongj.populateBean"})
public class AppApplication {}Copy the code
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
newAnnotationConfigApplicationContext(AppApplication.class); UserService bean = context.getBean(UserService.class); bean.test(); } result: XML no-argument construct injectionnull
test=com.gongj.populateBean.OrderService@156643d4
Copy the code
The constructor uses:
@Component
public class UserService {
//@Autowired
OrderService orderService;
public UserService(a){
System.out.println("XML no-parameter construct injection" + orderService);
}
@Autowired
public UserService(OrderService orderService1){
System.out.println("Parametric structural injection" + orderService1);
orderService = orderService1;
}
public UserService(OrderService orderService1,OrderService orderService2){
System.out.println("Two parameter construction injection" + orderService1 +"= = ="+ orderService2);
orderService = orderService1;
}
public void test(a){
System.out.println("test="+ orderService); }} the result: a reference structure into com. Gongj. PopulateBean. The OrderService @ 3 d012ddd test = com. Gongj. PopulateBean. 3 d012ddd OrderService @Copy the code
Whichever constructor you append the @Autowired annotation to, you use to instantiate the object.
For common methods:
@Autowired
public void yyy(OrderService orderService){
System.out.println("yyy=" + orderService);
this.orderService = orderService; } result: XML no-argument construct injectionnull
yyy=com.gongj.populateBean.OrderService@6504e3b2
test=com.gongj.populateBean.OrderService@6504e3b2
Copy the code
The method name is arbitrary.
For method parameters:
public void xxx(@Autowired OrderService orderService){
System.out.println("xxx=" + orderService);
this.orderService = orderService; } result: XML no-argument construct injectionnull
null
Copy the code
You can see that using the @Autowired annotation on method parameters does not work. Why is that? Well, Spring already shows that. I don’t know if you noticed, but there is a long English comment at the top of the # annotation source code, including a paragraph like this:
* <h3>Autowired Parameters</h3>
* <p>Although {@code @Autowired} can technically be declared on individual method
* or constructor parameters since Spring Framework 5.0. most parts of the * framework ignore such declarations. The only part of the core Spring Framework * that actively supports autowired parameters is the JUnit Jupiter support in * the {@code spring-test} module (see the
* <a href="https://docs.spring.io/spring/docs/current/spring-framework- reference/testing.html#testcontext-junit-jupiter-di">TestContext framework</a>
* reference documentation for details).
Copy the code
The @autowired annotation can be declared on method parameters, but it is ignored by most of the Spring Framework 5.0 frameworks. Only the Spring-test module supports this notation. But in fact, the use of 4.3.10.release on method parameters has no effect. In 3.0.5.RELEASE, @autowired annotation on method parameters is not supported.
But you can use it in this case:
@Bean
public OrderService user(@Autowired OrderService o){
System.out.println("user.o = "+ o);
return newOrderService(); } result: XML no-argument construct injectionnull
user.o = com.gongj.populateBean.OrderService@1cd072a9
test=null
Copy the code
Collection, Map, array injection
@Autowired
private List<OrderService> os;
@Autowired
private Map<String,OrderService> maps;
@Autowired
OrderService[] ar;
public void otherAutowiring(a) { System.out.println(os); System.out.println(maps); System.out.println(ar); } the result: [com.gongj.populateBean.OrderService@182decdb] {orderService=com.gongj.populateBean.OrderService@182decdb} [Lcom.gongj.populateBean.OrderService;@3796751bCopy the code
If the injected type is Map, the type of key must be String, the default value of key is beanName for each bean, and value is the specified bean type. This approach is used quite a lot, Spring combined with the policy pattern, really not cool.
Type found multiple
We all know that @AutoWired is injected by default by the injected type (byType). If there are two or more beans of the same type in the container, look up byName by property name or method parameter name. Most people will say that byType is followed by byName, but there are several layers of filtering behind byType, and if none of them are satisfied, byName is applied. Each of these approaches is described using examples.
The first step
Create an IPro interface and write a GET method.
public interface IPro {
void get(a);
}
Copy the code
The second step
Create two subclasses, ProductService and ScienceService, and override the methods of its parent interface.
@Service
public class ProductService implements IPro{
@Override
public void get(a) {
System.out.println("ProductService = "+ ProductService.class); }}@Service
public class ScienceService implements IPro{
@Override
public void get(a) {
System.out.println("ScienceService = "+ ScienceService.class); }}Copy the code
The third step
Create an injection class TypeManyTest, inject IPro into the TypeManyTest class and call its GET method.
@Component
public class TypeManyTest {
@Autowired
IPro pro;
public void test(a){ pro.get(); }}Copy the code
The fourth step
Modify the startup class, get the TypeManyTest bean from the container, and call its test method.
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppApplication.class);
TypeManyTest bean = context.getBean(TypeManyTest.class);
bean.test();
}
Copy the code
If nothing unexpected happens, the following exceptions occur:
No qualifying bean of type 'com.gongj.populateBean.IPro' available:
expected single matching bean but found 2: productService,scienceService
Copy the code
Find two beans according to the IPro type, and then find another bean according to the Pro as beanName, find no match, throw an exception. Change pro to productService or scienceService. ByName = byType = byName But let me look at some of the other solutions.
@Qualifier
Add the @qualifier annotation. By using the @qualifier annotation, we can specify which bean needs to be injected.
@Autowired
@Qualifier("productService")IPro pro; Start the project again and the result is ProductService =class com.gongj.populateBean.ProductService
Copy the code
You might think @qualifier is beanName. Note: the @qualifier value is not necessarily a beanName, it just compares it to a beanName that successfully matches the type. We can also use it on the ProductService class to achieve the same effect.
@Service
@Qualifier("XXXX")
public class ProductService implements IPro{
@Override
public void get(a) {
System.out.println("ProductService = "+ ProductService.class); }} use:@Autowired
@Qualifier("XXXX")
IPro pro;
Copy the code
@Primary
The @primary attribute is the Primary attribute of BeanDefinition. We add annotations to the classes that need to be used by default.
@Service
@Primary
public class ScienceService implements IPro{
@Override
public void get(a) {
System.out.println("ScienceService = "+ ScienceService.class); }}// The previous annotation @qualifier ("XXXX") needs to be removed or ProductService object should be used
@qualifier is resolved before @primaryUse:@AutowiredIPro pro; Result: ScienceService =class com.gongj.populateBean.ScienceService
Copy the code
@Priority
@priority It belongs to the Javax. annotation, JSR250 specification. Priority is defined by @priority. The smaller the number, the higher the Priority.
@Service
@Qualifier("XXXX")
@Priority(3) // Priority is 3
public class ProductService implements IPro{
@Override
public void get(a) {
System.out.println("ProductService = "+ ProductService.class); }}@Service
@Priority(1) // The priority is 1
public class ScienceService implements IPro{
@Override
public void get(a) {
System.out.println("ScienceService = "+ ScienceService.class); }} use:@AutowiredIPro pro; Result: ScienceService =class com.gongj.populateBean.ScienceService
Copy the code
The source address
The method in DefaultListableBeanFactory class. Write the candidates for this Map. In the example above, the value is two because two matching beans can be found based on the IPro type first.
- ScienceService: scienceService instance objects
- ProductService: productService instance objects
@Nllable
protected String determineAutowireCandidate(Map
candidates, DependencyDescriptor descriptor)
,> { Class<? > requiredType = descriptor.getDependencyType();// fetch @primary bean
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if(primaryCandidate ! =null) {
return primaryCandidate;
}
// Take the highest Priority bean to define the Priority via @priority, the smaller the number, the higher the Priority
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if(priorityCandidate ! =null) {
return priorityCandidate;
}
// byName
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if((beanInstance ! =null &&
this.resolvableDependencies.containsValue(beanInstance)) ||
// Based on the attribute name
/ / descriptor. GetDependencyName () : the field attribute name or method parameter names
matchesBeanName(candidateName, descriptor.getDependencyName())) {
returncandidateName; }}return null;
}
Copy the code
The @qualifier annotations are handled in a separate place and are not covered in detail in this article.
conclusion
By default, @autowired is injected byType (byType). If there are two or more beans of the same type in the container, the following filtering conditions will occur:
-
1. Match the name specified in the @Qualifier annotation. If the match is successful, the field is returned.
-
2, look for the bean marked by @primary, return if any.
-
3. Search according to the Priority specified by @priority (x). The smaller the number, the higher the Priority.
-
Finally, the bean is searched by property name or method parameter name, and null is returned if the bean with the specified name has not been found.
-
5. Check whether @AutoWired’s required attribute is true or whether the injected type is not array, Collection, or Map.
@Autowired(required = false) IPro pro; // This method still throws an exceptionCopy the code
Post the source code
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
// The only required beanName is not identified
if(isRequired(descriptor) || ! indicatesMultipleBeans(type)) {// The current dependency is required, or is not an array or Collection or Map, then an exception is thrown
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
return null; }}Copy the code
2. @AutoWired can be tagged on more than one constructor of the same class, but the required attribute must all be false. Other constructors are not allowed to have the @autowired annotation when one of them is required true, even if the required attribute is false.
- If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.