系列文章:
在上一篇我们已经实现了 beans 包中的相关类,本篇就来实现 context 中关于容器的具体逻辑。
1.MYAbstractApplicationContext
IOC容器顶层设计,是最顶层容器的规范,不管是 XmlApplication 还是 AnnotationApplication 都必须去实现。这种设计也便于我们日后扩展新容器。
public abstract class MYAbstractApplicationContext {
// 受保护,只提供给子类重写(最少知道原则)
protected void refresh() throws Exception {}
}
2.MYDefaultListableBeanFactory
上面有了 MYAbstractApplicationContext 那我们就可以多种选择,但是也必须提供 IOC 容器的默认实现。这就好比平常我们手机支付时可以用微信、支付宝、银行卡等,但是在具体的支付页面它都会设置好一个默认支付选项。
注意,这个类其实是在 beans 包中,因为 beans包更多放的是规范、配置、标准等。放到 context 包中说是为了更直观的看到 AbstractApplicationContext、DefaultListableBeanFactory、ApplicationContext 的关系。
public class MYDefaultListableBeanFactory extends MYAbstractApplicationContext {
// 伪IOC容器,保存了BeanDefinition(类信息)
// 这里的key是factoryBeanName,即beanName(对于一种Bean而言factoryName是唯一的)
protected final Map<String, MYBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, MYBeanDefinition>();
}
3.MYApplicationContext
// 实现 BeanFactory 接口,继承 DefaultListableBeanFactory
public class MYApplicationContext extends MYDefaultListableBeanFactory implements MYBeanFactory {
private String[] configLocations;
private MYBeanDefinitionReader reader;
// 通用IOC容器,存的是 BeanWrapper
private Map<String, MYBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, MYBeanWrapper>();
// 单例的IOC容器,存的是实例对象(相当于缓存,避免重复创建Bean)
private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();
// 构造函数,要传入加载的配置文件,然后调用refresh方法
public MYApplicationContext(String... configLocations) {
this.configLocations = configLocations;
try {
refresh();
} catch (Exception e) {
e.printStackTrace();
}
}
//...
}
我们再来看一遍继承关系:
refresh()
在 AbstractApplicationContext 定义的模板方法,是IOC容器初始化的入口
@Override
protected void refresh() throws Exception {
// 1.定位,定位配置文件
reader = new MYBeanDefinitionReader(configLocations);
// 2.加载,加载配置文件到内存(BeanDefinition)
List<MYBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
// 3.注册,注册配置信息到容器里面(伪IOC容器)
doRegisterBeanDefinition(beanDefinitions);
// 4.把不是延时加载的类,提前初始化
// 一般不开启懒加载,即在IOC容器初始化时就完成实例化与注入
doAutowired();
}
doRegisterBeanDefinition()
将BeanDefinition们注入容器(伪IOC容器)。该方法执行完后,IOC容器初始化就完成了。
private void doRegisterBeanDefinition(List<MYBeanDefinition> beanDefinitions) throws Exception {
for (MYBeanDefinition beanDefinition : beanDefinitions) {
if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
}
super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
}
}
doAutowired()
对于非延时加载的bean,在IOC容器初始化时就将Bean创建出来并完成依赖注入
private void doAutowired() {
// 遍历BeanDefinition,看哪个是延时加载了
for (Map.Entry<String, MYBeanDefinition> entry : super.beanDefinitionMap.entrySet()) {
String beanName = entry.getKey();
// 这里默认isLazyInit=false,即先提前将Bean放入IOC容器中,即这里所有BeanDefinition都会创建一个bean
if (!entry.getValue().isLazyInit()) {
try {
getBean(beanName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
getBean() *
初始化Bean并完成依赖注入:
- 初始化:读取BeanDefinition,通过反射创建Bean实例,包装成BeanWrapper,并放入IOC容器
- 注入:对IOC容器管理的Bean进行依赖注入
Spring中调用getBean的时机:
- DispatchServlet 创建 IOC容器:refresh --> doAutowired
- DispatchServlet 创建 HandlerMapping,要拿出所有Bean
- 手动调用 getBean 方法去获取Bean,比如上面doAutowired()在IOC容器初始化时调用
@Override
public Object getBean(String beanName) throws Exception {
// 根据 beanName 拿出 BeanDefinition
MYBeanDefinition myBeanDefinition = this.beanDefinitionMap.get(beanName);
Object instance = null;
// 创建事件处理器
MYBeanPostProcessor beanPostProcessor = new MYBeanPostProcessor();
// 在创建bean之前进行一些动作
beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
// 判断是否是Spring管理的对象
// 在创建BeanDefinition时,不是Spring管理的就没有BeanDefinition(这里是通过Properties配置scanPackage不是注解)
if (myBeanDefinition == null) {
throw new Exception("This Bean not exists!");
}
// 1.初始化
// 注:所有Bean实例都要封装成BeanWrapper,然后在BeanWrapper中再取出Bean实例
MYBeanWrapper beanWrapper = instantiteBean(beanName, myBeanDefinition);
// 2.将拿到的BeanWrapper放入IOC容器
this.factoryBeanInstanceCache.put(beanName, beanWrapper);
// 在创建bean之后进行一些动作
beanPostProcessor.postProcessAfterInitialization(instance, beanName);
// 3.注入
populateBean(beanName, new MYBeanDefinition(), beanWrapper);
Object o = this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
// 注:即使是单例模式有单例IOC容器,但获取Instance也要先封装为Wrapper,然后再在通用容器中取
return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
}
// 通过类型获取bean的方法跟beanName获取大同小异,这里就不实现了
@Override
public Object getBean(Class<?> beanClass) throws Exception {
return null;
}
循环注入:class A{ B b; } --> class B{ A a; }。因此要分成初始化与注入两个方法,即先将对象创建出来,然后再将依赖注入
instantiteBean()
创建 Bean 实例,并封装成 BeanWrapper
private MYBeanWrapper instantiteBean(String beanName, MYBeanDefinition myBeanDefinition) {
// 1.拿到要实例化的类名
String className = myBeanDefinition.getBeanClassName();
// 2.通过反射进行实例化
Object instance = null;
try {
// 默认所有对象都是单例的,即都可以通过单例IOC容器中获取Bean
if (this.factoryBeanObjectCache.containsKey(className)) {
instance = this.factoryBeanObjectCache.get(className);
} else {
Class<?> clazz = Class.forName(className);
instance = clazz.newInstance();
this.factoryBeanObjectCache.put(myBeanDefinition.getFactoryBeanName(), instance);
// 注:这里还要根据className(全类名)放一个
// 因为在通过类型进行getBean时,BeanDefinition只封装了接口做factoryName
this.factoryBeanObjectCache.put(className, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
// 3.封装BeanWrapper
// 注:无论单例多例,都要先封装成 BeanWrapper
MYBeanWrapper beanWrapper = new MYBeanWrapper(instance);
return beanWrapper;
}
populateBean()
对IOC中的Bean进行依赖注入:
- byName:指定 beanName(首字母小写,接口,自定义) -----> @Resource时
- byType:类型注入(默认) ----> @Autowired
private void populateBean(String beanName, MYBeanDefinition myBeanDefinition, MYBeanWrapper myBeanWrapper) {
Class<?> clazz = myBeanWrapper.getWrappedClass();
// 只有容器管理的bean才会给他依赖注入
if (! clazz.isAnnotationPresent(MYController.class ) || clazz.isAnnotationPresent(MYService.class)) { return; }
Object instance = myBeanWrapper.getWrappedInstance();
// 注:这里是getDeclaredFields,getFields只能获取到public字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(MYAutowired.class)) { continue; }
// 1.获取注解中指定注入的beanName(byName注入)
MYAutowired annotation = field.getAnnotation(MYAutowired.class);
String autowiredBeanName = annotation.value().trim();
// 2.没有指定beanName的话,通过类型进行注入(byType注入)
// 注意:类型 = 自身类型 || 接口类型。在BeanDefinitionReader#loadBeanDefinitions已经对接口创建过BeanDefinition了(当一个接口有多个实现类时,后扫描的会覆盖先扫描的)
if ("".equals(autowiredBeanName)) {
// 除了simpleName,通过class拿到的都是全类名
// 前面初始化时已经用className向容器中注入过wrapper了
autowiredBeanName = field.getType().getName();
}
field.setAccessible(true);
try {
// 因为要给当前Bean注入时,可能要注入的Bean还没初始化,因此就暂时不给这个字段注入
// 但是当正式使用时还会getBean一次,这时所有bean都初始化完成了,就可以注入了
if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){ continue; }
// 获取具体Bean实例:这里是在通用IOC容器中获取,因为可能有多例情况
field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
getBeanDefinitionNames()
返回所有的 beanName
public String[] getBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
getConfig()
获取配置文件的内容,实际上调用到 MYBeanDefinition#getConfig 方法
public Properties getConfig() {
return this.reader.getConfig();
}
4.MYApplicationContextAware
通过解耦方式获得IOC容器的顶层设计。后面将通过一个监听器去扫描所有的类,只要实现了此接口,将自动调用setApplicationContext()方法,从而将IOC容器注入到目标类中。
public interface MYApplicationContextAware {
void setApplicationContext(MYApplicationContext applicationContext);
}
结果演示
IOC和DI模块到此就写完了,下面我们写个测试类来看看能否运行。我们首先在 MyAction 中增加一个 test 方法
public void test(String name) {
System.out.println(queryService.query(name));
}
然后再 test 包下新建一个 Test 类
public class Test {
public static void main(String[] args) {
// 初始化 IOC 容器
MYApplicationContext context = new MYApplicationContext("classpath:application.properties");
try {
// 测试 IOC
Object bean = context.getBean("myAction");
System.out.println(bean);
// 测试 DI,即 myAction 对象中是否成功注入 QueryService 对象
MyAction myAction = (MyAction)bean;
myAction.test("張三");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果如下:
可以看到 IOC 容器中已经有了 myAction 对象,并且也成功注入 QueryService!
完整代码我放到 GitHub 上了,可以点击这里跳转…