项目地址

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();
} }

参考

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

Spring源码 20 手写模拟的更多相关文章

  1. Spring源码 20 手写模拟源码

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  2. 《四 spring源码》手写springioc框架

    手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...

  3. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

  4. Spring源码分析 手写简单IOC容器

    Spring的两大特性就是IOC和AOP. IOC Container,控制反转容器,通过读取配置文件或注解,将对象封装成Bean存入IOC容器待用,程序需要时再从容器中取,实现控制权由程序员向程序的 ...

  5. 源码分析 | 手写mybait-spring核心功能(干货好文一次学会工厂bean、类代理、bean注册的使用)

    作者:小傅哥 博客:https://bugstack.cn - 汇总系列原创专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 一个知识点的学习过程基本分为:运行helloworld ...

  6. 面试必会之ArrayList源码分析&手写ArrayList

    简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于对元素进行查找,效率非常高. 线程 ...

  7. Spring源码试读--BeanFactory模拟实现

    动机 现在Springboot越来越便捷,如果简单的Spring应用,已无需再配置xml文件,基本可以实现全注解,即使是SpringCloud的那套东西,也都可以通过yaml配置完成.最近一年一直在用 ...

  8. Spring源码学习之:模拟实现BeanFactory,从而说明IOC容器的大致原理

    spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后 ...

  9. 框架源码系列十二:Mybatis源码之手写Mybatis

    一.需求分析 1.Mybatis是什么? 一个半自动化的orm框架(Object Relation Mapping). 2.Mybatis完成什么工作? 在面向对象编程中,我们操作的都是对象,Myba ...

  10. MVVM模式源码分析手写实现

    1.demo1.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

随机推荐

  1. 【Azure Key Vault】在Alteryx中使用Azure Key Vault存储账号和密码并实现无交互登录

    问题描述: 需要在Alteryx中使用Azure Key Vault, Alteryx 能将数据发布到 Tableau,需要输入账号和密码,使用Azure Key Vault来替换这个输入账号和密码的 ...

  2. 【Azure Redis 缓存 Azure Cache For Redis】Azure Redis删除 TLS 1.0 和 1.1的计划及问题

    问题描述 Azure Redis 正式关闭TLS1.0和1.1关闭的时间 根据文档描述Azure Redis阶段2我们将停止支持 TLS 1.1 和 TLS 1.0,暂时计划 2020 年 12 月 ...

  3. 【Azure 云服务】Azure Cloud Service 关于虚拟机资源,杀毒软件配置,补丁机制的问答

    一:云服务后端是2台虚拟机,Work Role,Web Role,可以RDP进去,但是为什么在虚拟机列表里看不到呢? 因为云服务(经典)是PaaS服务,有别于传统的IaaS服务,其提供的WebRole ...

  4. goland快键键防忘

    环境: debian下的goland 2018实测: 鼠标button2 == 鼠标中键 光标移动: 跳转光标到刚才的位置: ctrl+win+alt+左/右 按词左右移动光标: ctrl + 左/右 ...

  5. [Linux] ubuntu系统使用zfs记录

    关于zfs 之前从B站视频里面了解到了btrfs这个文件系统,说是能够实现cow,然后我第一时间觉得这是个极好的特性,结果后来才发现,使用的时候并不多,而且只能通过cp --reflink的方式使用, ...

  6. 十四: Mysql数据结构选择的合理性

    Mysql数据结构选择的合理性 从MySQL的角度讲,不得不考虑一个现实问题就是磁盘I/O. 如果我们能让索引的数据结构尽量减少硬盘的I/O操作,所消耗的时间也就越小.可以说,磁盘的I/O操作次数对索 ...

  7. BeanShell 如何加密加签?

    一 首先我们要搞清楚接口签名步骤: **第一步:初步实现接口****第二步:找开发拿到算法和key** * key:sAHDRNJg0ZevmEn7HwBfbw== * 算法:HmacMD5 咱们就找 ...

  8. Codeforces Round 927 (Div. 3)(A~F)

    目录 A B C D E F A 第一个遇到连续两个荆棘的地方就不能再赢金币了. 所以统计连续两个荆棘之前的所有金币 #include <bits/stdc++.h> #define in ...

  9. coding 的pages 静态要六分钱一个月,我也是醉了。

    前言 前几天,收到个短信,说coding 竟然免费开放 动态pages,我这心说 NB啊,这咱得蹭上一波热度. github 总是打不开 gitee 每次pages都要手动点下发布,而且是静态的. c ...

  10. 英语单词 重读 注意第六条 类似tion前面的重读这种的

    单词音节重读的10个基本判断规则: 1.一个单词只有一个重读音节 无论该单词有多少个音节(syllable),其重读音节只有一个,而且都在元音上,辅音不重读.单音节词也重读,只是省略了重音符号.如:b ...