手写Spring框架的目的:为了更了解Spring框架的执行流程和生命周期
Spring框架前提准备:
1 自定义的ApplicationContext
2 自定义的cofig配置类
3 自定义的注解(例如@ComponentScan,@Component,@Scope)
第一步:main()方法为入口(模拟getBean的过程)
public class ApplicationTest {
public static void main(String[] args) {
// 自定义的ApplicationContext
YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
// 从容器中要找到yuWenWenService的对象
yuWenWenApplicationContext.getBean("yuWenWenService");
}
}
第二步:自定义的ApplicationContext(此处的构造方法是要AppCofig配置类中的信息)
public class YuWenWenApplicationContext {
private Class config;
public YuWenWenApplicationContext(Class config) {
this.config = config;
}
第三步:自定义的注解加上配置类(@ComponentScan是自己写的注解并非spring原生注解)
//扫描包路径
@ComponentScan("com.sbi.service")
public class AppConfig {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String value();
}
第四步:自定义ApplicationContext的初始化方法逻辑
其实spring框架做的最重要的一件事,就是将一个类变成一个Bean的过程。
设计思路:
1 扫描ComponentScan的包路径
2 拿到包路径下面所有的带有@Component的注解的类
并且将这些类的名字和类型包装成BeanDefinition对象并且放到BeanDefinitionMap里
(我们视为持有这个注解的类将要被spring管理最终要成为一个bean)
3 将持有@Component注解的类通过反射的方式创建对象并放到一个单例池里
(遍历BeanDefinitionMap根据类描述通过反射创建对象,并且扔到单例池里)
,准备提供给getBean()使用
我们最终的目的其实就是要
通过反射的方式来创建对象
Class> aClass = classLoader.loadClass("com.sbi.service.YuWenWenService"); aClass.newInstance();
这块稍微需要考虑的点就是:
如何通过ComponentScan定义的包路径,拿到包路径下面的所有类的路径并且是以"xxx.xxx.xxx.xxx"的字符串形式传到这个classLoader.loadClass这个方法的参数里
private void scan(Class config) throws ClassNotFoundException {
// 此处得到@ComponentScan的扫描路径
ComponentScan path = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
// 得到一个类加载器 classpath下面用AppClassLoader
ClassLoader classLoader = YuWenWenApplicationContext.class.getClassLoader();
// 得到类的包路径(此处为com.sbi.service格式需要改成com/sbi/service)
URL resource = classLoader.getResource(path.value().replace(".", "/"));
// 得到包下的文件
File file = new File(resource.getFile());
if (file.isDirectory()) {
// 得到包下所有的文件信息
File[] files = file.listFiles();
for (File f : files) {
String absolutePath = f.getAbsolutePath();
String pathReplaceBefore = absolutePath
.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
String pathreplaceAfter = pathReplaceBefore.replace("\\", ".");
// 此处最终需要com.sbi.service.YuWenWenService格式
Class> aClass = classLoader.loadClass(pathreplaceAfter);
// 如果这个类拥有@Component注解,那么我们就要反射
if (aClass.isAnnotationPresent(Component.class)) {
Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
// 拿到@Component的value值也就是bean的名字
String springbeanName = declaredAnnotation.value();
// 描述类创建
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(aClass);
// 判断这个类是否还持有@Scope的其他作用域(spring 一共有四种作用域,默认不指定的情况下就是单例)
if (aClass.isAnnotationPresent(Scope.class)) {
Scope declaredAnnotation1 = aClass.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(declaredAnnotation1.value());
} else {
// spring默认不指定的情况下就是单例作用域
beanDefinition.setScope("singleton");
}
// 将类信息放到beanDefinitionMap里
beanDefinitionMap.put(springbeanName, beanDefinition);
}
}
}
}
// 单例池(key:beanName,value:bean)
private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>();
// BeanDefinitionMap(key:beanName,value:类描述)
private ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>();
第五步:getBean()实装
applicationContext根据beanName去单例池里面去拿自己的bean
public Object getBean(String beanName)
throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
//去beanDefinitionMap寻找是否有自己的类描述
if (beanDefinitionMap.containsKey(beanName)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//如果是单例bean,直接从单例池拿
if (beanDefinition.getScope().equals("singleton")) {
Object bean = singletonObjects.get(beanName);
return bean;
//如果是原型bean就给创建一个新的bean
} else {
Object bean = createBean(beanDefinition);
return bean;
}
//没招到的情况下就说明此类并没有加@Competent
} else {
throw new NullPointerException("此类不存在");
}
}
第六步:构造器初始化逻辑就是将以上的逻辑拼接起来
public YuWenWenApplicationContext(Class config)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
this.config = config;
// 扫描bean,并且得到所有beanDefinition放在beanDefinitionMap里
scan(config);
// 循环beanDefinitionMap全部
for (Map.Entry entry : beanDefinitionMap.entrySet()) {
if (entry.getValue().getScope().equals("singleton")) {
// 创建bean
Object bean = createBean(entry.getValue());
// 将创建好的bean放到单例池等待getBean调用
singletonObjects.put(entry.getKey(),bean);
}
}
}
测试一下
public class ApplicationTest {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
System.out.println(yuWenWenApplicationContext.getBean("yuWenWenService"));
System.out.println(yuWenWenApplicationContext.getBean("yuWenWenService"));
}
}
结果是两次拿到的都是同一个对象(这里边默认@Scope默认不设置就是单例)
成功的实现了利用简单工厂的一个单例模式。
com.sbi.service.YuWenWenService@799f7e29
com.sbi.service.YuWenWenService@799f7e29
第七步:依赖注入的功能实装
到第七步之前,已经可以实现了一个容器创建Bean的过程。
但Spring框架还有一个强大的功能就是依赖注入
自己定义个注解@Autowired
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
此刻你仅仅是在属性加上@Autowired注解,是肯定达不到依赖注入的功能的。
@Component("yuWenWenService")
public class YuWenWenService {
@Autowired
private MiaoWenWenService miaoWenWenService;
private void test() {
// 此刻不做任何逻辑 就是null
System.out.println(miaoWenWenService);
}
}
在刚刚的createBean方法中,我们已经成功的创建了一个对象,那么依赖注入的这一步部分逻辑就要写在创建对象后面
思想:拿到这个对象的属性,看看属性有没有持有@Autowired的注解,如果持有,就去单例池里面去拿到这个bean,并给自己的这个属性赋值。
private Object createBean(BeanDefinition beanDefinition)
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 第一步:创建对象
Object o = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
// 第二部:依赖注入
// 先通过反射拿到这个对象的私有属性
Field[] declaredFields = o.getClass().getDeclaredFields();
// 遍历私有属性
for (Field field : declaredFields) {
// 判断私有属性的是否持有@Autowired
if (field.isAnnotationPresent(Autowired.class)) {
// 如果持有@Autowired 就从单例池里拿这个bean并赋值
Object bean = getBean(field.getName());
// 默认是不允许设置私有属性的值的 此处必须得改成true
field.setAccessible(true);
// 实现依赖注入
field.set(o,bean);
}
}
return o;
}
测试一下
public class ApplicationTest {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
YuWenWenService yuWenWenService = (YuWenWenService) yuWenWenApplicationContext.getBean("yuWenWenService");
yuWenWenService.test();
}
}
结果成功的依赖注入
com.sbi.service.MiaoWenWenService@7aec35a
加入我此刻把 MiaoWenWenService的@Component去除掉,那么属性注入的时候 从单例池里根本找不到MiaoWenWenService这个bean应该就会失效 看看实际效果是不是如此
结果:成功了
Exception in thread "main" java.lang.NullPointerException: 此类不存在
以上便是实现了手写Spring框架最重要的两个功能 控制反转和依赖注入
所有源码如下↓(applicationContext)
package com.sbi.config;
import com.sbi.annotation.Autowired;
import com.sbi.annotation.Component;
import com.sbi.annotation.ComponentScan;
import com.sbi.annotation.Scope;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class YuWenWenApplicationContext {
private Class config;
// 单例池(key:beanName,value:bean)
private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>();
// BeanDefinitionMap(key:beanName,value:类描述)
private ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>();
public YuWenWenApplicationContext(Class config)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
this.config = config;
// 扫描bean,并且得到所有beanDefinition放在beanDefinitionMap里
scan(config);
// 循环beanDefinitionMap全部
for (Map.Entry entry : beanDefinitionMap.entrySet()) {
if (entry.getValue().getScope().equals("singleton")) {
// 创建bean
Object bean = createBean(entry.getValue());
// 将创建好的bean放到单例池等待getBean调用
singletonObjects.put(entry.getKey(), bean);
}
}
}
private Object createBean(BeanDefinition beanDefinition)
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 第一步:创建对象
Object o = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
// 第二部:依赖注入
// 先通过反射拿到这个对象的私有属性
Field[] declaredFields = o.getClass().getDeclaredFields();
// 遍历私有属性
for (Field field : declaredFields) {
// 判断私有属性的是否持有@Autowired
if (field.isAnnotationPresent(Autowired.class)) {
// 如果持有@Autowired 就从单例池里拿这个bean并赋值
Object bean = getBean(field.getName());
// 默认是不允许设置私有属性的值的 此处必须得改成true
field.setAccessible(true);
// 实现依赖注入
field.set(o, bean);
}
}
return o;
}
private void scan(Class config) throws ClassNotFoundException {
// 此处得到@ComponentScan的扫描路径
ComponentScan path = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
// 得到一个类加载器 classpath下面用AppClassLoader
ClassLoader classLoader = YuWenWenApplicationContext.class.getClassLoader();
// 得到类的包路径(此处为com.sbi.service格式需要改成com/sbi/service)
URL resource = classLoader.getResource(path.value().replace(".", "/"));
// 得到包下的文件
File file = new File(resource.getFile());
if (file.isDirectory()) {
// 得到包下所有的文件信息
File[] files = file.listFiles();
for (File f : files) {
String absolutePath = f.getAbsolutePath();
String pathReplaceBefore = absolutePath
.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
String pathreplaceAfter = pathReplaceBefore.replace("\\", ".");
// 此处最终需要com.sbi.service.YuWenWenService格式
Class> aClass = classLoader.loadClass(pathreplaceAfter);
// 如果这个类拥有@Component注解,那么我们就要反射
if (aClass.isAnnotationPresent(Component.class)) {
Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
// 拿到@Component的value值也就是bean的名字
String springbeanName = declaredAnnotation.value();
// 描述类创建
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(aClass);
// 判断这个类是否还持有@Scope的其他作用域(spring 一共有四种作用域,默认不指定的情况下就是单例)
if (aClass.isAnnotationPresent(Scope.class)) {
Scope declaredAnnotation1 = aClass.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(declaredAnnotation1.value());
} else {
// spring默认不指定的情况下就是单例作用域
beanDefinition.setScope("singleton");
}
// 将类信息放到beanDefinitionMap里
beanDefinitionMap.put(springbeanName, beanDefinition);
}
}
}
}
public Object getBean(String beanName)
throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
//去beanDefinitionMap寻找是否有自己的类描述
if (beanDefinitionMap.containsKey(beanName)) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//如果是单例bean,直接从单例池拿
if (beanDefinition.getScope().equals("singleton")) {
Object bean = singletonObjects.get(beanName);
return bean;
//如果是原型bean就给创建一个新的bean
} else {
Object bean = createBean(beanDefinition);
return bean;
}
//没招到的情况下就说明此类并没有加@Competent
} else {
throw new NullPointerException("此类不存在");
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)