Spring之手撕框架核心逻辑
Posted 愉悦滴帮主)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring之手撕框架核心逻辑相关的知识,希望对你有一定的参考价值。
Spring之手撕框架核心逻辑
前言:
相比大多数开发者都用过Spring,但是由于工作性质原因一直没有时间或经历去研究和整理Spring底层到底是怎么运作的,核心逻辑又是什么。下面小编通过手写一套简单的Spring框架底层逻辑,帮助大家更好的认识Spring,也为想要冲击大厂的小伙伴提供一些文章资料。
相关项目资源已经上传。下载地址:https://download.csdn.net/download/qq_45065241/19496977?spm=1001.2014.3001.5503
手写实现Spring的扫描逻辑
我们知道Spring在启动的时候,会去通过@ComponentScan注解提供的路径去扫描对应路径下带有bean标识的注解(例如:@bean、@Component、@Service等等),然后将带有这些注解的类实例化成bean对象,然后将这些bean对象交给Spring容器去管理。
下面我们来手动实现Spring的扫描逻辑。
1. 我们在启动Spring的时候一般会通过ClassPathXmlApplicationContext 解析XML文件来启动Spring,或者使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文。使用AnnotationConfigApplicationContext的好处可以避免使用application.xml进行配置,而是通过注解的形式。相比XML配置,更加便捷。
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("config.xml");
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfigure.class);
2. 想要启动Spring我们需要手动创建一个AnnotationConfigApplicationContext类与一个配置类。代码如下:
创建TguoConfigApplicationContext 类来模拟AnnotationConfigApplicationContext类作为Spring的启动类。通过构造方法对属性进行赋值。
public class TguoConfigApplicationContext {
private Class configClass;
public TguoConfigApplicationContext(Class configClass) {
this.configClass = configClass;
}
}
自定义配置类ApplicationConfigure 。注意其中的@Configuration、@ComponentScan注解都需要手动实现。
@Configuration
@ComponentScan("com.tguo.demo.service")
public class ApplicationConfigure {
}
2.Spring在扫描的时候会通过@ComponentScan注解指定扫描路径,然后去扫描带有@Compoment注解的.Class文件。所以我们需要手动建立@ComponentScan,@Compoment注解。代码如下:
我们需要提供一个属性字段来帮助开发者指定扫描路径。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() default "";
}
我们需要提供一个属性字段来帮助开发者指定类的beanName。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
3.但是随着业务的不断扩展,有些bean我们需要的不是单例的,有些bean我们需要getBean的时候才会去创建。Spring为我们提供了两个类:一个是@Scope用来指定模式,一个是@Lazy用来作懒加载。我们自定义这两个注解,代码如下:
@Scope注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value() default "singleton";
}
@Lazy 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Lazy {
}
4.准备工作暂时完毕,这时我们实例化TguoConfigApplicationContext并运行我们的模拟启动类。这时TguoConfigApplicationContext会去解析我们提供的配置类ApplicationConfigure 。然后将解析出来的注解信息放入我们的TguoConfigApplicationContext类的Class字段中。步骤:1.解析@ComponentSacn注解得到路径。2.扫描路径下的所有.Class文件。3.将文件放入List集合。代码如下:
/**
* 扫描,得到class
*
* @param configClass
* @return
*/
private List<Class> scan(Class configClass) {
//1.得到扫描路径
//将扫描得到的所有.Class文件放入集合中
List<Class> classList = new ArrayList<>();
//得到@ComponentScan注解的值
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value();
//2.去该路径下扫描对应的Class文件
//将路径转换为目录路径形式
path = path.replace(".", "/");
//获取TguoConfigApplicationContext的类加载器,通过类加载器去获取对应路径目录的Class文件
ClassLoader classLoader = TguoConfigApplicationContext.class.getClassLoader();
URL url = classLoader.getResource(path);
File file = new File(url.getFile());
//判断该文件是否为一个文件夹
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
//我们得到了Class文件,然后进行Class文件的加载
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("\\\\", ".");
try {
Class<?> clazz = classLoader.loadClass(absolutePath);
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return classList;
}
以上我们的扫描逻辑就写好了。
手写实现beanDefination的创建
通过了解过bean的生命周期,我们知道在拿到.Class文件之后我们需要创建对应的beanDefiniation。代码如下:
不了解bean生命周期的小伙伴请参考:https://blog.csdn.net/qq_45065241/article/details/117597180
简述过程:1.遍历所有的.Class文件。2.创建BeaDefinition 对象。 3.将BeaDefinition 对象放入Map中。
public class BeaDefinition {
private Class beanClass;
private String scope;
private Boolean isLazy;
......省略set,get方法
}
public class TguoConfigApplicationContext {
private Class configClass;
//要来存放BeaDefinition
private Map<String, BeaDefinition> beaDefinitionMap = new ConcurrentHashMap<>();
public TguoConfigApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描,得到class
List<Class> ClassList = scan(configClass);
//解析这些类----》beaDefinition---》beaDefinitionMap
//过滤,在实例化bean之前要先判断是否有Component注解
for (Class clazz : ClassList) {
//如果Class含有Component注解
if (clazz.isAnnotationPresent(Component.class)) {
//生成BeaDefinition 对象
BeaDefinition beaDefinition = new BeaDefinition();
beaDefinition.setBeanClass(clazz);
Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
String beanName = componentAnnotation.value();
//如果有Scope注解
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
beaDefinition.setScope(scopeAnnotation.value());
}
//否则表示单例的
else {
beaDefinition.setScope("singleton");
}
beaDefinitionMap.put(beanName, beaDefinition);
}
}
//基于Class去创建单例bean,实例化单例bean
instanceSingletonBean();
}
}
手写实现Spring中的bean初始化的实现原理
根据bean的生命周期,我们知道在创建完所有的BeanDefination对象之后需要实例化。代码如下:
通过类加载器去实例化bean。实例化之后需要进行属性填充-->Aware回调---->初始化。
/**
* 实例化单例bean
*
* @param beanName
* @param beaDefinition
* @return
*/
private Object doCreateBean(String beanName, BeaDefinition beaDefinition) {
try {
//1.实例化
Class beanClass = beaDefinition.getBeanClass();
Object bean = beanClass.getDeclaredConstructor().newInstance();
//2.属性填充
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
//判断属性是否有Autowired注解
if (field.isAnnotationPresent(Autowired.class)) {
//属性赋值
Object o = getBean(field.getName());
field.setAccessible(true);
field.set(bean, o);
}
}
//3.Aware
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
//初始化之前BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
}
//4.初始化
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
//初始化之后BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
}
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
手写实现@Autowired注解的实现逻辑
我们在用Spring开发中,创建一个类可能需要依赖其他的类,这时就需要@Autowired注解。例如:
@Component("userService")
public class UserService{
@Autowired
private User user;
private String beanName;
}
在上面代码中,我们的UserService需要依赖User类。也就是通过@Autowired注解从Spring容器中获取到User对应的bean对象,赋值给UserService类中的User属性。所以我们需要创建@Autowired注解。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
我们需要在实例化bean的过程中,也就是对原始对象填充属性的时候判断这个bean的字段中是否包含@Autowired注解。如果存在调用getBean的方法。
//2.属性填充
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
//判断属性是否有Autowired注解
if (field.isAnnotationPresent(Autowired.class)) {
//属性赋值
Object o = getBean(field.getName());
field.setAccessible(true);
field.set(bean, o);
}
}
getBean的方法如下:
逻辑:判断这个bean是单例的还是原型的,如果是单例的则取单例池中获取,获取不到则创建bean,如果是原型的则直接创建一个bean并返回。
/**
* 获取bean实例对象
*
* @param beanName
* @return
*/
public Object getBean(String beanName) {
BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
//如果beaDefinition是原型的则创建bean
if (beaDefinition.getScope().equals("propotype")) {
//创建bean
Object bean = doCreateBean(beanName, beaDefinition);
return bean;
}
//如果beaDefinition是单例的则去单例池中获取bean
else if (beaDefinition.getScope().equals("singleton")) {
Object bean = singletonObjects.get(beanName);
if (bean == null) {
Object bean1 = doCreateBean(beanName, beaDefinition);
return bean1;
}
return bean;
}
return null;
}
此上就完成了Spring中bean的创建并放入Spring容器中。
全部核心代码如下:
package com.tguo.spring;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class TguoConfigApplicationContext {
//配置类
private Class configClass;
//要来存放BeaDefinition
private Map<String, BeaDefinition> beaDefinitionMap = new ConcurrentHashMap<>();
//单例池
private Map<String, Object> singletonObjects = new ConcurrentHashMap();
//用来存放BeanPostProcessor
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
//构造方法,实例化的时候执行
public TguoConfigApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描,得到class
List<Class> ClassList = scan(configClass);
//解析这些类----》beaDefinition---》beaDefinitionMap
//过滤,在实例化bean之前要先判断是否有Component注解
for (Class clazz : ClassList) {
//如果Class含有Component注解
if (clazz.isAnnotationPresent(Component.class)) {
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
try {
BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
beanPostProcessorList.add(instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
BeaDefinition beaDefinition = new BeaDefinition();
beaDefinition.setBeanClass(clazz);
Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
String beanName = componentAnnotation.value();
//如果有Scope注解
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
beaDefinition.setScope(scopeAnnotation.value());
}
//否则表示单例的
else {
beaDefinition.setScope("singleton");
}
beaDefinitionMap.put(beanName, beaDefinition);
}
}
//基于Class去创建单例bean,实例化单例bean
instanceSingletonBean();
}
/**
* 扫描,得到class
*
* @param configClass
* @return
*/
private List<Class> scan(Class configClass) {
//1.得到扫描路径
List<Class> classList = new ArrayList<>();
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value();
//2.去该路径下扫描对应的Class文件
//将路径转换为目录路径形式
path = path.replace(".", "/");
//获取TguoConfigApplicationContext的类加载器,通过类加载器去获取对应路径目录的Class文件
ClassLoader classLoader = TguoConfigApplicationContext.class.getClassLoader();
URL url = classLoader.getResource(path);
File file = new File(url.getFile());
//判断该文件是否为一个文件夹
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
//我们得到了Class文件,然后进行Class文件的加载
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("\\\\", ".");
try {
Class<?> clazz = classLoader.loadClass(absolutePath);
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return classList;
}
/**
* 获取bean实例对象
*
* @param beanName
* @return
*/
public Object getBean(String beanName) {
BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
//如果beaDefinition是原型的则创建bean
if (beaDefinition.getScope().equals("propotype")) {
//创建bean
Object bean = doCreateBean(beanName, beaDefinition);
return bean;
}
//如果beaDefinition是单例的则去单例池中获取bean
else if (beaDefinition.getScope().equals("singleton")) {
Object bean = singletonObjects.get(beanName);
if (bean == null) {
Object bean1 = doCreateBean(beanName, beaDefinition);
return bean1;
}
return bean;
}
return null;
}
/**
* 校验并实例化单例bean
*/
public void instanceSingletonBean() {
for (String beanName : beaDefinitionMap.keySet()) {
BeaDefinition beaDefinition = beaDefinitionMap.get(beanName);
if (beaDefinition.getScope().equals("singleton")) {
//校验单例池中是否已经存在bean
if (!singletonObjects.containsKey(beanName)) {
//创建bean
Object bean = doCreateBean(beanName, beaDefinition);
//将bean放入单例池中
singletonObjects.put(beanName, bean);
}
}
}
}
/**
* 实例化单例bean
*
* @param beanName
* @param beaDefinition
* @return
*/
private Object doCreateBean(String beanName, BeaDefinition beaDefinition) {
try {
//1.实例化
Class beanClass = beaDefinition.getBeanClass();
Object bean = beanClass.getDeclaredConstructor().newInstance();
//2.属性填充
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
//判断属性是否有Autowired注解
if (field.isAnnotationPresent(Autowired.class)) {
//属性赋值
Object o = getBean(field.getName());
field.setAccessible(true);
field.set(bean, o);
}
}
//3.Aware
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
//初始化之前BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
}
//4.初始化
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
//初始化之后BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
}
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
}
简述BeanNameAware接口。
BeanNameAware接口是提供给开发者在bean创建过程,也就是执行生命周期的过程中调用的接口。例如:
@Component("userService")
public class UserService implements BeanNameAware {
@Autowired
private User user;
private String beanName;
public void test() {
System.out.println(beanName);
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
我们需要在UserService创建实例化之后完成对UserService的属性beanName赋值。这时我们就可以实现BeanNameAware 接口。该接口的作用就是在bean实例化之后去回调,从而再次对bean对象进行属性赋值。
public Object doCreateBean(String beanName, BeaDefinition beaDefinition){
.....此处省略
//3.Aware
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
.....此处省略
return bean;
}
简述InitializingBean接口
InitializingBean接口与BeanNameAware类似,接口是提供给开发者在bean创建过程,也就是执行生命周期的过程中调用的接口。例如:
@Component("userService")
public class UserService implements BeanNameAware , InitializingBean {
@Autowired
private User user;
private String userName;
public void test() {
System.out.println(userName);
}
@Override
public void afterPropertiesSet() {
this.userName = "xxxx";
}
}
手写实现BeanPostprocessor的实现原理
BeanPostProcessor 接口中有两个默认方法,一个是postProcessBeforeInitialization该方法在初始化bean之前调用。一个是postProcessAfterInitialization该方法在初始化bean之后调用。
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean,String beanName);
Object postProcessAfterInitialization(Object bean,String beanName);
}
范例:
@Component
public class TguoBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化之前");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化之后");
return bean;
}
}
public Object doCreateBean(String beanName, BeaDefinition beaDefinition){
.....此处省略
//初始化之前BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
}
//4.初始化
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
//初始化之后BeanPostProcessor
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
}
.....此处省略
}
以上是关于Spring之手撕框架核心逻辑的主要内容,如果未能解决你的问题,请参考以下文章