文章目录
- IOC控制反转
- 依赖注入
- Bean的自动装配方式
- 丐版IOC实现
- BeanDefinition.java
- ResourceLoader.java
- BeanRegister.java
- Bean和DI的注解
- BeanFactory.java
- ApplicationContext
- 测试,实现
- 在这里插入图片描述
大家好,我是Leo。Spring核心中依赖注入和IOC容器是非常常见的,用起来也是非常的顺手,只能说是真香,那如何实现一个丐版的SpringIOC呢?那今天我们就来讲如何实现一个乞丐版的IOC和通过注解进行依赖注入。
IOC控制反转
在开始之前,首先得先理解什么是IOC的控制反转,Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系 。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
说白了就是,IOC负责我们的饮食,我们要饭和菜,只需要从IOC容器获取就行。
那这样的作用也就非常明显了,解耦、方便
依赖注入
那依赖注入肯定是IOC的精髓所在,我们平常使用@Autowired注解等自动注入方式,用起来确实舒服。
那依赖注入常用方式有哪些呢?
-
构造函数的参数实现注入
-
基于set注入(静态工厂和动态工厂)
-
基于属性的注入
Bean的自动装配方式
可以在文件中设置自动装配(autowire)方式,支持的装配方式有
方式 | 描述 |
---|---|
no | 手动装配 |
byName | 通过id的名字自动注入对象 |
byType | 通过类型自动注入对象 |
constructor | 通过构造方法自动注入对象 |
autodectect | 完全交给Spring管理,按先Constructor后byType的顺序进行匹配 |
丐版IOC实现
BeanDefinition.java
Bean的定义类,主要是包括配置Bean定义的对应的实体,对应的构造方法和getset方法就省略了
public class BeanDefinition {
private String beanName;
private Class beanClass;
ResourceLoader.java
资源加载器,用来完成包的扫描和Bean的加载
public class ResourceLoader {
private static String rootPath;
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap(16);
public Map<String, BeanDefinition> getResource(String basePackage) {
// com.zly
try {
// 1. 把.换成/
String packagePath = basePackage.replaceAll("\\.", "\\\\");
// 2.获取包的绝对路径
Enumeration<URL> urls =
Thread.currentThread()
.getContextClassLoader()
.getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
// 包扫描
try {
loadBean(new File(filePath));
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return this.beanDefinitionMap;
}
private void loadBean(File file) throws Exception {
// 1. 判断是否是文件夹
if (file.isDirectory()) {
// 2. 获取文件夹的所有内容
File[] childFiles = file.listFiles();
// 3. 判断文件夹内为空,直接返回
if (childFiles == null || childFiles.length == 0) {
return;
}
// 4. 如果文件夹里面不为空,遍历文件夹所有的内容
for (File childFile : childFiles) {
// 4.1 遍历得到每个File对象,继续是文件夹,递归
if (childFile.isDirectory()) {
loadBean(childFile);
} else {
// 4.2 得到不是文件夹,是文件
// 4.3 得到包路径 和类名称
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
// 4.4 判断当前文件类型是否为class
if (pathWithClass.contains(".class")) {
// 4.5 是class类型,把\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
// 4.6 判断类上面是否有注解@Bean,放到map集合beanFactory
// 4.6.1 获取类的Class对象
Class<?> clazz = Class.forName(allName);
// 4.6.2 判断不是接口
if (!clazz.isInterface()) {
// 4.6.3 判断上面是否有@Bean注解
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null) {
// 4.6.4 实例化
String beanName = annotation.value();
if ("".equals(beanName)) {
beanName = allName;
}
if (clazz.getInterfaces().length > 0) {
System.out.println("正在加载【"+ clazz.getInterfaces()[0] +"】");
BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
beanDefinitionMap.put(beanName, beanDefinition);
} else {
System.out.println("正在加载【"+ clazz.getInterfaces()[0]);
BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);
beanDefinitionMap.put(beanName, beanDefinition);
}
}
}
}
}
}
}
}
}
BeanRegister.java
用于向容器中注册一个Bean,在扫描后,我们的Bean主要是放在了beanDefinitionMap中,还没有进行加载和初始化,在获取中再进行加载到这个缓存Map中
public class BeanRegister {
private Map<String, Object> singletonMap = new HashMap<>(32);
public Object getSingletonBean(String beanName) {
return singletonMap.get(beanName);
}
public void registerSingletonBean(String beanName, Object bean) {
if (singletonMap.containsKey(beanName)) {
return;
}
singletonMap.put(beanName, bean);
}
}
Bean和DI的注解
这里的两个注解主要是用来标志这是一个Bean和进行依赖注入的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
BeanFactory.java
这里的对象工厂主要是我们最重要的一个类,在创建时,需要加载注册器和资源加载,在获取Bean中,需要进行依赖注入,并创建一个Bean
public class BeanFactory {
/**
* 创建一个map集合,放bean对象
*/
private Map<String , BeanDefinition> beanDefinitionMap = new HashMap<>();
private BeanRegister beanRegister;
public BeanFactory(String basePackage) {
beanRegister = new BeanRegister();
this.beanDefinitionMap = new ResourceLoader().getResource(basePackage);
}
public Object getBean(String beanName) {
// 从缓存中获取
Object bean = beanRegister.getSingletonBean(beanName);
if (bean != null) {
return bean;
}
// 创建bean
return createBean(beanDefinitionMap.get(beanName));
}
public Object getBean(Class<?> clazz) {
BeanDefinition beanDefinition = getBeanNameByType(clazz);
if (Objects.isNull(beanDefinition)) {
log.error("can not find {}", clazz);
return null;
} else {
return getBean(beanDefinition.getBeanName());
}
}
private Object createBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().getConstructor().newInstance();
beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
loadDi(beanDefinition.getBeanName());
return bean;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 属性注入
*/
private void loadDi(String beanName) {
// 1. 实例化对象都在beanFactory的map集合中,遍历
Object bean = this.beanRegister.getSingletonBean(beanName);
// 2. 获取map集合每个对象和属性
Class<?> objectClass = bean.getClass();
// 3. 遍历得到每个对象属性数组,得到每个属性
Field[] declaredFields = objectClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 4. 判断属性上面是否有@Di注解
Di annotation = declaredField.getAnnotation(Di.class);
if (annotation != null) {
declaredField.setAccessible(true);
// 如果私有属性,可以设置值
// 5. 如果有@Di注解,把对象进行注入
try {
BeanDefinition beanDefinition = getBeanNameByType(declaredField.getType());
if (Objects.isNull(beanDefinition)) {
declaredField.set(bean, null);
} else {
declaredField.set(bean, getBean(beanDefinition.getBeanName()));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
private BeanDefinition getBeanNameByType(Class clazz) {
Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
for (Map.Entry<String, BeanDefinition> entry : entries) {
if (entry.getValue().getBeanClass().equals(clazz)) {
return entry.getValue();
}
if (entry.getValue().getBeanClass().getInterfaces()[0].equals(clazz)) {
return entry.getValue();
}
}
return null;
}
}
ApplicationContext
我也模仿了Spring一样定义了一个ApplicationContext容器,获取Bean从这个容器获取,context容器再从BeanFactory中获取
public class AnnotationApplicationContext implements ApplicationContext {
/**
* 创建一个map集合,放bean对象
*/
private BeanFactory beanFactory;
@Override
public Object getBean(Class clazz) {
return beanFactory.getBean(clazz);
}
@Override
public Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
/**
* 设置包扫描规则
* 当前包及其子包,哪个类有@Bean注解,把这个类通过反射化
*/
public AnnotationApplicationContext(String basePackage) throws Exception {
this.beanFactory = getBeanFactory(basePackage);
}
private BeanFactory getBeanFactory(String baskPackage) {
return new BeanFactory(baskPackage);
}
}
测试,实现
@Bean
public class UserServiceImpl implements UserService {
@Di
private UserDao userDao;
@Override
public void add() {
System.out.println("service.........");
userDao.add();
}
}
public interface UserService {
void add();
}
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("dao add...");
}
}
public interface UserDao {
void add();
}
public class UserTest {
public static void main(String[] args){
try {
ApplicationContext context = new AnnotationApplicationContext("com.zly");
UserService userService = (UserService)context.getBean(UserService.class);
userService.add();
} catch (Exception e) {
e.printStackTrace();
}
}
}
相信你看到这里,就可以很容易的理解了这个丐版的Spring就算完成了,希望可以对大家有帮助啦👍👍👍