Dependency injection (DI)
DI(Dependency Injection)Spring IOC is not a technology, but an idea, which can guide us to design loosely coupled program code. The function of spring IOC is reflected in two aspects: one is how to assemble beans into a container and how to get beans from the container; the other is how to solve the dependency relationship between beans. In other words, if the IOC container manages the dependency relationship, when a bean needs to rely on another bean, how can IOC container implement such dependency relationship.
The implementation method to solve the dependency between beans in spring is called in the concept of springDependency injection (DI). Generally speaking, there are three ways to implement spring dependency injection: construction method injection, setter method injection and annotation injection. However, as far as I’m concerned, I think it should be divided into two forms – XML based injection and annotation based injection, and then subdivided into the following forms:
XML based injection is the first way we learn and use, and it is also the most familiar way. Let’s give a brief introduction and give an example.
- Injection by construction method
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
/**Methods inherited from userservice**/
}
First, define a service layerUserServiceImpl
And then add a reference to the Dao layer inside ituserDao
。
The next step is to add a constructorpublic UserServiceImpl(UserDao userDao)
Let’s wait for spring to use this methoduserDao
Injection instance.
Finally, the corresponding bean instance is injected into the spring XML configuration file.
If there is no corresponding construction method, an error will be reported.
- Injection through setter method
modifyUserServiceImpl.java
For:
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
/**Methods inherited from userservice**/
}
Then modify the content of the XML file as follows:
The difference between the two methods is that: 1UserServiceImpl.java
You don’t need to add a constructor, but there must be a parameterless constructor (such aspublic UserServiceImpl()
, which is not written in the example, because Java will provide a parameterless construction method by default for the spring container to register and generate beans (such asuserService
)。 2、 In XML file, when the construction method is used to inject, we need to use theThis pair of tags; when the setter method is injected, the
label.
In the XML injection process, in addition to usingref=""
In addition to references, you can also usevalue=""
Set specific value, its effect and use annotation@Value
almost.
Annotation based dependency injection
@Autowired
- Source code
@Target({ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
@Autowired
Is the key point of annotation based dependency injection, its source code is very simple, only one parameterrequest()
The function of this parameter is to identify whether the bean must be injected, that is, when the spring container does not find the corresponding bean, if its value istrue
If the value isfalse
, there will be no exception, but in the process of using, if the container does not inject beans all the time, there may be null pointer exception.
Another point is that the@Target
The parameters included are exactly the types of dependency injection methods based on annotations,@Target
decided@Autowired
Can be marked on which types.
- Injection by construction method
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
/**Methods inherited from userservice**/
}
According to the development documents, there is only one construction method. Since spring 4.3, there is no need to add it @Autowired
Mark, you can. However, if there are multiple construction methods, you must label one of them @Autowired
Otherwise spring will report an exception.
- Injection through setter method
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
/**Methods inherited from userservice**/
}
- Field injection
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
/**Methods inherited from userservice**/
}
- Injection of parameters through the method
The above three injection methods are relatively familiar, so I will not elaborate more. Let’s focus on parameter injection. In fact, the method of parameter injection is similar to that of constructor method and setter method, which is equivalent to annotation on constructor method and setter method@Autowired
Put it in the position of the input parameter. It may be abstract. Let’s look at the example
@Component
public class UserDaoImpl implements UserDao {
//Simply return a user to simulate the database search process
@Override
public User getUser(Long id, String name){
User user = new User();
user.setId(id);
user.setName(name);
user.setAccount("12345678911");
user.setPassword("******");
user.setOtherInfo("this is a test account");
return user;
}
}
//Userservice class
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(@Autowired UserDao userDao,
@Autowired User user) {
System.out.println("UserServiceImpl: "+user);
this.userDao = userDao;
}
@Override
public User getUser(Long id, String name){
return userDao.getUser(id,name);
}
}
//Simple configuration class
//The function is to generate bean for the class marked with @ componet (@ service) annotation
//At the same time, the instance is injected into the bean (object) identified by @ Autowired
@Configuration
@ComponentScan
public class DIConfig {
//Used for injection of user in service class
@Bean
public User getUser(){
User u = new User();
u.setName("user inject into service");
return u;
}
}
//Test class
//Note: when testing with junit4, if you need to use @ Autowired injection, you must add
//@Runwith annotation starts in spring mode (or springbootrunner)
//@Contextconfiguration scan configuration class
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
//If you do not add two annotations on the test class, the injection fails
@Autowired
private UserService userService;
@Test
public void testAutowired(){ System.out.println(userService.getUser(1L,"name"));
}
}
After running the test method, we get the following results:
public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)
The output results in are as follows:
public void testAutowired()
The output results of the test method are as follows:
Pay attention herepublic UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)
In this paper, we introduce a new method
userDao
yesUserServiceImpl
Field, butuser
no In other words, we can add any parameter to the construction method. As long as it is needed, it is not necessarily required that the parameter is a property field in the class.
In addition, it should be noted that the method mentioned here is not an arbitrary method, but a construction method or setter methodpublic void initService(@Autowired UserDao userDao)
Custom methods cannot be injected.
@Primary and @ qualifier
In the above example, all the beans we inject are only one bean instance in the container. Then, when there are multiple objects of the same type in the containerBean
How to deal with it?
Modify the configuration class code as follows:
@Configuration
@ComponentScan
public class DIConfig {
@Bean
public User getUser(){
User u = new User();
u.setName("this is user");
return u;
}
@Bean
public User getUser2(){
User u = new User();
u.setName("this is user2");
return u;
}
}
Modify test class:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
@Autowired
private User user;
@Test
public void testAutowiredPriamry(){
System.out.println(user);
}
}
When no other treatment is done, the result is as follows
Because there are two user beans(getUser , getUser2
If @ bean is not specified, the default method name is bean name, so spring cannot determine which one to use for injection.
Modification method:
- stay
@Bean
Set name in, such as@Bean(name="user")
When the name matchesprivate User user;
The injection can also be completed. - take
private User user
Rewrite asgetUser
orgetUser2
Any one can also be injected. The reason is the same as above. Spring will first match according to the type. If it can’t match, then it will match according to the name. If it can’t match, it will naturally throw an exception.
In addition, spring provides us with two annotations to eliminate the ambiguity of dependency injection.
@Primary
@Target({ ElementType.TYPE , // class, interface, enumeration type
ElementType.METHOD }) // method
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {
}
@Primary
It is an annotation that sets the priority of the same type bean, that is, once it is added to a type@Priamry
When the bean is not specified, the bean will be injected@Priamry
The bean of the identity.
@Configuration
@ComponentScan
public class DIConfig {
@Primary
@Bean
public User getUser(){
User u = new User();
u.setName("this is user");
return u;
}
@Bean
public User getUser2(){
User u = new User();
u.setName("this is user2");
return u;
}
}
Like the above, in thegetUser()
The test method can also run normally by adding the corresponding annotation on the.
But the problem with this approach is that@Priamry
It can be used in many classes, if multiple beans of the same type are labeled@Primary
So@Priamry
It will lose its due effect.
@Qualifier
Therefore, spring provides@Qualifier
This annotation is marked directly in the@Autowired
For the injected bean, a bean is explicitly specified to be injected.
@Target({ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
@Qualifier
You can see anything@Autowired
Where it can appear, it can be used with it. For example:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = DIConfig.class)
public class DITest {
//Directly specify getuser2 for injection
@Autowired
@Qualifier("getUser2")
private User user;
@Test
public void testAutowiredPriamry(){
System.out.println(user);
}
}
Both annotations are disambiguated and are recommended@Bean (name = XXX) and @ qualifier (value = XXX)
How to use it in combination. However, if there is no ambiguity in the development environment, it is unnecessary to use them.
Of course, it’s just for@Autowired
For more information, you can check the annotation based container configuration. This reference document has a more detailed and rich introduction.
summary
In general, how does spring implement IOC? First, spring provides an IOC container for getting and managing beans. Then, a set of dependency injection mechanism is provided to help IOC container better manage the dependency relationship among beans, so as to better realize the idea of IOC. It is impossible for a bean to exist independently from the dependency relationship of other beans. When a bean needs the introduction of other beans to initialize, it needs the dependency injection mechanism.
For example, if there is a class a method that wants to call the B interface, or an instance of the B interface is needed.
The traditional program flow is to use a C class to implement B interface, and then create an instance of C class to call its method.
In the process of spring’s dependency injection, class a only needs to add an injection interface (in a broad sense, not an interface)interface
This interface can be a constructor method, setter method or other forms. At the same time, add a reference to B interface(private B b;
)。
When you really need to generate class a instances, the spring IOC container injects the corresponding bean according to the interface provided by class A, which can be class C(class C implements B
{}), or class D(class D implements B
{} and so on; the specific person is determined according to the bean assembly strategy and the bean in the IOC container, and is no longer managed by the developer.
Official account: good Linux