文章目录
1. 内容大纲
- 手写Spring启动及扫描流程
- 手写getBean()流程
- 手写Bean生命周期流程
- 手写依赖注入流程
- 手写BeanPostProcessor机制
- 手写AOP机制
- 手写BeanFactory
- 手写事务
……
2. Spring用法复习
入门spring时,都是用ClassPathXmlApplicationContext这个容器来学习spring。
但是现在一般都会使用AnnotationConfigApplicationContext。
二者区别:
- ClassPathXmlApplicationContext需要传入spring.xml文件,即配置文件。通过
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml")
,创建一个spring容器,就可以从中拿到某一个bean对象。 - AnnotationConfigApplicationContext传入的配置文件参数为java类。本项目手写的方式为AnnotationConfigApplicationContext
3. 准备工作
3.1 项目结构
- 创建一个maven工程,pom.xml里什么都不用写
- main.java.com下创建包spring,存储我们手写的spring源码类
- main.java.com下创建包psy,可以创建main方法中的test类,测试源码
3.2 Spring包下源码类
- spring下创建PsyApplicationContext,表示spring里面的容器类,添加相关API:
- 添加属性
configClass
及其有参构造器 - 添加方法
getBean
,参数为String类型beanName
,返回值为Object类型,该方法先返回null
- 添加属性
- spring下创建ComponentScan,文件扫描类,添加属性Sting类型
value()
,定义扫描路径。添加注解@Retention(RetentionPolicy.RUNTIME)
和@Target(ElementType.TYPE)
- spring下创建Component,添加属性Sting类型
value()
,定义bean的名字,默认值为空,因为这个不一定非要写,如果没写spring会把首字母小写。注解同上。
package com.spring;
public class PsyApplicationContext {
private Class configClass;
public PsyApplicationContext(Class configClass){
this.configClass = configClass;
// 解析配置类
// ComponemtScan注解---->拿到扫描路径---->扫描
}
public Object getBean(String beanName){
return null;
}
}
package com.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value();
}
package com.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
3.3 psy包下模拟项目类
- psy.service包下新建UserService,添加注解
@Component("userService")
- psy下创建配置类AppConfig,添加注解
@ComponentScan("com.psy.service")
,定义扫描路径。 - psy下创建Test类,实现main方法,使用
PsyApplicationContext
传入配置类AppConfig
package com.psy.service;
import com.spring.Component;
@Component("userService")
public class UserService {
}
package com.psy;
import com.spring.ComponentScan;
@ComponentScan("com.psy.service")
public class AppConfig {
}
package com.psy;
import com.spring.PsyApplicationContext;
public class Test {
public static void main(String[] args){
PsyApplicationContext applicationContext = new PsyApplicationContext(AppConfig.class);
Object userService = applicationContext.getBean("userService");
}
}
4. Spring扫描逻辑
4.1 得到扫描路径下的所有bean
- 获取配置文件上的
ComponentScan.class
注解 - 通过注解得到扫描路径(包)
- 通过类加载器得到扫描路径(包)下所有的文件类,遍历所有类
- 去掉类名
com
前文本以及.class
后文本 - 再次通过类加载器得到class对象
- 判断当前class是否有注解
Component
- 去掉类名
public PsyApplicationContext(Class configClass){
this.configClass = configClass;
// 解析配置类
// ComponemtScan注解---->拿到扫描路径---->扫描
// 1. 获取配置文件类上的ComponentScan.class注解
ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);
// 2. 拿到扫描路径 com.psy.service (包名)
String path = componentScanAnnotation.value();
path = path.replace(".", "/");
System.out.println("扫描路径为:" + path);
// 3. 扫描:通过包名得到包下所有的类———>通过类加载器得到
// Bootsstrap--->jre/lib
// Ext---------->jre/ext/lib
// App---------->classpath
ClassLoader classLoader = PsyApplicationContext.class.getClassLoader(); //app
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
//System.out.println(f);
//System.out.println(f.getAbsolutePath());
String fileName = f.getAbsolutePath();
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
className = className.replace("\\", ".");
System.out.println(className);
try {
Class<?> clazz = classLoader.loadClass(className);
if (clazz.isAnnotationPresent(Component.class)) {
// 表示当前这个类是一個bean
// Class ---> bean? 不一定需要在这一步做
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
4.2 根据scope创建bean
-
spring下创建BeanDefinition,其中包含bean的定义,属性目前有bean的类
private Class clazz;
,以及bean是否为单例或其他private String Scope;
-
通过ConcurrentHashMap创建单例池(存储所有单例bean)和bean定义集合(存储所有bean的定义)
-
PsyApplicationContext下创建方法
creatBean
,可以使用beanDefinition创建bean。调用无参的构造方法反射得到实例对象Object instance = clazz.getDeclaredConstructor().newInstance();
-
提取扫描配置类逻辑为单独方法
scan
。得到扫描路径下所有类后,选择带有Component
注解的类,- 通过注解得到类名
beanName
- 实例化一个新的
BeanDefinition
- 将先前得到的
clazz
通过set方法赋予BeanDefinition
- 如果当前类有scope注解,将scope注解的值赋予
BeanDefinition
- 如果没有scope注解,将scope设为
singleton
- 将当前bean的
BeanDefinition
存入beanDefinitionMap
- 通过注解得到类名
-
扫描完配置类后,遍历
beanDefinitionMap
,如果当前``beanDefinitionMap`的Scope为singleton,即单例,就创建bean并将其放入单例池。 -
修改getBean方法:
- 如果当前bean是单例的,就从单例池map里拿
- 如果不是单例的,调用
creatBean
方法创建bean
package com.spring; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class PsyApplicationContext { private Class configClass; private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>(); //单例池 private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); //所有的bean的定义 public PsyApplicationContext(Class configClass){ this.configClass = configClass; // 解析配置类 // ComponemtScan注解--->拿到扫描路径--->扫描--->BeanDefinition--->ConcurrentHashMap scan(configClass); for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) { String beanName = entry.getKey(); BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if (beanDefinition.getScope().equals("singleton")){ Object bean = creatBean(beanDefinition); // 单例bean对象 singletonObjects.put(beanName, bean); // 放入单例池 } } } // 根据bean的定义创建bean的对象 public Object creatBean(BeanDefinition beanDefinition) { Class clazz = beanDefinition.getClazz(); try { Object instance = clazz.getDeclaredConstructor().newInstance();// 调用无参的构造方法反射得到实例对象 return instance; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; } private void scan(Class configClass) { // 1. 获取配置文件类上的ComponentScan.class注解 ComponentScan componentScanAnnotation = (ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class); // 2. 拿到扫描路径 com.psy.service (包名) String path = componentScanAnnotation.value(); path = path.replace(".", "/"); System.out.println("扫描路径为:" + path); // 3. 扫描:通过包名得到包下所有的类———>通过类加载器得到 // Bootsstrap--->jre/lib // Ext---------->jre/ext/lib // App---------->classpath ClassLoader classLoader = PsyApplicationContext.class.getClassLoader(); //app URL resource = classLoader.getResource(path); File file = new File(resource.getFile()); if(file.isDirectory()) { File[] files = file.listFiles(); for (File f : files) { //System.out.println(f); //System.out.println(f.getAbsolutePath()); String fileName = f.getAbsolutePath(); if (fileName.endsWith(".class")) { String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")); className = className.replace("\\", "."); System.out.println(className); try { Class<?> clazz = classLoader.loadClass(className); if (clazz.isAnnotationPresent(Component.class)) { // 有Component注解表示当前这个类是一个bean // 解析类----->BeanDefinition // BeanDefinition:bean的定义 Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class); String beanName = componentAnnotation.value(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setClazz(clazz); if (clazz.isAnnotationPresent(Scope.class)) { //有scope注解 Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class); beanDefinition.setScope(scopeAnnotation.value()); //用户设置的值配入beanDefinition } else { //没有scope注解 beanDefinition.setScope("singleton"); } beanDefinitionMap.put(beanName, beanDefinition); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } public Object getBean(String beanName){ if (beanDefinitionMap.containsKey(beanName)) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if (beanDefinition.getScope().equals("singleton")){ // 如果是单例的,就从单例池里拿 Object o = singletonObjects.get(beanName); return o; } else { // 创建Bean对象 Object bean = creatBean(beanDefinition); return bean; } } else { // 不存在对应的bean throw new NullPointerException(); } } }