🎉🎉本文是基于尚硅谷的Spring6教程的笔记,希望对正在学习的你有一定帮助。
03spring6-ioc-demo
开发环境
- Java17
- Maven3.8.5
前言:
Spring框架的IOC是基于Java反射机制实现,所以要自己实现一个Spring-IOC容器,需要了解反射。此文篇幅有限,不再讨论反射。
目录结构:
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─coding
│ │ │ ├─annotation
│ │ │ ├─bean
│ │ │ ├─dao
│ │ │ ├─service
│ │ │ └─test
│ │ └─resources
│ └─test
│ └─java
Task
- 自己定义@Bean注解
- 自己定义@DI注解
准备
dao
package com.coding.dao;
/**
* @time 2023-02-21-20:38
*/
public interface UserDao {
void add();
}
package com.coding.dao;
import com.coding.annotation.Bean;
/**
* @time 2023-02-21-20:39
*/
@Bean
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("自定义bean dao.......");
}
}
service
package com.coding.service;
/**
* @time 2023-02-21-20:39
*/
public interface UserService {
void add();
}
package com.coding.service;
import com.coding.annotation.Bean;
import com.coding.annotation.DI;
import com.coding.dao.UserDao;
/**
* @time 2023-02-21-20:39
*/
@Bean
public class UserServiceImpl implements UserService {
@DI
private UserDao userDao;
@Override
public void add() {
System.out.println("自定义bean service........");
userDao.add();
}
}
自定义注解
annotation
package com.coding.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @time 2023-02-22-15:20
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
package com.coding.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @time 2023-02-22-15:20
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DI {
}
定义bean容器接口
package com.coding.bean;
/**
* @time 2023-02-22-15:22
*/
public interface ApplicationContext {
Object getBean(Class clazz);
}
public class AnnotationApplicationContext implements ApplicationContext {
private Map<Class, Object> beanFactoryMap = new HashMap<>();
@Override
public Object getBean(Class clazz) {
return beanFactoryMap.get(clazz);
}
}
构造器基本逻辑
//扫描规则
public AnnotationApplicationContext(String basePackagePath) {
//包扫描
try {
//把.替换成斜杠\
String packagePath = basePackagePath.replaceAll("\\.", "\\\\");
//获取包的绝对路径
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() - basePackagePath.length());
//扫描包
loadBean(new File(filepath));
}
} catch (Exception e) {
e.printStackTrace();
}
//属性注入
loadDI();
}
主要是实现loadBean方法和loadDI方法
思路:
private void loadBean(File file) {
//1判断是否是文件夹
//2获取文件夹中所有内容
//3判断文件夹里面为空,直接返回
//4如果文件夹里面不为空,遍历文件夹所有内容
//4.1 遍历得到每个FiLe对象,继续判断,如果还是文件夹,递归
//4.2遍历得到FiLe对象不是文件夹,是文件,
//4.3得到包路径+类名称部分-字符串截取
//4.4判断当前文件类型是否.cLass
//4.5如果是.cLass类型,把路径\替换成. 把 .class去掉
//4.6判断类上是否有@Bean注解,有就实例化
//4.6.1获取类的Class
//4.6.2判断是不是接口
//4.6.3判断类上是否有@Bean注解
//4.6.4实例化
//4.7实例化后放到map集合beanFactoryMap里面
//如果有接口把接口的class当成key,实例对象当成value
//如果没有接口把自己的class当成key,实例对象当成value
}
//属性注入
private void loadDI(){
//实例化对象在 beanFactoryMap 的map集合里面
//1 遍历 beanFactoryMap map集合
//2获取map集合每个对象(value) ,每个对象属性获取到
//3 遍历得到每个对象属性数组,得到每个属性
//4判断属性上面是否有@DI注解
//私有属性 ,设置可以访问
//5如果有@DI注解,把对象进行设置(注入)
}
实现:
//包扫描
private void loadBean(File file) throws Exception {
//1判断是否是文件夹
if (file.isDirectory()) {
//2获取文件夹中所有内容
File[] childrenFiles = file.listFiles();
//3判断文件夹里面为空,直接返回
if (childrenFiles == null || childrenFiles.length == 0) {
return;
}
//4如果文件夹里面不为空,遍历文件夹所有内容
for (File childFile : childrenFiles) {
//4.1 遍历得到每个FiLe对象,继续判断,如果还是文件夹,递归
if (childFile.isDirectory()) {
loadBean(childFile);
} else {
//4.2遍历得到FiLe对象不是文件夹,是文件,
//4.3得到包路径+类名称部分-字符串截取
String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);
//4.4判断当前文件类型是否.class
if (pathWithClass.endsWith(".class")) {
//4.5如果是.cLass类型,把路径\替换成. 把 .class去掉
String allName = pathWithClass.substring(0, pathWithClass.length() - 6).replaceAll("\\\\", ".");
//4.6判断类上是否有@Bean注解,有就实例化
//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实例化
Object instance = clazz.getConstructor().newInstance();
//4.7实例化后放到map集合beanFactoryMap里面
if (clazz.getInterfaces().length > 0) {
//如果有接口把接口的class当成key,实例对象当成value
beanFactoryMap.put(clazz.getInterfaces()[0], instance);
} else {
//如果没有接口把自己的class当成key,实例对象当成value
beanFactoryMap.put(clazz, instance);
}
}
}
}
}
}
}
}
//属性注入
private void loadDI() {
//实例化对象在 beanFactoryMap 的map集合里面
//1 遍历 beanFactoryMap map集合
Set<Map.Entry<Class, Object>> entries = beanFactoryMap.entrySet();
for (Map.Entry<Class, Object> entry : entries) {
//2获取map集合每个对象(value) ,每个对象属性获取到
Object obj = entry.getValue();
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
//3 遍历得到每个对象属性数组,得到每个属性
for (Field field : declaredFields) {
//4判断属性上面是否有@DI注解
DI annotation = field.getAnnotation(DI.class);
if (annotation != null) {
//私有属性 ,设置可以访问
field.setAccessible(true);
//5如果有@DI注解,把对象进行设置(注入)
try {
field.set(obj, beanFactoryMap.get(field.getType()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
测试:
package com.coding.test;
import com.coding.bean.AnnotationApplicationContext;
import com.coding.bean.ApplicationContext;
import com.coding.service.UserService;
/**
* @time 2023-02-22-16:23
*/
public class TestBean {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.coding");
UserService userServiceClass = (UserService) context.getBean(UserService.class);
System.out.println(userServiceClass);
userServiceClass.add();
}
}
结果:
com.coding.service.UserServiceImpl@421faab1
自定义bean service........
自定义bean dao.......
1535

被折叠的 条评论
为什么被折叠?



