相关系列
依赖注入原理
依赖注入(Dependency Injection,简称DI)是一种设计模式,它允许我们解藕组件之间的关系,使得代码更易于测试和维护。在Spring框架中,依赖注入被广泛使用来管理对象(称为beans)及相互之前的依赖关系。Spring容器负责创建对象,配置对象以及管理这些对象的生命周期。
在手撕spring框架(1)-CSDN博客 中实现核心功能点,今天我们就在此基础上,来完成依赖注入。
创建Autowired.java
@Autowired是Spring框架提供的一个注解,用于实现依赖注入。我们简化的自定义实现。
功能
- 自动装配:@Autowired注解可以让Spring容器自动为字段、构造器、setter方法等注入所需的bean。这意味着开发者不需要手动使用new操作符来创建依赖对象的实例,Spring容器会根据类型匹配(默认)或按名称匹配(通过@Qualifier配合使用)来自动提供这些依赖。
- 默认行为:默认情况下,@Autowired按照类型匹配进行注入。如果存在多个相同类型的bean,Spring会尝试通过名称匹配或者使用@Primary标记的bean来解决歧义。如果没有找到匹配项,且没有配置为可选注入(通过@Autowired(required = false)),Spring将抛出异常。
- 位置 :
- 字段:直接标注在字段上,是最常见的使用方式。
- 构造器:可以用来标识一个构造器来进行依赖注入,特别是当类有多个构造器或者依赖是必须的时候非常有用。
- setter方法:适用于需要在对象创建之后设置依赖的情况。
- 可选性:通过required属性可以声明依赖是否是必须的,默认为true表示必须注入,设为false则表示如果没有匹配的bean,也不会抛出异常。
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
String value() default "";
}
字段注入
修改原有的UserService.java文件
java
package com.dzend.service;
import com.spring.Autowired;
import com.spring.Component;
@Component(value = "userService")
public class UserService {
@Autowired
private OrderService orderService;
public OrderService getOrderService() {
return orderService;
}
}
调用DzendApplicationContext中文件createBean方法
则整个文件如下:
java
package com.spring;
import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DzendApplicationContext {
private Class<?> configClass;
private Map<String,BeanDefinition> beanDefinitionMap= new HashMap<>();
private Map<String, Object> singletonObjects=new HashMap<>();
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
public DzendApplicationContext(Class configClass) {
this.configClass=configClass;
scan(configClass);
}
private void scan(Class configClass) {
if(configClass.isAnnotationPresent(ComponentScan.class)){
ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScan.value();
path = path.replace(".","/");
ClassLoader classLoader = DzendApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path);
try {
path = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
File file = null;
try {
file = new File( URLDecoder.decode(resource.getFile(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if(file.isDirectory()){
for (File f : file.listFiles()) {
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
absolutePath=absolutePath.replace("\\",".");
try {
Class<?> clazz = classLoader.loadClass(absolutePath);
if (clazz.isAnnotationPresent(Component.class)) {
Component componentAnnotaion = clazz.getAnnotation(Component.class);
String beanName= componentAnnotaion.value();
if("".equals(beanName)){
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
}else{
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
}
public Object getBean(String beanName){
if(!beanDefinitionMap.containsKey(beanName)){
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition.getScope().equals("singleton")){
Object singletonObject = singletonObjects.get(beanName);
if(singletonObject == null){
singletonObject = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,singletonObject);
}
return singletonObject;
}else{
//原型
Object prototypeBean = createBean(beanName, beanDefinition);
return prototypeBean;
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();
Object instance = null;
try {
instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
if(field.isAnnotationPresent(Autowired.class)){
field.setAccessible(true);
field.set(instance,getBean(field.getName()));
}
}
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
return instance;
}
}
最后修改Test.java中的main方法
java
package com.dzend;
import com.dzend.service.UserService;
import com.spring.DzendApplicationContext;
public class Test {
public static void main(String[] args) {
//扫描 ->创建对象
DzendApplicationContext applicationContext = new DzendApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.getOrderService().test();
}
}