Spring总体功能为扫描类文件,构成BeanDefinition,然后生成代理对象,对这些对象进行管理。
表面上看:
从日常spring使用上可以看出,
首先需要一个启动容器类:生成容器类对象是会需要一个参数(配置类或者配置文件),然后有个常用方法getBean();
然后是一些常用的注解:
@ComponentScan:根据定义的扫描路径,把符合扫描规则的类装配到spring容器
@Component:用于标记一个类为组件
@Scope:表明类的作用域,如singleton(单例)、prototype(多例);Web 作用域(reqeust、session、globalsession),自定义作用域。
@AutoWired:对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作
所以可先准备这些类。
具体步骤:
1、先创建一个容器类,包含一个参数,表示配置类,一个方法getBean();
public class ApplicationContext {
private Class configClass;
public ApplicationContext(Class configClass) {
this.configClass = configClass;
}
public Object getBean(String beanName){
return null;
}
}
2、然后是注解的声明,简单写表示意思即可
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
//扫描路径
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
//bean取名字用
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
//bean作用域
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}
3、spring启动后会进行扫描,会根据配置的扫描路径,将符合扫描规则的类装配到spring容器中,因此,spring容器的构造方法中会进行扫描。我这里是配置类上加@ComponentScan("com.xxx.ccc"),所以如下这样写。
public class ApplicationContext {
private Class configClass;
public ApplicationContext(Class configClass) {
this.configClass = configClass;
//扫描
//扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc
//扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
path = path.replace(".", "/");
ClassLoader classLoader = ApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service
File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
className = className.replace("\\", ".");
try {
//判断是不是一个bean -->看是不是有Component注解
Class<?> aClass = classLoader.loadClass(className); //参数需要这样的 com.xxx.service.UserService,所以做上边的字符串转换
if (aClass.isAnnotationPresent(Component.class)) {
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
public Object getBean(String beanName){
return null;
}
}
4、扫描完成后,就该生成对象了。但是考虑到spring中bean的作用域问题,以及在getBean()中不可能根据bean名字就知道从哪里拿bean对象的问题,所以下一步不是直接创建对象,而是创建包含类信息的对象。这时@Scope注解和BeanDefinition类就出现了,BeanDefinition:类的定义
/**
*至少两个参数吧
*/
public class BeanDefinition {
private Class type; //bean是哪个类的
private String scope; //bean作用域
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;
}
}
因此继续补充代码,ApplicationContext中新增参数Map类型变量beanDefinitionMap,key为bean的名字,value为BeanDefinition对象,存储BeanDefinition的信息。这之后我们在getBean()时,就可以通过这个map来获取bean的信息,从而可以知道从里获取bean对象。
public class ApplicationContext {
private Class configClass;
public ApplicationContext(Class configClass) {
this.configClass = configClass;
private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//扫描
//扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc
//扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
path = path.replace(".", "/");
ClassLoader classLoader = ApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service
File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
className = className.replace("\\", ".");
try {
//判断是不是一个bean -->看是不是有Component注解
Class<?> aClass = classLoader.loadClass(className); //参数需要这样的 com.xxx.service.UserService,所以做上边的字符串转换
if (aClass.isAnnotationPresent(Component.class)) {
Component component = aClass.getAnnotation(Component.class);
String beanName = component.value();
if(beanName.equals("")){
beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(aClass);
if (aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefinition.setScope(scope);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
public Object getBean(String beanName){
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new NullPointerException();
} else {
String scope = beanDefinition.getScope();
if (scope.equals("singleton")) {
} else {
//多例
}
}
return null;
}
}
5、这时就可以开始创建bean对象了。补充代码
ApplicationContext中新增参数Map类型变量singleObjects,key为bean的名字,value为bean对象
新增方法 createBean(String beanName , BeanDefinition beanDefinition) 用于专门创建bean。
补充getBean代码
public class ApplicationContext {
private Class configClass;
public ApplicationContext(Class configClass) {
this.configClass = configClass;
private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String ,Object> singleObjects = new ConcurrentHashMap<>();
//扫描
//扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc
//扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
path = path.replace(".", "/");
ClassLoader classLoader = ApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service
File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
className = className.replace("\\", ".");
try {
//判断是不是一个bean -->看是不是有Component注解
Class<?> aClass = classLoader.loadClass(className); //参数需要这样的 com.xxx.service.UserService,所以做上边的字符串转换
if (aClass.isAnnotationPresent(Component.class)) {
Component component = aClass.getAnnotation(Component.class);
String beanName = component.value();
if(beanName.equals("")){
beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(aClass);
if (aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefinition.setScope(scope);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
//实例化单例bean
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
Object bean = createBean(beanName, beanDefinition);
singleObjects.put(beanName,bean);
}
}
}
private Object createBean(String beanName , BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().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;
}
public Object getBean(String beanName){
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new NullPointerException();
} else {
String scope = beanDefinition.getScope();
if (scope.equals("singleton")) {
Object bean = singleObjects.get(beanName);
if (bean == null) {
bean = createBean(beanName, beanDefinition);
singleObjects.put(beanName,bean);
}
return bean;
} else {
//多例
return createBean(beanName,beanDefinition);
}
}
}
}
6、完善createBean()方法。
spring在创建bean时,有些变量加了@AutoWired,就需要依赖注入。
spring还提供了回调的功能,用于比如说告诉对象他自己的beanName。
回调功能需要使用Aware接口,需要回调功能的类需要实现Aware接口
spring还提供了初始化功能,用于比如说在创建时查询数据库信息给变量赋值。
初始化功能需要用的Initial接口,需要初始化功能的类需要实现Initial接口。
private Object createBean(String beanName , BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
//简单版依赖注入
for (Field f : clazz.getDeclaredFields()) {
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 InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}
//初始化后 AOP
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
7、spring还有一个重要内容是AOP。这个部分在bean初始化后进行。
这里spring提供了一个接口 BeanPostProcessor,其中包含方法
public Object postProcessBeforeInitialization(String beanName,Object bean); public Object postProcessAfterInitialization(String beanName,Object bean);
分别在bean初始化前后对生成的bean进行操作,初始化后的操作方法中可对bean进行aop。
BeanPostProcessor接口声明
public interface BeanPostProcessor {
public Object postProcessBeforeInitialization(String beanName,Object bean);
public Object postProcessAfterInitialization(String beanName,Object bean);
}
创建类实现BeanPostProcessor。
@Component
public class xxxBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(String beanName, Object bean) {
System.out.println("初始化前");
return bean;
}
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
System.out.println("初始化后");
if (beanName.equals("userService")) {
//AOP生成代理对象
Object proxyInstance = Proxy.newProxyInstance(LskApplicationContext.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;
}
}
修改容器类的扫描和bean生成代码。
新增数组专门存放 BeanPostProcessor 派生类
private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
扫描时就把扫描到的 BeanPostProcessor 派生类加入beanPostProcessorList
创建bean时,遍历beanPostProcessorList,对相应的类进行AOP生成代理对象
public class ApplicationContext {
private Class configClass;
public ApplicationContext(Class configClass) {
this.configClass = configClass;
private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String ,Object> singleObjects = new ConcurrentHashMap<>();
private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
//扫描
//扫描路径是java文件对应的class文件的路径,不是配置类上的声明的
if(configClass.isAnnotationPresent(ComponentScan.class)){ //判断是否有..注解
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); //这个时候还不是扫描路径,只是注解上配的java文件包路径 com.xxx.ccc
//扫描路径在编译路径-classpath里可找到,所以这里做字符串转换
path = path.replace(".", "/");
ClassLoader classLoader = ApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path); // 获取class所在文件资源file:/E:/Project/xxxSpring/out/production/xxxSpring/com/xxx/service
File file = new File(resource.getFile()); //E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();//长这样 E:\Project\xxxSpring\out\production\xxxSpring\com\xxx\service\AppConfig.class
if (fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
className = className.replace("\\", ".");
try {
//判断是不是一个bean -->看是不是有Component注解
Class<?> aClass = classLoader.loadClass(className); //参数需要这样的 com.xxx.service.UserService,所以做上边的字符串转换
if (aClass.isAnnotationPresent(Component.class)) {
if (BeanPostProcessor.class.isAssignableFrom(aClass)) { //判断类是否是由前者派生的
BeanPostProcessor instance = (BeanPostProcessor) aClass.newInstance();
beanPostProcessorList.add(instance);
}
Component component = aClass.getAnnotation(Component.class);
String beanName = component.value();
if(beanName.equals("")){
beanName = Introspector.decapitalize(aClass.getSimpleName()); //将类名首字母小写
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(aClass);
if (aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefinition.setScope(scope);
} else {
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
//实例化单例bean
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
Object bean = createBean(beanName, beanDefinition);
singleObjects.put(beanName,bean);
}
}
}
private Object createBean(String beanName , BeanDefinition beanDefinition){
Class clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
//简单版依赖注入
for (Field f : clazz.getDeclaredFields()) {
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 InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}
//初始化后 AOP
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 (scope.equals("singleton")) {
Object bean = singleObjects.get(beanName);
if (bean == null) {
bean = createBean(beanName, beanDefinition);
singleObjects.put(beanName,bean);
}
return bean;
} else {
//多例
return createBean(beanName,beanDefinition);
}
}
}
}
到此,基本就模拟结束spring的基本功能。
我是跟着这个视频细看敲打了一遍。
【这是我见过最好的Spring高级底层原理源码教程,大家觉得呢?(2022最新版)】 https://www.bilibili.com/video/BV1tR4y1F75R/?p=7&share_source=copy_web&vd_source=800dd7f74ab854b348d03ba8a1c229cd