感知,是一个很玄乎的词。其实我们可以这样理解。正常情况下,我们的bean,是不应该意识到Spring框架的存在的,它虽然被Spring所管理,但是它本身意识不到管理者的存在。从初始化,到销毁,在它自己看来,其实都是一个正常的java对象应该有的流程。只不过调用者是Spring而已。那么总有些时候,我们需要让bean【感知到】Spring框架的存在,体现在代码中,就是获取到属于Spring框架的一部分,比如bean工厂:是谁生产出自己的。ApplicationContext:自己属于哪个上下文。或者更加单纯的beanName:自己在Spring中叫什么名字。一旦我们的需求需要这些信息的时候,我们的bean就需要通过一些手段,来获取到它们。这就是Aware接口的作用。
但其实,Aware接口本身设计出来是用于Spring框架本身的,其实它不太提倡由用户去实现这些接口,因为这样的程序会难以避免的和Spring框架耦合在一起,我们知道高耦合的程序是不好的。但我们有些时候难免会遇到一些情况,需要我们这样处理。又或者像现在这样,当我们去研究Spring本身的时候,也绕不开这个点。
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─akitsuki
│ │ │ └─springframework
│ │ │ ├─beans
│ │ │ │ ├─exception
│ │ │ │ │ BeanException.java
│ │ │ │ │
│ │ │ │ └─factory
│ │ │ │ │ Aware.java
│ │ │ │ │ BeanClassLoaderAware.java
│ │ │ │ │ BeanFactory.java
│ │ │ │ │ BeanFactoryAware.java
│ │ │ │ │ BeanNameAware.java
│ │ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ │ DisposableBean.java
│ │ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ │ InitializingBean.java
│ │ │ │ │ ListableBeanFactory.java
│ │ │ │ │
│ │ │ │ ├─config
│ │ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ │ BeanDefinition.java
│ │ │ │ │ BeanFactoryPostProcessor.java
│ │ │ │ │ BeanPostProcessor.java
│ │ │ │ │ BeanReference.java
│ │ │ │ │ ConfigurableBeanFactory.java
│ │ │ │ │ DefaultSingletonBeanRegistry.java
│ │ │ │ │ PropertyValue.java
│ │ │ │ │ PropertyValues.java
│ │ │ │ │ SingletonBeanRegistry.java
│ │ │ │ │
│ │ │ │ ├─support
│ │ │ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ │ │ AbstractBeanDefinitionReader.java
│ │ │ │ │ AbstractBeanFactory.java
│ │ │ │ │ BeanDefinitionReader.java
│ │ │ │ │ BeanDefinitionRegistry.java
│ │ │ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ │ │ DefaultListableBeanFactory.java
│ │ │ │ │ DisposableBeanAdapter.java
│ │ │ │ │ InstantiationStrategy.java
│ │ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │ │
│ │ │ │ └─xml
│ │ │ │ XmlBeanDefinitionReader.java
│ │ │ │
│ │ │ ├─context
│ │ │ │ │ ApplicationContext.java
│ │ │ │ │ ApplicationContextAware.java
│ │ │ │ │ ConfigurableApplicationContext.java
│ │ │ │ │
│ │ │ │ └─support
│ │ │ │ AbstractApplicationContext.java
│ │ │ │ AbstractRefreshableApplicationContext.java
│ │ │ │ AbstractXmlApplicationContext.java
│ │ │ │ ApplicationContextAwareProcessor.java
│ │ │ │ ClasspathXmlApplicationContext.java
│ │ │ │
│ │ │ ├─core
│ │ │ │ └─io
│ │ │ │ ClasspathResource.java
│ │ │ │ DefaultResourceLoader.java
│ │ │ │ FileSystemResource.java
│ │ │ │ Resource.java
│ │ │ │ ResourceLoader.java
│ │ │ │ UrlResource.java
│ │ │ │
│ │ │ └─util
│ │ │ ClassUtils.java
│ │ │
│ │ └─resources
│ └─test
│ ├─java
│ │ └─com
│ │ └─akitsuki
│ │ └─springframework
│ │ └─test
│ │ │ ApiTest.java
│ │ │
│ │ └─bean
│ │ UserDao.java
│ │ UserService.java
│ │
│ └─resources
│ spring.xml
虽然看起来很多,但实际上这一章的内容相当轻松。大部分内容都是前面积累下来的,新增的内容并没有多少。抱着轻松的心情来开始这一章吧。
既然我们要让bean感知到Spring的存在,那么我们需要一个标记,来告诉Spring:这个bean,需要感知到你。那么对于Spring来说,这个标记,就是Aware接口。
package com.akitsuki.springframework.beans.factory;/*** 标记接口,本身没有内容,仅用于提供标记功能* 实现这个接口及其子接口,可以被Spring容器【感知】** @author ziling.wang@hand-china.com* @date 2022/11/24 14:11*/
public interface Aware {
}
空空如也的接口,没有继承,也没有自己的方法。但有了它,Spring就可以通过 instanceof
操作,来知道,这个bean是否需要进行感知,从而进行统一的处理。这就像是被盖上了一个印章一样。
我们虽然说,感知Spring。但我们不可能把庞大的Spring,一股脑的全部给Bean。来,你要的Spring给你了,拿去玩吧。我们肯定是要按需分配。你告诉我,你想要哪块儿,我再把你需要的部分给你。于是,就有了Aware的各种子接口。在这里,我们介绍实现其中的四种:BeanFactory感知接口、BeanClassLoader感知接口、BeanName感知接口、ApplicationContext感知接口。
package com.akitsuki.springframework.beans.factory;import com.akitsuki.springframework.beans.exception.BeanException;/*** 实现此接口,即可感知到所属的BeanFactory** @author ziling.wang@hand-china.com* @date 2022/11/24 14:12*/
public interface BeanFactoryAware extends Aware {/*** 设置所属的BeanFactory** @param beanFactory beanFactory* @throws BeanException 防止出现初始化错误*/void setBeanFactory(BeanFactory beanFactory) throws BeanException;
}
首先是我们的BeanFactory感知接口,提供了一个setBeanFactory方法,很好理解,bean所需要的BeanFactory,就会通过这个方法来传递给它。
package com.akitsuki.springframework.beans.factory;/*** 实现此接口,即可感知到所使用的BeanClassLoader** @author ziling.wang@hand-china.com* @date 2022/11/24 14:14*/
public interface BeanClassLoaderAware extends Aware {/*** 设置所使用的classLoader** @param classLoader classLoader*/void setBeanClassLoader(ClassLoader classLoader);
}
与上面类似,这里传递的是ClassLoader。
package com.akitsuki.springframework.beans.factory;/*** 实现此接口,即可感知到当前bean的name** @author ziling.wang@hand-china.com* @date 2022/11/24 14:17*/
public interface BeanNameAware extends Aware {/*** 设置beanName** @param beanName beanName*/void setBeanName(String beanName);
}
嗯,beanName,我们的老朋友
package com.akitsuki.springframework.context;import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.Aware;/*** 实现此接口,即可感知到所属的ApplicationContext** @author ziling.wang@hand-china.com* @date 2022/11/24 14:19*/
public interface ApplicationContextAware extends Aware {/*** 设置ApplicationContext** @param applicationContext applicationContext* @throws BeanException applicationContext中的方法可能会抛出此异常*/void setApplicationContext(ApplicationContext applicationContext) throws BeanException;
}
最后出场的是我们的重量级选手,ApplicationContext。
可以看到,这些接口的方法都差不多,都是将bean需要的内容,传递给它。那么我们来思考一下,这些东西,要在什么时候传递给bean呢?很自然的想法就是,在创建它的时候。但是这个时候我们就犯了难:在创建bean的时候,我们是拿不到ApplicationContext的>_<!这个时候,beanFactory、classLoader、beanName,我们都可以获取到,唯独ApplicationContext拿不到。这要怎么办?往前想想,前面我们实现的一个功能,可以帮助我们实现这一个需求。嗯…想到了吗?现在来揭晓答案:bean后置处理器!我们可以在ApplicationContext的刷新方法中,将自身作为参数传入处理器中,再将处理器注册到beanFactory中。最后,在beanFactory创建bean的时候,就可以在执行bean处理器的时候操作ApplicationContext了,真的是很好玩的一个设计。
来,我们看看这个后置处理器要怎么设计
package com.akitsuki.springframework.context.support;import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.ApplicationContextAware;
import lombok.AllArgsConstructor;/*** ApplicationContext感知包装处理器,由于ApplicationContext的创建在Bean创建之后* 所以不能在创建bean的时候就拿到,就需要后置处理器,在bean初始化之前来将ApplicationContext进行注入** @author ziling.wang@hand-china.com* @date 2022/11/24 14:28*/
@AllArgsConstructor
public class ApplicationContextAwareProcessor implements BeanPostProcessor {private ApplicationContext applicationContext;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(applicationContext);}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {return bean;}
}
嗯嗯,非常简单易懂的设计,判断bean实现了 ApplicationContextAware
接口,就对其进行强转,再调用 setApplicationContext
方法,将applicationContext设置进去即可。顺带一提,这里用lombok的 @AllArgsConstructor
注解,省略了有参构造方法的编写,实际上这里应该是要传入进来 ApplicationContext
来对属性进行初始化的,需要注意。
接下来是 refresh
方法的改造,方法位于 AbstractApplicationContext
中。
@Overridepublic void refresh() throws BeanException {//创建beanFactory,加载beanDefinitionrefreshBeanFactory();//获取beanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();//添加ApplicationContextAwareProcessorbeanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));//在bean实例化之前,执行BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);//注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);//提前实例化单例bean对象beanFactory.preInstantiateSingletons();}
可以看到,方法中我们增加了一步:向beanFactory中添加了后置处理器。这样就可以保证在bean初始化的时候,执行到这个处理器了。由此也可以看出,Spring设计的这些功能,并不仅仅是用来给用户使用的,Spring内部本身也大量的用到了这些设计。
那么接口我们也定义好了,难题也解决了。接下来就该正式的去实现这些方法了。我们刚才也说到,应该在创建bean的时候,将这些感知对象传递进去,那么我们就来改造一下创建bean的过程。让我看看!AbstractAutowireCapableBeanFactory
。
/*** 初始化bean** @param beanName bean名称* @param bean 待初始化bean* @param beanDefinition bean定义* @return bean*/private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {//执行Aware感知操作invokeAwareMethods(beanName, bean);//执行beanPostProcessor before处理Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);//执行bean初始化内容try {invokeInitMethod(beanName, wrappedBean, beanDefinition);} catch (Exception e) {throw new BeanException("执行bean初始化方法失败,bean名称:" + beanName);}//执行beanPostProcessor after处理wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);return wrappedBean;}/*** 执行bean注册的感知接口相应操作** @param beanName* @param bean*/private void invokeAwareMethods(String beanName, Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(this);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(getClassLoader());}if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}}}
可以看到,我们把操作放在了初始化中,在执行初始化前置处理之前,我们先执行了Aware感知的相关操作。操作的内容和前面对ApplicationContext的类似,我们就不多说了。invokeAwareMethods
方法执行完毕后,紧接着就会到bean初始化前后置处理器的执行。在那里又会对ApplicationContext进行处理。
这里有一点点地方需要注意的是,可以看到对BeanClassLoader的处理,这里调用了getClassLoader方法。但实际上前面我们并没有这个方法。这个方法是这次新加的,加在了 AbstractBeanFactory
中,内容也很简单。
private ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); public ClassLoader getClassLoader() {return this.classLoader;
}
好了,这次我们的测试环节来的格外的快,因为这次内容还是相对来说比较简单的(虽然不像第一章那样简单)。来看看我们bean的变化吧
package com.akitsuki.springframework.test.bean;import com.akitsuki.springframework.beans.factory.*;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.ApplicationContextAware;
import lombok.Getter;
import lombok.Setter;/*** @author ziling.wang@hand-china.com* @date 2022/11/8 14:42*/
@Getter
@Setter
public class UserService implements InitializingBean, DisposableBean,BeanFactoryAware, BeanClassLoaderAware, BeanNameAware, ApplicationContextAware {private BeanFactory beanFactory;private ClassLoader beanClassLoader;private String beanName;private ApplicationContext applicationContext;private String dummyString;private int dummyInt;private UserDao userDao;public void queryUserInfo(Long id) {System.out.println("dummyString:" + dummyString);System.out.println("dummyInt:" + dummyInt);String userName = userDao.queryUserName(id);if (null == userName) {System.out.println("用户未找到>_<");} else {System.out.println("用户名:" + userDao.queryUserName(id));}}@Overridepublic void destroy() throws Exception {System.out.println("userService的destroy执行了");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("userService的afterPropertiesSet执行了");}
}
嗯,真的是我全都要。实现了我们上面完成的那四个接口,这也导致了我们的UserService实现的接口数量达到了可怕的6个,承受着这个年纪不该承受的重量。至于这里看起来没有实现接口方法的原因,是因为这些接口都是set方法,而这里通过lombok的@Setter注解,刚好为这些属性提供了setter方法,所以就没有显式的去写了。
接下来是我们的正式测试类
package com.akitsuki.springframework.test;import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;/*** @author ziling.wang@hand-china.com* @date 2022/11/15 13:58*/
public class ApiTest {@Testpublic void test() {//初始化BeanFactoryClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");context.registerShutdownHook();//获取bean,测试UserService userService = context.getBean("userService", UserService.class);userService.queryUserInfo(1L);userService.queryUserInfo(4L);userService.queryUserInfo(114L);System.out.println("beanFactory:" + userService.getBeanFactory());System.out.println("beanClassLoader:" + userService.getBeanClassLoader());System.out.println("beanName:" + userService.getBeanName());System.out.println("applicationContext:" + userService.getApplicationContext());}
}
测试结果
执行UserDao的initMethod
userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<
beanFactory:com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory@3551a94
beanClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
beanName:userService
applicationContext:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@531be3c5
执行UserDao的destroyMethod
userService的destroy执行了Process finished with exit code 0
可以看到,我们新加的那些Spring的部分,都已经被注入进来了。这也就意味着,我们的bean成功的感知到了Spring的部分。终于,这次练习也圆满结束了,值得鼓励。
相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring
,这里对应的代码是mini-spring-08