I wrote the IOC implementation of spring before, but now I write the implementation of DI dependency injection
Paste class diagram
Because junit package will be used when DI is implemented for testing, import dependency in pom.xml
<!--SpringDI-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13-beta-3</version>
<scope>compile</scope>
</dependency>
Let's start with a few comments
package com.spring.DI.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
package com.spring.DI.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
public String scope() default "";
}
package com.spring.DI.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
public String value();
}
Create entity classes again (to save space, get and set methods are omitted)
package com.spring.DI.pojo;
import com.spring.DI.annotation.MyComponent;
import com.spring.DI.annotation.MyValue;
@MyComponent(scope = "prototype")
public class User {
@MyValue(value = "1")
private Integer id;
@MyValue(value = "Wax gourd")
private String name;
@MyValue(value = "123456")
private String password;
public User() {
System.out.println("Nonparametric construction method execution");
}
public void login() {
System.out.println("User login: id = " + id + ", name" + name + ", password= " + password);
}
}
Next, create the UserService class, and inject the User with the dependency in the Service class;
package com.spring.DI.service;
import com.spring.DI.annotation.MyAutowired;
import com.spring.DI.annotation.MyComponent;
import com.spring.DI.pojo.User;
@MyComponent
public class UserService {
@MyAutowired
User user1;
@MyAutowired
User user2;
public void userLogin(){
System.out.println("User 1:"+user1);
user1.login();
System.out.println("User 2:"+user2);
user2.login();
}
}
Create annotation factory class
package com.spring.DI.applicationContext;
import com.spring.DI.annotation.MyAutowired;
import com.spring.DI.annotation.MyComponent;
import com.spring.DI.annotation.MyValue;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class AnnotationConfigApplicationContext {
private Map<String, Class<?>> beanDefinationFactory = new ConcurrentHashMap<String, Class<?>>();
private Map<String, Object> singletonBeanFactory = new ConcurrentHashMap<String, Object>();
public AnnotationConfigApplicationContext(String... packageNames) {
for (String packageName : packageNames) {
System.out.println("Start scanning package:" + packageName);
scanPkg(packageName);
}
dependencyInjection();
}
}
In the construction method of factory class, multiple package paths can be received, and each package path can be scanned circularly. The scanPkg method of scanning package is as follows:
private void scanPkg(final String pkg) {
String pkgDir = pkg.replaceAll("\\.", "/");
URL url = getClass().getClassLoader().getResource(pkgDir);
File file = new File(url.getFile());
File[] fs = file.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
String fName = file.getName();
if (file.isDirectory()) {
scanPkg(pkg + "." + fName);
} else {
if (fName.endsWith(".class")) {
return true;
}
}
return false;
}
});
for (File f : fs) {
String fName = f.getName();
fName = fName.substring(0, fName.lastIndexOf("."));
String beanId = String.valueOf(fName.charAt(0)).toLowerCase() + fName.substring(1);
String pkgCls = pkg + "." + fName;
try {
Class<?> c = Class.forName(pkgCls);
if (c.isAnnotationPresent(MyComponent.class)) {
beanDefinationFactory.put(beanId, c);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Start injection
private void dependencyInjection() {
Collection<Class<?>> classes = beanDefinationFactory.values();
for (Class<?> cls : classes) {
String clsName = cls.getName();
clsName = clsName.substring(clsName.lastIndexOf(".") + 1);
String beanId = String.valueOf(clsName.charAt(0)).toLowerCase() + clsName.substring(1);
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyAutowired.class)) {
try {
String fieldName = field.getName();
System.out.println("Property name:" + fieldName);
Object fieldBean = null;
if (beanDefinationFactory.get(fieldName) != null) {
fieldBean = getBean(fieldName, field.getType());
} else {
String type = field.getType().getName();
type = type.substring(type.lastIndexOf(".") + 1);
String fieldBeanId = String.valueOf(type.charAt(0)).toLowerCase() + type.substring(1);
System.out.println("Attribute types ID: " + fieldBeanId);
fieldBean = getBean(fieldBeanId, field.getType());
}
System.out.println("Value to inject for attribute:" + fieldBean);
if (fieldBean != null) {
Object clsBean = getBean(beanId, cls);
field.setAccessible(true);
field.set(clsBean, fieldBean);
System.out.println("Injection succeeded!");
} else {
System.out.println("Injection failure");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
During injection, the getBean method was called:
public Object getBean(String beanId) {
Class<?> cls = beanDefinationFactory.get(beanId);
MyComponent annotation = cls.getAnnotation(MyComponent.class);
String scope = annotation.scope();
try {
if ("singleton".equals(scope) || "".equals(scope)) {
if (singletonBeanFactory.get(beanId) == null) {
Object instance = cls.newInstance();
setFieldValues(cls, instance);
singletonBeanFactory.put(beanId, instance);
}
return singletonBeanFactory.get(beanId);
}
if ("prototype".equals(scope)) {
Object instance = cls.newInstance();
setFieldValues(cls, instance);
return instance;
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public <T> T getBean(String beanId, Class<?> c) {
return (T) getBean(beanId);
}
In the getBean method, you need to assign a value to the member property of the object and call the setFieldValues method
private void setFieldValues(Class<?> cls, Object obj) {
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyValue.class)) {
String fieldName = field.getName();
String value = field.getAnnotation(MyValue.class).value();
String type = field.getType().getName();
fieldName = String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
String setterName = "set" + fieldName;
try {
Method method = cls.getDeclaredMethod(setterName, field.getType());
if ("java.lang.Integer".equals(type) || "int".equals(type)) {
int intValue = Integer.valueOf(value);
method.invoke(obj, intValue);
}else if ("java.lang.String".equals(type)){
method.invoke(obj,value);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
Finally release resources
public void close(){
beanDefinationFactory.clear();
beanDefinationFactory=null;
singletonBeanFactory.clear();
singletonBeanFactory=null;
}
Now that the DI implementation is completed, start the test
package com.spring.DI.springTest;
import com.spring.DI.annotation.MyComponent;
import com.spring.DI.applicationContext.AnnotationConfigApplicationContext;
import com.spring.DI.service.UserService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@MyComponent
public class TestSpringDi {
AnnotationConfigApplicationContext ctx;
UserService userService;
@Before
public void init() {
ctx = new AnnotationConfigApplicationContext
("com.spring.DI.pojo",
"com.spring.DI.service", "com.spring.DI.springTest");
userService = ctx.getBean("userService", UserService.class);
}
@Test
public void userLogin() {
userService.userLogin();
}
@After
public void close() {
ctx.close();
}
}
Operation result