前置说明

我在写一些我认为很繁琐的东西或者需要扩展的东西,我总是想能不能用一种很巧妙的东西,去代替他去优雅的解决它(我总是在说优雅,这个是我的毛病见谅)。

最近,新开个项目,需要写定时器,问题是,我们是两台服务器,所以需要redis实现分布式锁,来确认这个定时任务只能被一个服务器所运行。其实是这个很好实现,以前是怎么去实现的,一个aop就行了。但是,又想,我想加个ip控制,这个也简单啊,继续aop就行了啊。正常来说,aop就行。但是并不优雅、并且太过于无侵入,aop我们都知道,无侵入是改变代码喽(这是bean实例化后进行的增强操作,这里先不说了),aop个人感觉这个东西用不好是大问题,于是想到,把我需要的定时器特性抽象出来,由定时任务来继承他,并且写入业务逻辑,就能很好的辨认出我这个定时继承了哪些特性,当然不仅仅如此,还可以写入一些插件功能,例如:授权ip访问。我们还可以很好的实现【运行成功回调】【异常回调】来确定这个定时器运行的情况,等等。

为什么扯这么多东西呢,和我们的主题有什么关系呢?原因是:

我的确【很勉强】实现上面的功能,能用是能用,但是我并不满意,太low了,写的头都大了,于是,我意思的一个很严重东西,我没有经验、能力去写好这个东西,我的想法很多,能写出来太少了QAQ。于是产生了,学习前人的想法,看他们怎么做的,然后,开始了学习了spring源码!!!

前端时间学了一点大数据,明年再说吧。。。哎,先打打设计基础。

杂谈

说框架

一个框架最重要的是扩展性,扩展性,扩展性,而扩展性设计思想是:

抽象:抽

设计模式:设计模式只是一种思想,是前人总结出来的,能够更好的对我们所遇到的问题,做出良好的扩展性,以及避免一些常见的错误的思想。

IOC

概念简单介绍

IOC老生常态的东西:【控制翻转】。什么是控制翻转呢,就是把Bean交给spring来控制,spring来对bean进行赋值增强。然后,bean默认单例,可以设置多例、requesu、session。。。等等

老的spring需要使用xml配置来设置一些对象,例如:

1
<bean id=? class=? />

简单的举个例子。

新的spring boot则可以使用注解来进行bean的注入、实例化、增强。其实,还是spring ioc老一套的东西,只不过spring boot进行了扩展,说的简单点就是,读取配置文件由xml简化成一个注解,当然也可以有xml,所以,我们能分析出,有一个抽象读取配置文件的抽象类,实现它就行,不管什么xml、json、注解都可以写,只有有对应的解析类。具体看下面的流程图

IOC流程图

这里截取学习视频流程图。简单的说下流程图的流程,其实中间有什么细节没有说。

流程理解说明

读取Bean配置

怎么知道实例哪些Bean、实例Bean要进行哪些操作?当然是配置文件,一开始是xml,但是过于繁琐,别的语言接口都写好几个了,java还在配置环境。于是spring boot出来了,用默认配置+注解解决了这一的问题。

但是spring boot并不是脱离的spring的,而是spring的【扩展】,这里就要吹spring牛皮啊,或者进行的很好。简单的说明下。

spring是吧读取配置文件抽象了出来,他可以读取各种格式的配置文件,只要你实现他的接口就行,并把格式定义好。这里是先把Bean的定义信息先读取出来了。

Bean工厂增强处理器

就是上面的【BeanFactoryPostProcessor】,这是个Bean接口,看看官方翻译:

1
2
* Allows for custom modification of an application context's bean definitions,
* adapting the bean property values of the context's underlying bean factory.

什么不懂?没事翻译!

然后,通过流程图得知,哦!是处理Bean定义信息的啊,好家伙,Bean的定义信息有什么处理的。不就是字符串的数据吗?举个例子,xml数据源配置:

1
2
3
4
5
6
7
<!-- dbcp 数据源配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

其实【${jdbc.username}】就是一个字符串,怎么转成root的呢?实现BeanFactoryPostProcessor接口,处理这样类似的数据不是很轻松吗。这只是其中一个例子。

总得来说,就是Bean定义信息增强器,我们写一套程序专门【批量】处理bean定义信息,比如:加密!!!自己体会。

定义信息增强完,在进行一系列的处理,开始实例化Bean

实例化Bean

拿到Bean 的信息,并且经过一系列处理,开始实例化bean,怎么实例化呢?当然是反射了!!!

没错,IOC最终还是通过反射去实例化!值得注意的是,这里的实例化是只是把对象【创建】出来。然后接着去填充属实,就是给对象的属性赋值!实例化就完成了。当然对象创建出来了,我们可以进行下一步的增强的工作了。

Bean增强器【BeanPostProcessor】

这个接口是对实例完成后的Bean进行增强操作。你会和BeanFactoryPostProcessor怎么这么像啊。我们看看解释

BeanFactoryPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Allows for custom modification of an application context's bean definitions,
adapting the bean property values of the context's underlying bean factory.

Application contexts can auto-detect BeanFactoryPostProcessor beans in
their bean definitions and apply them before any other beans get created.

Useful for custom config files targeted at system administrators that
override bean properties configured in the application context.

See PropertyResourceConfigurer and its concrete implementations
for out-of-the-box solutions that address such configuration needs.

A BeanFactoryPostProcessor may interact with and modify bean
definitions, but never bean instances. Doing so may cause premature bean
instantiation, violating the container and causing unintended side-effects.
If bean instance interaction is required, consider implementing

