作者:小傅哥
沉淀、分享、成长,让自己和他人都能有所收获!😄
有什么方式,能给代码留条活路?
有人说:人人都是产品经理,那你知道吗,人人也都可以是码农程序员!就像:
其实学会写代码并不难,但学会写好代码却很难。从易阅读上来说你的代码要有准确的命名和清晰的注释、从易使用上来说你的代码要具备设计模式的包装让对外的服务调用更简单、从易扩展上来说你的代码要做好业务和功能的实现分层。在易阅读、易使用、易扩展以及更多编码规范的约束下,还需要在开发完成上线后的交付结果上满足;高可用、高性能、高并发,与此同时你还会接到现有项目中层出不穷来自产品经理新增的需求。
🎙怎么办?知道你在码砖,不知道你在盖哪个猪圈!
就算码的砖是盖的猪圈,也得因为猪多扩面积、改水槽、加饲料呀,所以根本没法保证你写完的代码就不会加需求。那么新加的需求如果是以破坏了你原有的封装了非常完美500行的 ifelse
咋办,拆了重盖吗?
兄嘚,给代码留条活路吧!你的代码用上了定义接口吗、接口继承接口吗、接口由抽象类实现吗、类继承的类实现了接口方法吗,而这些操作都是为了让你的程序逻辑做到分层、分区、分块,把核心逻辑层和业务封装层做好隔离,当有业务变化时候,只需要做在业务层完成装配,而底层的核心逻辑服务并不需要频繁变化,它们所增加的接口也更原子化,不具备业务语意。所以这样的实现方式才能给你的代码留条活路。如果还不是太理解,可以多看看《重学Java设计模式》和现在编写的《手撸Spring》,这里面都有大量的设计模式应用实践
当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力。就像我们在上一章节已经给类对象添加了修改注册Bean定义未实例化前的属性信息修改和实例化过程中的前置和后置处理,这些额外能力的实现,都可以让我们对现有工程中的类对象做相应的扩展处理。
那么除此之外我们还希望可以在 Bean 初始化过程,执行一些操作。比如帮我们做一些数据的加载执行,链接注册中心暴漏RPC接口以及在Web程序关闭时执行链接断开,内存销毁等操作。如果说没有Spring我们也可以通过构造函数、静态方法以及手动调用的方式实现,但这样的处理方式终究不如把诸如此类的操作都交给 Spring 容器来管理更加合适。 因此你会看到到 spring.xml 中有如下操作:
可能面对像 Spring 这样庞大的框架,对外暴露的接口定义使用或者xml配置,完成的一系列扩展性操作,都让 Spring 框架看上去很神秘。其实对于这样在 Bean 容器初始化过程中额外添加的处理操作,无非就是预先执行了一个定义好的接口方法或者是反射调用类中xml中配置的方法,最终你只要按照接口定义实现,就会有 Spring 容器在处理的过程中进行调用而已。整体设计结构如下图:
init-method、destroy-method
两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化操作的工程中,就可以通过反射的方式来调用配置在 Bean 定义属性当中的方法信息了。另外如果是接口实现的方式,那么直接可以通过 Bean 对象调用对应接口定义的方法即可,((InitializingBean) bean).afterPropertiesSet()
,两种方式达到的效果是一样的。destroy-method
和 DisposableBean
接口的定义,都会在 Bean 对象初始化完成阶段,执行注册销毁方法的信息到 DefaultSingletonBeanRegistry 类中的 disposableBeans 属性里,这是为了后续统一进行操作。这里还有一段适配器的使用,因为反射调用和接口直接调用,是两种方式。所以需要使用适配器进行包装,下文代码讲解中参考 DisposableBeanAdapter 的具体实现
-关于销毁方法需要在虚拟机执行关闭之前进行操作,所以这里需要用到一个注册钩子的操作,如:Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("close!")));
这段代码你可以执行测试,另外你可以使用手动调用 ApplicationContext.close 方法关闭容器。small-spring-step-07
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── factory
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── UserDao.java
│ └── UserService.java
└── ApiTest.java
工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码
Spring 应用上下文和对Bean对象扩展机制的类关系,如图 8-4
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("close!")));
cn.bugstack.springframework.beans.factory.InitializingBean
public interface InitializingBean {
/**
* Bean 处理了属性填充后调用
*
* @throws Exception
*/
void afterPropertiesSet() throws Exception;
}
cn.bugstack.springframework.beans.factory.DisposableBean
public interface DisposableBean {
void destroy() throws Exception;
}
cn.bugstack.springframework.beans.factory.config.BeanDefinition
public class BeanDefinition {
private Class beanClass;
private PropertyValues propertyValues;
private String initMethodName;
private String destroyMethodName;
// ...get/set
}
init-method="initDataMethod" destroy-method="destroyDataMethod"
操作,最终实现接口的效果是一样的。只不过一个是接口方法的直接调用,另外是一个在配置文件中读取到方法反射调用cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
// ...
addSingleton(beanName, bean);
return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 1. 执行 BeanPostProcessor Before 处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 执行 Bean 对象的初始化方法
try {
invokeInitMethods(beanName, wrappedBean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
}
// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
return wrappedBean;
}
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
// 1. 实现接口 InitializingBean
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 配置信息 init-method {判断是为了避免二次执行销毁}
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
if (null == initMethod) {
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
}
cn.bugstack.springframework.beans.factory.support.DisposableBeanAdapter
public class DisposableBeanAdapter implements DisposableBean {
private final Object bean;
private final String beanName;
private String destroyMethodName;
public DisposableBeanAdapter(Object bean, String beanName, BeanDefinition beanDefinition) {
this.bean = bean;
this.beanName = beanName;
this.destroyMethodName = beanDefinition.getDestroyMethodName();
}
@Override
public void destroy() throws Exception {
// 1. 实现接口 DisposableBean
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
// 2. 配置信息 destroy-method {判断是为了避免二次执行销毁}
if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
if (null == destroyMethod) {
throw new BeansException("Couldn't find a destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
destroyMethod.invoke(bean);
}
}
}
实现接口 DisposableBean
、配置信息 destroy-method
,两种方式。而这两种方式的销毁动作是由 AbstractApplicationContext 在注册虚拟机钩子后看,虚拟机关闭前执行的操作动作。cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
// 注册实现了 DisposableBean 接口的 Bean 对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
addSingleton(beanName, bean);
return bean;
}
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
}
Map<String, DisposableBean> disposableBeans
属性中去,因为这个接口的方法最终可以被类 AbstractApplicationContext 的 close 方法通过 getBeanFactory().destroySingletons()
调用。cn.bugstack.springframework.context.ConfigurableApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext {
void refresh() throws BeansException;
void registerShutdownHook();
void close();
}
registerShutdownHook
和手动执行关闭的方法 close
。cn.bugstack.springframework.context.support.AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
// ...
@Override
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
@Override
public void close() {
getBeanFactory().destroySingletons();
}
}
Runtime.getRuntime().addShutdownHook
,可以尝试验证。在一些中间件和监控系统的设计中也可以用得到,比如监测服务器宕机,执行备机启动操作。cn.bugstack.springframework.test.bean.UserDao
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
public void initDataMethod(){
System.out.println("执行:init-method");
hashMap.put("10001", "小傅哥");
hashMap.put("10002", "八杯水");
hashMap.put("10003", "阿毛");
}
public void destroyDataMethod(){
System.out.println("执行:destroy-method");
hashMap.clear();
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
cn.bugstack.springframework.test.bean.UserService
public class UserService implements InitializingBean, DisposableBean {
private String uId;
private String company;
private String location;
private UserDao userDao;
@Override
public void destroy() throws Exception {
System.out.println("执行:UserService.destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行:UserService.afterPropertiesSet");
}
// ...get/set
}
基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
init-method="initDataMethod" destroy-method="destroyDataMethod"
,这样两个配置。从源码的学习中可以知道,这两个配置是为了加入到 BeanDefinition 定义类之后写入到类 DefaultListableBeanFactory 中的 beanDefinitionMap 属性中去。@Test
public void test_xml() {
// 1.初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
applicationContext.registerShutdownHook();
// 2. 获取Bean对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
}
applicationContext.registerShutdownHook();
测试结果
执行:init-method
执行:UserService.afterPropertiesSet
测试结果:小傅哥,腾讯,深圳
执行:UserService.destroy
执行:destroy-method
Process finished with exit code 0
implements InitializingBean, DisposableBean
和在spring.xml中配置 init-method="initDataMethod" destroy-method="destroyDataMethod"
的两种具体在 AbstractAutowireCapableBeanFactory
完成初始方法和 AbstractApplicationContext
处理销毁动作的具体实现过程。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。