✨✨hello,愿意点进来的小伙伴们,你们好呐!
🐻🐻系列专栏:【JavaEE】
🐲🐲本篇内容:自己实现简单的Spring容器 ,实现功能 : IOC , BeanPostProcessor , AOP
🐯🐯作者简介:一名现大二的三非编程小白,日复一日,仍需努力。
😘😘gitee仓库地址 : gitee代码地址
在我们日常的学习中会发现有时候学习很多都是在学习API,停留于API的层面,这个是很不好的,因为我们学习要是都停留在表面的API上的话,那么只会使用,并不知道为何能这样子中,在使用的时候看到的永远是API,看不到底层的东西
因此在学习Spring的过程中,最好还是自己实现一个简易版本的Spring,不然看到的都是一堆配置和注解,这样子并不会进步
一图胜千言 : Spring剖析思路 :
这个实现思路的示意图先摆在这里,感兴趣的小伙伴可以看着我一步一步地实现Spring
扫描指定的包将Bean信息存入容器中
现在我们来实现这条线,读取bean.xml中配置的包路径,并扫描包,得到bean的class对象,然后将bean的信息封装到BeanDefinition中,并初始化bean池
-
我们先创建一个annotation 包,用来存放注解的.
-
在该包下面创建一个 ComponentScan 注解,用来使用注解指定要扫描的包, 创建Componet注解来表示要注入的对象,Scope注解表示是否为单例,Autowired 注解来依赖注入
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ComponentScan {
//value指定要扫描的包
String value() default "";
}
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Scope {
//这里要来填入是否单例
String value() default "singleton";
}
-
然后创建一个ioc包,用来存放与IOC容器有关的类
-
在ioc包下面创建一个LhjSpringConfig类,用来指定要扫描的包的注解,我们这里就不用xml文件了,要是想使用xml来配置要扫描的包路径的小伙伴可以使用dom4j来解析xml文件
@ComponentScan(value = "com_.lhjedu.spring.component")
public class LhjSpringConfig {
}
com_.lhjedu.spring.component 就是用来存放要存入Spring容器的bean的包
- ioc包中创建BeanDefinition 类,用来存放bean的信息,有scope和 class对象
public class BeanDefinition {
private String scope;
private Class clazz;
public String getSingleton() {
return scope;
}
public void setSingleton(String singleton) {
this.scope = singleton;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "BeanDefinition{" +
"scope='" + scope + '\'' +
", clazz=" + clazz +
'}';
}
}
- 在ioc包下面创建LhjSpringApplicationContext类,是Spring容器的主要类,用来封装bean信息,实例化bean,获取bean等功能 现在我们先来实现封装BeanDefinition放入map中,初始化单例池的功能
package com_.lhjedu.spring.ioc;
import com.lhjedu.spring.annotation.Autowired;
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
import com_.lhjedu.spring.annotation.Component;
import com_.lhjedu.spring.annotation.ComponentScan;
import com_.lhjedu.spring.annotation.Scope;
import com_.lhjedu.spring.anomaly.NotFoundBeanException;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 罗鸿基
* @version 1.0
*/
public class LhjSpringApplicationContext {
private Class configClass;
//用来存储bean对象的信息
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap();
private ConcurrentHashMap<String,Object> singletonObjects =
new ConcurrentHashMap();
public LhjSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描存入bean信息
beanDefinitionScan(configClass);
//初始化对象池
initializationBean();
}
private void initializationBean() {
//使用枚举来遍历
Enumeration<String> keys = beanDefinitionMap.keys();
//先将对象存入单例池
while (keys.hasMoreElements()) {
String beanId = keys.nextElement();
BeanDefinition beanDefinition = beanDefinitionMap.get(beanId);
if (beanDefinition == null) {
//如果没有该bean的信息
//那么报null异常
throw new NotFoundBeanException("没有该bean");
}
//判断是否单例
if ("singleton".equals(beanDefinition.getSingleton())) {
try {
//创建实例
Object bean = createBean(beanDefinition,beanId);
//存入单例池
singletonObjects.put(beanId,bean);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//创建对象
private Object createBean(BeanDefinition beanDefinition, String beanId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = beanDefinition.getClazz();
//创建对象
Object bean = clazz.getDeclaredConstructor().newInstance();
//判断是否依赖注入
//遍历要创建的对象的所有字段
//判断字段上是否有依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
//得到字段的名字
if (declaredField.isAnnotationPresent(com.lhjedu.spring.annotation.Autowired.class)) {
//获取注解的字段
com.lhjedu.spring.annotation.Autowired autowired = declaredField.getDeclaredAnnotation(Autowired.class);
boolean required = autowired.required();
if (required) {
//拿到依赖注入的字段名字
String name = declaredField.getName();
//再通过getBean来获取对象
Object obj = getBean(name);
declaredField.setAccessible(true);//因为属性是私有的,所以需要爆破
//进行组装, 创建 bean , bean中有个obj属性要来组装
declaredField.set(bean, obj);
}
}
}
return bean;
}
//这个方法来扫描包
private void beanDefinitionScan(Class configClass) {
//拿到注解
ComponentScan componentScan
= (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
//拿到注解的路径
String scanPath = componentScan.value();
scanPath = scanPath.replace(".", "/");
//拿到类加载器
ClassLoader classLoader = LhjSpringApplicationContext.class.getClassLoader();
//类加载器得到该扫描路径的url (全路径)
URL url = classLoader.getResource(scanPath);
/*file:/G:/HSPVIP/hsp-Java/java-web/spring_poject/lhj-myspring/target/classes/com_/lhjedu/spring/component*/
//通过类的路径获取该路径下的类
File file = new File(url.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f:
files) {
//拿到类的路径
String classAbsolutePath = f.getAbsolutePath();
//判断是不是.class后缀
if (classAbsolutePath.endsWith(".class")) {
//拿到类名
String className =
classAbsolutePath.substring(classAbsolutePath.lastIndexOf("\\") + 1,
classAbsolutePath.indexOf(".class"));
//拿到类路径
scanPath = scanPath.replace("/", ".");
String classScanPath = scanPath + "." + className;
//通过反来创建类的class对象
try {
Class<?> clazz = classLoader.loadClass(classScanPath);
//判断是否有要注入的注解
if (clazz.isAnnotationPresent(Component.class)) {
//然后要封装到singleton中
//先判断是否有指定id
Component component = clazz.getDeclaredAnnotation(Component.class);
String beanId = component.value();
if ("".equals(beanId)) {
//没有指定id
//id就是类名首字母小写
beanId = StringUtils.uncapitalize(className);
}
//到了这里id好了
//然后要来封装到BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//判断是否为单例,有单例多例的注解
String scope = "singleton";
if (clazz.isAnnotationPresent(Scope.class)) {
if ("prototype".equals(clazz.getDeclaredAnnotation(Scope.class).value())) {
//多例
scope = "prototype";
}
}
beanDefinition.setSingleton(scope);
beanDefinitionMap.put(beanId,beanDefinition);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
- 我们来编写getBean方法,getBean返回对象思路如下 :
public Object getBean(String beanId) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Object bean = null;
//判断是否有该bean
if (beanDefinitionMap.containsKey(beanId)) {
//如果有bean就判断是否为单例,看要创建还是获取
BeanDefinition beanDefinition = beanDefinitionMap.get(beanId);
String scope = beanDefinition.getSingleton();
if ("singleton".equals(scope)) {
bean = singletonObjects.get(beanId);
}else if ("prototype".equals(scope)){
bean = createBean(beanDefinition, beanId);
}
}else {
throw new NotFoundBeanException("该bean不存在");
}
return bean;
}
😘😘😘