这套课程也有源码讲解,但个人觉得Spring5那套视频讲解思路更清晰,用什么依赖就导入什么依赖,这样才能知道,哪部分实际用到的依赖,而不是一股脑把需要用到的依赖都导进去;当然,这是学习期间的学习方法,实际工作中,肯定是要求依赖完整,最好一次性导入的,而且最好是使用Maven或Gradle那种依赖管理工具。
IoC容器对于Bean管理主要是①Bean的创建②属性注入
diy自己的IoC容器之Bean创建
先准备一个接口和接口实现类
public interface UserService {
void show();
}
这里已经加上了我们自定义的帽子(注解)了~~~
@MyBean
public class UserServiceImpl implements UserService {
@Override
public void show() {
System.out.println("UserServiceImpl...");
}
}
注解方式实现,于是先自定义注解吧
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBean {
}
仿BeanFactory接口的子接口ApplicationContext,自定义Bean工厂接口及实现类
public interface MyApplicationContext {
Object getBean(Class clazz);
}
简单介绍以下这部分的功能吧~主要是!包路径的解析,从包路径获取所有类的全路径名,据此创建自定义的BeanDefinitionMap。
public class MyAnnotationApplicationContext implements MyApplicationContext{
private static Map<Class, Object> MyBeanDefinition = new HashMap<>();
private static String rootPath;//根路径
@Override
public Object getBean(Class clazz) {
return MyBeanDefinition.get(clazz);
}
//设置包扫描规则
//带参构造,参数为包路径,eg.com.coffeeship 扫描包及子包下的所有类
public MyAnnotationApplicationContext(String basePackage) {
//包路径转换
String packagePath = basePackage.replaceAll("\\.", "\\\\");
try {
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// System.out.println(filePath);
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
loadBean(new File(filePath));
}
} catch (IOException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
if (file.isDirectory()) {
File[] childFiles = file.listFiles();
//若文件夹路径下没有任何文件夹或文件,则直接返回
if (childFiles == null || childFiles.length == 0) {
return;
}
for (File child : childFiles) {
//若是子文件夹,则递归
if (child.isDirectory()) {
loadBean(child);
} else {
String classPath = child.getAbsolutePath().substring(rootPath.length() - 1);
if (classPath.contains(".class")) {
String fullPath = classPath.replaceAll("\\\\", "\\.").replaceAll(".class", "");
Class<?> clazz = Class.forName(fullPath);
//判断当前引用数据类型是类还是接口
if (!clazz.isInterface()) {
MyBean myBean = clazz.getAnnotation(MyBean.class);
if (myBean != null) {
Object newInstance = clazz.getConstructor().newInstance();
//判断当前类是否有接口,若有接口,则接口是key,否则该类是key
if (clazz.getInterfaces().length > 0) {
MyBeanDefinition.put(clazz.getInterfaces()[0], newInstance);
} else {
MyBeanDefinition.put(clazz, newInstance);
}
}
}
}
}
}
}
}
}
测试
public class Main {
public static void main(String[] args) {
MyApplicationContext applicationContext = new MyAnnotationApplicationContext("com.coffeeship");
//通过接口获取Bean
UserService bean = (UserService) applicationContext.getBean(UserService.class);
bean.show();
}
}
diy自己的IoC容器之属性注入
再准备一个接口和接口实现类
public interface UserDao {
void show();
}
@MyBean
public class UserDaoImpl implements UserDao {
@Override
public void show() {
System.out.println("UserDaoImpl...");
}
}
在Service层注入Dao层属性,并调用Dao层的方法
@MyBean
public class UserServiceImpl implements UserService {
@MyDI
private UserDao userDao;
@Override
public void show() {
System.out.println("UserServiceImpl...");
userDao.show();
}
}
补充属性注入方法
private static void loadDi() {
//遍历BeanDefinitionMap,对每个Bean获取其所有属性,判断属性上是否有注解,若有则从Map中根据属性类型进行赋值
Set<Map.Entry<Class, Object>> entrySet = MyBeanDefinition.entrySet();
for (Map.Entry<Class, Object> entry : entrySet) {
Object obj = entry.getValue();
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field field : declaredFields) {
MyDI annotation = field.getAnnotation(MyDI.class);
if (annotation != null) {
field.setAccessible(true);
try {
field.set(obj, MyBeanDefinition.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
再次测试,成功输出Service层和Dao层的方法体