好家伙,又是洋文。看翻译!!!

1
2
3
4
5
6
7
8
9
10
11
12
允许自定义修改应用程序上下文的bean定义,
调整上下文的底层bean工厂的bean属性值。
应用程序上下文可以自动检测BeanFactoryPostProcessor
它们的bean定义,并在创建任何其他bean之前应用它们。
适用于针对系统管理员的自定义配置文件
覆盖在应用程序上下文中配置的bean属性。
请参阅PropertyResourceConfigurer及其具体实现
用于解决此类配置需求的开箱即用的解决方案。
BeanFactoryPostProcessor可以与bean交互并修改bean
定义,而不是bean实例。这样做可能会导致早熟
实例化,违反容器并导致意想不到的副作用。
如果需要bean实例交互,请考虑实现

没错,是修改(增强)Bean定义信息的,他是BeanFactoryPostProcessor,从名字就能看出来。

BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
Factory hook that allows for custom modification of new bean instances,
e.g. checking for marker interfaces or wrapping them with proxies.

ApplicationContexts can autodetect BeanPostProcessor beans in their
bean definitions and apply them to any beans subsequently created.
Plain bean factories allow for programmatic registration of post-processors,
applying to all beans created through this factory.

Typically, post-processors that populate beans via marker interfaces
or the like will implement {@link #postProcessBeforeInitialization},
while post-processors that wrap beans with proxies will normally
implement {@link #postProcessAfterInitialization}.

翻译:

1
2
3
4
5
6
7
8
9
10
允许自定义修改新bean实例的工厂钩子,
例如,检查标记接口或用代理包装它们。
ApplicationContexts可以在它们的
bean定义,并将它们应用于随后创建的任何bean。
普通bean工厂允许对后处理器进行编程注册,
应用到通过此工厂创建的所有bean。
通常是通过标记接口填充bean的后处理器
或类似的方法将实现{@link #postProcessBeforeInitialization},
而用代理包装bean的后处理器通常会
实现{@link # postProcessAfterInitialization}。

没错,允许【自定义修改新Bean实例】,就是对实例完毕的对象进行进一步处理。如aop、检查Bean。。。等等一系列操作。

BeanPostProcessor有两个方法postProcessBeforeInitializationpostProcessAfterInitialization,分别处理Bean 的前置方法、后置方法。

在中间会执行Bean初始化方法【init-method】

全部执行完毕bean就实例化完成,最后放入spring容器里。最终,感受到spring属实牛皮,扩展性贼强,希望以后鄙人也能写出这样的代码!!!

一套流程流程走下来,完全可以感受到spring boot完全依赖spring开发,只不过spring boot的核心是【约定大于配置】。

BeanFactory和FactoryBean的区别

BeanFactory:Bean工厂,生产一些模板类型的对象!怎么理解呢,就是我们xml、或者@Component、@Service等声明的对象,注入到BeanFactory里。

FactoryBean:其实是一个普通对象,也由BeanFactory管理,不过他的作用是生成对象!唉,你会说BeanFactory已经包含了全部对象,那还生产什么对象?上面说了,BeanFactory是生产模板类型的对象,而FactoryBean是可以按照我们的方法生产,我们想要的对象。举个例子,Feign:我想通过网络去获取一个对象,生产出来,BeanFactory并不能做到,而FactoryBean是ok的。

spring的生命周期

直接看源码解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
* <p>Bean factory implementations should support the standard bean lifecycle interfaces
* as far as possible. The full set of initialization methods and their standard order is:
* <ol>
* <li>BeanNameAware's {@code setBeanName}
* <li>BeanClassLoaderAware's {@code setBeanClassLoader}
* <li>BeanFactoryAware's {@code setBeanFactory}
* <li>EnvironmentAware's {@code setEnvironment}
* <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
* <li>ResourceLoaderAware's {@code setResourceLoader}
* (only applicable when running in an application context)
* <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
* (only applicable when running in an application context)
* <li>MessageSourceAware's {@code setMessageSource}
* (only applicable when running in an application context)
* <li>ApplicationContextAware's {@code setApplicationContext}
* (only applicable when running in an application context)
* <li>ServletContextAware's {@code setServletContext}
* (only applicable when running in a web application context)
* <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
* <li>InitializingBean's {@code afterPropertiesSet}
* <li>a custom init-method definition
* <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
* </ol>
*
* <p>On shutdown of a bean factory, the following lifecycle methods apply:
* <ol>
* <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
* <li>DisposableBean's {@code destroy}
* <li>a custom destroy-method definition
* </ol>

重要翻译

1
2
* <p>Bean factory implementations should support the standard bean lifecycle interfaces
* as far as possible. The full set of initialization methods and their standard order is:
1
Bean工厂实现应该支持标准的Bean生命周期接口尽可能地。完整的初始化方法及其标准顺序是:
1
2
3
4
5
6
* <p>On shutdown of a bean factory, the following lifecycle methods apply:
* <ol>
* <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
* <li>DisposableBean's {@code destroy}
* <li>a custom destroy-method definition
* </ol>
1
2
3
4
5
6
在关闭bean工厂时,应用以下生命周期方法:
{@code postProcessBeforeDestruction}的DestructionAwareBeanPostProcessors方法
*
disablebean的{@code destroy}
*
自定义销毁方法定义

没错,经过一系列的XXXAware==>BeanPostProcessors.postProcessBeforeInitialization==>InitializingBean==>自定义初始化方法==>BeanPostProcessors.postProcessAfterInitialization==>销毁。