Spring源码 20 手写模拟源码
参考源
https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click
https://www.bilibili.com/video/BV12Z4y197MU?spm_id_from=333.999.0.0
《Spring源码深度解析(第2版)》
版本
本文章基于 Spring 5.3.15
项目地址
https://gitee.com/liao-hang/hand-write-spring.git
模拟 Spring
注解
自动装配
Autowired
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
组件
Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
组件扫描
ComponentScan
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
String value() default "";
}
范围
Scope
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "";
}
Bean 定义信息
BeanDefinition
public class BeanDefinition {
/**
* 类型
*/
private Class type;
/**
* 范围
*/
private String scope;
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
Bean 名称感知
BeanNameAware
public interface BeanNameAware {
/**
* 设置 Bean 名称
* @param beanName Bean名称
*/
void setBeanName(String beanName);
}
Bean 后置处理器
BeanPostProcessor
public interface BeanPostProcessor {
/**
* 初始化前
* @param beanName Bean名称
* @param bean Bean对象
* @return
*/
Object postProcessBeforeInitialization(String beanName, Object bean);
/**
* 初始化后
* @param beanName Bean名称
* @param bean Bean对象
* @return
*/
Object postProcessAfterInitialization(String beanName, Object bean);
}
初始化 Bean
InitializationBean
public interface InitializationBean {
/**
* 属性设置后
*/
void afterPropertiesSet();
}
注解配置应用上下文
AnnotationConfigApplicationContext
public class AnnotationConfigApplicationContext {
/**
* 配置的类
*/
private Class configClass;
/**
* Bean 后置处理器列表
*/
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
/**
* Bean 定义信息池
*/
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* 单例池
*/
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
public AnnotationConfigApplicationContext(Class configClass) {
this.configClass = configClass;
/*
扫描 -> BeanDefinition -> beanDefinitionMap
*/
// 是否有 @ComponentScan 注解
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
// 注解中的路径
String path = componentScan.value();
// 处理路径
path = path.replace(".", "/");
// 类加载器
ClassLoader classLoader = AnnotationConfigApplicationContext.class.getClassLoader();
// 资源
URL resource = classLoader.getResource(path);
// 文件
File file = new File(resource.getFile());
// 如果为目录
if (file.isDirectory()) {
// 文件列表
File[] files = file.listFiles();
for (File f : files) {
// 绝对路径
String fileName = f.getAbsolutePath();
// 是否为 class 文件
if (fileName.endsWith(".class")) {
// 类名 cn\sail\test\文件名
String className = fileName.substring(fileName.indexOf("cn"), fileName.indexOf(".class"));
// 处理路径
className = className.replace("\\", ".");
try {
Class<?> clazz = classLoader.loadClass(className);
// 是否有 @Component 注解
if (clazz.isAnnotationPresent(Component.class)) {
// 接口和类是否相同或者是否为其超类或超接口
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
beanPostProcessorList.add(instance);
}
Component component = clazz.getAnnotation(Component.class);
String beanName = component.value();
if ("".equals(beanName)) {
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
// 是否有 @Scope 注解
if (clazz.isAnnotationPresent(Scope.class)) {
// 如果有,以传入的模式为准
Scope scope = clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scope.value());
} else {
// 如果没有,默认为单例模式
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
// 实例化单例 Bean
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
// 如果为单例模式,将 BeanDefinition 放入单例池中,下次直接从池中取,以保证单例
if ("singleton".equals(beanDefinition.getScope())) {
Object bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, bean);
}
}
}
/**
* 创建 Bean
* @param beanName Bean 名称
* @param beanDefinition Bean 定义信息
* @return Bean 对象
*/
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
// 依赖注入
for (Field f : clazz.getDeclaredFields()) {
// 是否有 @Autowired 注解
if (f.isAnnotationPresent(Autowired.class)) {
// 忽略访问修饰符限制
f.setAccessible(true);
// 注入
f.set(instance, getBean(f.getName()));
}
}
// Aware
if (instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
// 初始化前
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
}
// 初始化
if (instance instanceof InitializationBean) {
((InitializationBean) instance).afterPropertiesSet();
}
// 初始化后
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(beanName, instance);
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new NullPointerException();
} else {
String scope = beanDefinition.getScope();
if ("singleton".equals(scope)) {
Object bean = singletonObjects.get(beanName);
// 如果单例池中没有对应 Bean,创建 Bean 后再放入池中
if (bean == null) {
bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, bean);
}
return bean;
} else {
// 如果不是单例模式,直接创建 Bean
return createBean(beanName, beanDefinition);
}
}
}
}
使用 Spring
应用配置
AppConfig
@ComponentScan("cn.sail.test")
public class AppConfig {
}
用户服务
UserInterface
public interface UserInterface {
/**
* 测试
*/
void test();
}
用户服务实现类
UserService
@Component
public class UserService implements UserInterface{
@Autowired
private OrderService orderService;
@Override
public void test() {
System.out.println(orderService);
}
}
排序服务
OrderService
@Component
public class OrderService {
}
Bean 后置处理器
MyBeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 初始化前
*
* @param beanName
* @param bean
* @return
*/
@Override
public Object postProcessBeforeInitialization(String beanName, Object bean) {
if ("userService".equals(beanName)) {
System.out.println("1111");
}
return bean;
}
/**
* 初始化后
*
* @param beanName
* @param bean
* @return
*/
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
if ("userService".equals(beanName)) {
Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑");
return method.invoke(bean, args);
}
});
return proxyInstance;
}
return bean;
}
}
测试
Test
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserInterface userService = (UserInterface) applicationContext.getBean("userService");
userService.test();
}
}
Spring源码 20 手写模拟源码的更多相关文章
- 手写koa-static源码,深入理解静态服务器原理
这篇文章继续前面的Koa源码系列,这个系列已经有两篇文章了: 第一篇讲解了Koa的核心架构和源码:手写Koa.js源码 第二篇讲解了@koa/router的架构和源码:手写@koa/router源码 ...
- 【Spring系列】- 手写模拟Spring框架
简单模拟Spring 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 前言 上次已经学习了 ...
- 《四 spring源码》手写springioc框架
手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...
- 《四 spring源码》手写springmvc
手写SpringMVC思路 1.web.xml加载 为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...
- 面试必会之ArrayList源码分析&手写ArrayList
简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于对元素进行查找,效率非常高. 线程 ...
- 手写Redux-Saga源码
上一篇文章我们分析了Redux-Thunk的源码,可以看到他的代码非常简单,只是让dispatch可以处理函数类型的action,其作者也承认对于复杂场景,Redux-Thunk并不适用,还推荐了Re ...
- Spring事务原理分析--手写Spring事务
一.基本概念和原理 1.Spring事务 基于AOP环绕通知和异常通知的 2.Spring事务分为编程式事务.声明事务.编程事务包括注解方式和扫包方式(xml) Spring事务底层使用编程事务(自己 ...
- Spring源码分析 手写简单IOC容器
Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...
- 源码分析 | 手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用)
作者:小傅哥 博客:https://bugstack.cn - 汇总系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 一个知识点的学习过程基本分为:运行helloworld ...
随机推荐
- C#获取PLC信息 (KepServer)
首先注册DLL 我这里把此DLL放到我自己的网站上了,供大家下载 https://blog.nwctwang.top/OPCDAAuto.dll 把此dll复制到C:\Windows\SysWOW64 ...
- 可靠的分布式KV存储产品-ETCD-初见
目录 Paxos Raft(Understandable Distributed Consensus) 名词介绍 Leader Election Log Replication 请求完整流程 etcd ...
- markdowm使用学习
markdowm学习 标题(#/##/###/####) 三级标题 四级标题 字体(*/) hello world! hello world! hello world! hello world! he ...
- HIPPO-4J 1.3.0 正式发布:支持 Dubbo、RibbitMQ、RocketMQ 框架线程池
文章首发在公众号(龙台的技术笔记),之后同步到个人网站:xiaomage.info Hippo-4J 距离上一个版本 1.2.1 已经过去一个月的时间.在此期间,由 8 位贡献者 提交了 170+ c ...
- Vmware 10~16激活码/序列号 汇总
Vmware 16 ZF3R0-FHED2-M80TY-8QYGC-NPKYF YF390-0HF8P-M81RQ-2DXQE-M2UT6 ZF71R-DMX85-08DQY-8YMNC-PPHV8 ...
- MAUI模板项目闪退问题
MAUI模板项目闪退问题 在MAUI最初发布的时候就曾创建过几个模板项目进行体验过,没遇到什么坑.由于最近需要开发针对餐饮行业的收银机(安卓系统)开发一款应用,这种收银机一般配置不咋滴,系统版本和性能 ...
- 集成学习——XGBoost(手推公式)
- skywalking链路监控
1. 下载安装包官网地址:http://skywalking.apache.org/downloads/ 2. tar xzf apache-skywalking-apm-6.5.0.tar.gz - ...
- Unity-A-Star寻路算法
最短路径 将地图存成二维数组,通过行列查找: 每一步都查找周围四个方向,或者八方向的所有点,放入开表: 是否边缘 是否障碍 是否已经在确定的路线中 计算每个方向上路径消耗,一般斜着走消耗小,收益大: ...
- 本机通过IP地址连接Ubuntu18.04+ on Vmware
一.Vmware-顶部菜单栏-编辑-虚拟网络编辑器: 点一下 添加一个NAT模式的网络:要记住名称,比如这里我的是VMnet8 子网ip可以自己写,建议全程就都按我这个写,后续方便校对. 点一下 NA ...