In the Spring IOC
IoC is an Inversion of Control feature. It’s not a Spring feature or a Java feature. It’s a design idea. DI(Dependency Injection) is an implementation of Ioc. As for the specific definition of Ioc and DI, as well as their advantages and disadvantages, you can find some information by yourself. I won’t go into details here. In a word, spring’s Ioc function greatly facilitates our development work.
Before implementing our Ioc, let’s take a look at dependency injection in Spring. There are three methods of dependency injection in Spring:
- Interface Injection
- Set method Injection
- Constructor Injection
@Component
public class ComponentA {
@Autowired // 1. Interface injection
private ComponentB componentB;
@Autowired // 2. Set method injection
public void setComponentB(ComponentB componentB) {
this.componentB = componentB;
}
@Autowired // 3. Construct injection
public ComponentA(ComponentB componentB) {
this.componentB = componentB; }}Copy the code
Cyclic dependency injection
If you just implement dependency injection, it’s actually quite easy to ‘inject’ the corresponding properties using Java’s reflection principle. But there is one problem that you must be aware of, and that is the problem of circular dependencies. Cyclic dependency is A cycle formed by the interdependence of classes. For example, A depends on B, and B depends on A at the same time, which forms A mutual loop.
// ComponentA
@Component
public class ComponentA {
@Autowired
private ComponentB componentB;
}
// ComponentB
@Component
public class ComponentB {
@Autowired
private ComponentA componentA;
}
Copy the code
So how do we solve the problem of loop dependency in Spring? Let’s talk about the principle.
If you want to create a class, first put the class in ‘creating pool’, create an instance by reflection, etc., then put the instance in the create pool and remove the class in ‘creating pool’. Whenever an instance has a dependency that needs to be injected, the dependency is created first by looking for the corresponding instance from the creation pool.
Using the in-process state cache, the Bean can be created by putting the Bean in the in-process state even if the dependency is not instantiated, and then running to create the dependency. If the dependency class is dependent on the Bean, just pull the Bean out in the in-process pool. Injection into this dependency ensures that the Bean’s dependency can be instantiated. Go back and inject the dependency into the Bean, then the Bean is instantiated, move the Bean from ‘creating pool’ to ‘creating pool’, and the circular dependency problem is resolved.
Although Spring does an excellent job of avoiding circular dependencies, there is virtually no way that construction injection can avoid circular dependencies. Because when instantiating the constructor of ComponentA, we must get the instance of ComponentB, but when instantiating the constructor of ComponentB, we must have the instance of ComponentA. Both Bean can’t through reflection instantiate and then into the ‘is to create a pool, so I can’t solve the problem of circular dependencies, then spring will take the initiative to throw BeanCurrentlyInCreationException exception to avoid infinite loop.
* Note that the above mentioned are based on spring singleton mode, if it is a multi-instance mode will be different, you can learn by yourself.
To realize the IOC
Now you can start implementing IOC functionality
Add annotations
Create an Annotation package under the ZBW. Ioc package, and then create an Autowired annotation. The annotation Target has only one elementType. FIELD, which can only be annotated on attributes. This means that we currently only implement interface injection. This avoids the problem of circular dependencies caused by construct injection, and interface injection is the most commonly used method. If you want to implement set value injection you can do it yourself, it’s pretty much the same.
package com.zbw.ioc.annotation;
import.@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
Copy the code
Implementation IOC class
package com.zbw.ioc;
import.@Slf4j
public class Ioc {
/** * Bean container */
private BeanContainer beanContainer;
public Ioc(a) {
beanContainer = BeanContainer.getInstance();
}
/** * execute Ioc */
public void doIoc(a) {
for(Class<? > clz : beanContainer.getClasses()) {// Iterate over all beans in the Bean container
final Object targetBean = beanContainer.getBean(clz);
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) { // Iterate over all properties in the Bean
if (field.isAnnotationPresent(Autowired.class)) {// If the property is annotated by Autowired, inject it
finalClass<? > fieldClass = field.getType(); Object fieldValue = getClassInstance(fieldClass);if (null! = fieldValue) { ClassUtil.setField(field, targetBean, fieldValue); }else {
throw new RuntimeException("Cannot inject corresponding class, target type :" + fieldClass.getName());
}
}
}
}
}
/** * get an example or implementation Class */ based on Class
private Object getClassInstance(finalClass<? > clz) {
returnOptional .ofNullable(beanContainer.getBean(clz)) .orElseGet(() -> { Class<? > implementClass = getImplementClass(clz);if (null! = implementClass) {return beanContainer.getBean(implementClass);
}
return null;
});
}
/** * get the implementation class of the interface */
privateClass<? > getImplementClass(finalClass<? > interfaceClass) {return beanContainer.getClassesBySuper(interfaceClass)
.stream()
.findFirst()
.orElse(null); }}Copy the code
After knowing the principle of IOC, I found that it is really very simple. Here, I realized the function of IOC with dozens of lines of code.
Firstly, the BeanContainer container that we have singleton before is obtained in the Ioc class construction.
Then it is in the doIoc() method that IOC functionality is formally implemented.
- Iterate over all beans in the BeanContainer
- The Field property of each Bean is traversed
- If a Field property is
Autowired
Annotation, then callsgetClassInstance()
Method to inject it getClassInstance()
We will try to get the corresponding instance from the Bean container according to the Class of the Field. If we get the instance, we will return the instance. If we do not get the Field, we will consider it as an interface and we will callgetImplementClass()
Method to obtain the implementation Class of the interface, and then obtain the corresponding implementation Class instance in the Bean container based on the implementation Class.
The test case
To test that our Ioc and BeanContainer are written correctly, let’s write a test case to test it.
Start by adding junit’s dependencies to POM.xml
<properties>.<junit.version>4.12</junit.version>
</properties>
<dependencies>.<! -- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Copy the code
Then add DoodleController, DoodleService and DoodleServiceImpl to test package for convenience
// DoodleController
package com.zbw.bean;
@Controller
@Slf4j
public class DoodleController {
@Autowired
private DoodleService doodleService;
public void hello(a) { log.info(doodleService.helloWord()); }}// DoodleService
package com.zbw.bean;
public interface DoodleService {
String helloWord(a);
}
// DoodleServiceImpl
package com.zbw.bean;
@Service
public class DoodleServiceImpl implements DoodleService{
@Override
public String helloWord(a) {
return "hello word"; }}Copy the code
Then write the IocTest test case
package com.zbw.ioc;
import.@Slf4j
public class IocTest {
@Test
public void doIoc(a) {
BeanContainer beanContainer = BeanContainer.getInstance();
beanContainer.loadBeans("com.zbw");
newIoc().doIoc(); DoodleController controller = (DoodleController) beanContainer.getBean(DoodleController.class); controller.hello(); }}Copy the code
See the string in the helloWord() method that outputs the DoodleServiceImpl in the DoodleController, indicating that the DoodleService in the DoodleController has successfully injected the DoodleServiceImpl. Then our IOC function is complete.
- Implement a simple Java MVC framework from scratch (I)– Preface
- Implement a simple Java MVC framework from scratch (two)- implement Bean container
- Implement a simple Java MVC framework from scratch (iii)– implement IOC
- Implement a simple Java MVC framework from scratch (four)– implement AOP
- Implementing a simple Java MVC framework from scratch (5)– Introducing AspectJ to implement AOP pointcuts
- Implement a simple Java MVC framework from scratch (6)– enhance AOP functionality
- Implement a simple Java MVC framework from scratch (7)– Implement MVC
- Implement a simple Java MVC framework from scratch (8)- Starter
- Implement a simple Java MVC framework from scratch (9)– optimize THE MVC code
Source address :doodle
Implement a simple Java MVC framework from scratch — implement IOC