相关系列
手撕spring框架(1)-CSDN博客
依赖注入原理
依赖注入(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 {@Autowiredprivate 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();}
}