1、ApplicationContext和AppConfig
我们首先需要模拟的是spring的自动扫描机制,我们来想一下,当spring容器启动,自动扫描的时候,首先是肯定不能缺少启动类的,也就是我们的ApplicationContext,所以我们先创建一个ApplicationContext。
回想一下,我们之前手动去配置spring.xml的时候,是要通过启动配置ApplicationContext将配置文件进行读取,所以我们先要写一个spring的配置类 AppConfig
,当然,我们这里并不进行一些复杂的配置。
package com.sinosoft.gov.spring;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import java.beans.Introspector;
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.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author WQ
* @Classname WangqingApplycation
* @Description TODO
* @Date 2022/11/16 5:07 下午
*/
public class WangqingApplycationContxt {
private Class configClass;
// BeanNameAware
/***
* 用来存放bean 对象 key value
*/
private Map beanDefinitionMap = new HashMap<>();
/****
* 用来存单例对象的
*
*/
private Map singletonObjects = new HashMap<>();
/***
* 用来存放实现了 BeanPostProcessor 的类
*/
private List beanPostProcessors = new ArrayList<>();
/***
* 创建一个Spring 容器
* @param configClass
*/
public WangqingApplycationContxt(Class configClass) {
this.configClass = configClass;
// 扫描
scan(configClass);
// 创建
for (Map.Entry entry : beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
BeanDefinition beanDefinition = entry.getValue();
// 如果是单例对象,就直接从单例池里拿
if (beanDefinition.getScope().equals("singleton")) {
Object bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, beanDefinition);
}
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();
Object instance = null;
try {
instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
field.setAccessible(true);
field.set(instance, getBean(field.getName()));
}
}
if (instance instanceof InitializingBean) {
((InitializingBean) instance).afterPropertiesSet();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}
private void scan(Class configClass) {
// 判断传入的配置类上是否添加了ComponentScan注解
if (configClass.isAnnotationPresent(ComponentScan.class)) {
// 获取注解的信息
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
// 获取扫描路径(就是注解上是否添加的包路径) 如com.xx.service
String path = componentScanAnnotation.value();
// 处理路径字符串,获取class文件的绝对路径 如 com/xx/service
path = path.replace(".", "/");
// 获取容器的类加载器,获取class文件的绝对路径 如 com/xx/service
ClassLoader classLoader = WangqingApplycationContxt.class.getClassLoader();
URL resource = classLoader.getResource(path);
// 将class文件封装为一个文件或者是文件夹
File file = new File(resource.getFile());
if (file.isDirectory()) {
// 拿到当前文件夹中的所有文件
for (File f : file.listFiles()) {
// 拿到文件的绝对路径
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("\\", ".");
try {
Class> aClass = classLoader.loadClass(absolutePath);
// 判断当前类是否实现 BeanPostProcessor
// 如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。
if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
BeanPostProcessor instance = (BeanPostProcessor) aClass.getConstructor().newInstance();
beanPostProcessors.add(instance);
// 遍历实现了BeanPostProcessor的类,执行初始化方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
}
}
//
if (aClass.isAnnotationPresent(Component.class)) {
// bean
// 判断传入的配置类上是否添加了Component注解
Component componentAnnotation = aClass.getAnnotation(Component.class);
// 获取注解的value值 service 的名称
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {
// 如果没设置,默认按照类名来
beanName = Introspector.decapitalize(aClass.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(aClass);
//判断传入的实例,是否用了Scope注解
if (aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
} else {
// 没写 默认是单例
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String beanName) {
if (!beanDefinitionMap.containsKey(beanName)) {
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
// 如果是单例,直接从单例池里拿
if (beanDefinition.getScope().equals("singleton")) {
Object singletonBean = singletonObjects.get(beanName);
if (singletonBean == null) {
// 没有 直接创建bean
Object bean = createBean(beanName, beanDefinition);
singletonObjects.put(beanName, bean);
}
return singletonBean;
} else {
// 圆形
Object prototypeBean = createBean(beanName, beanDefinition);
return prototypeBean;
}
}
}
BeanPostProcessor 介绍及使用
该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
1、InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。
2、spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。
3、在Spring初始化bean的时候,如果该bean实现了InitializingBean接口,并且同时在配置文件中指定了init-method,系统则是先调用afterPropertieSet()方法,然后再调用init-method中指定的方法。
spring初始化bean有两种方式:
第一:实现InitializingBean接口,继而实现afterPropertiesSet的方法
第二:反射原理,配置文件使用init-method标签直接注入bean。
相同点: 实现注入bean的初始化。
不同点:
(1)实现的方式不一致。
(2)接口比配置效率高,但是配置消除了对spring的依赖。而实现InitializingBean接口依然采用对spring的依赖。
@Component
public class MyInitializingBean implements InitializingBean {
public MyInitializingBean() {
System.out.println("我是MyInitializingBean构造方法执行...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("我是afterPropertiesSet方法执行...");
}
@PostConstruct
public void postConstruct() {
System.out.println("我是postConstruct方法执行...");
}
public void init(){
System.out.println("我是init方法执行...");
}
@Bean(initMethod = "init")
public MyInitializingBean test() {
return new MyInitializingBean();
}
}
通过启动日志我们可以看出执行顺序优先级:构造方法 > postConstruct >afterPropertiesSet > init方法。