花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍。那看书有什么用呢?主要还是扩展视野,毕竟书是别人总结出来的东西,看一遍可以发现自己的理解偏差,而且还可以看到一些平时不太关注的内容,当然看也可以是一种学习技术的方式。

最开始只是想了解一下AOP,没想到就陷的这么深,为了搞清楚spring是如何完成切面功能这两天还是把Ioc部分的内容也给读了读。还是看懂了大概,只不过这复杂的内部结构确实不易理解与阅读,我在想Spring确实是个好的开源软件,但代码可能真的少了点亲近感。一个BeanFactory和FactroyBean就可以写上好几页纸来说明,毕竟这些名字没有多少Ioc的影子。

一、Spring Ioc的简单理解

对于Ioc的功能就不再多说,这里主要是理解一下Ioc的关键代码,至于BeanFactory、ApplicationContent、Resource之类就不说了,直接从getBean开始吧。

从最基本的容器开始:

public interface ISay {
void say();
void noaop();
} public class SayImpl implements ISay{ public void say() {
System.out.print("我是5207.");
} public void noaop() {
System.out.println("别aop我");
} } public class Client { @SuppressWarnings("resource")
public static void main(String[] args) {
XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
say1.say();
say1.noaop();
}
}

这里面有一个sayImpl的java代码,下面是spring.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="sayImpl" class="aop.demo.SayImpl"></bean>
</beans>

运行Client的会得到下面的结果:

我是5207.别aop我

这里的XmlBeanFactory只是用来解析Xml文件而创建的类,它本身只是传递了一个Resource而已,最终的容器功能是在AbstractBeanFactory中完成,那么我们直接就看AbstractBeanFactory中getBean的实现,而getBean又调用了doGetBean方法,这才是本尊。

下面是doGetBean的主要代码,代码太多,有些不是很重要的就删除了。

protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException { final String beanName = transformedBeanName(name);
Object bean; //先从缓存里查找单例的对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//如果对象存在则判断是否要通过FactoryBean来审批
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//判断是否需要从父ParentBeanFactory获得对象
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
} //先把当前bean依赖的bean给加载起来,通过getBean递归来完成
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
} if (mbd.isSingleton()) {
//创建单实例的对象
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
//这里是真正的实例化一个对象的地方
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
//看看是不是FactoryBean,如果是的话要使用FactoryBean.getObject来返回对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 如果是prototype,创建新对象
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
} return (T) bean;
}

其实上面的代码中可以找出一段代码中完成核心调用的是createBean和getObjectForBeanInstance这两个方法。getObjectForBeanInstance的主要逻辑是

  • 检查对象是否为FactoryBean类型,如果不是直接返回对象实例
  • 如果是FactoryBean类型,那么就要通过FactoryBean的getObject来返回对象实例

看到这里就明白了FactoryBean的用处了吧,这个也是Spring Aop与Ioc进行交互的一个重要逻辑。

再来看看createBean

会发现createBean在AbstractBeanFactory中只是一个虚方法,说明是在派生类中实现的,前面是从XmlBeanFactory开始的,那么顺着它的继承关系看看,最终是在AbstractAutowireCapableBeanFactory中找到了createBean的实现:

@Override
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
....省略.
Object beanInstance = doCreateBean(beanName, mbd, args);
....省略.
return beanInstance;
} protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); ....省略.
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
....省略.
}
....省略.
return exposedObject;
}

代码太多了,找关键部分吧。createBean的过程主要是

  • 生成bean的wrapper

首先是创建好bean,然后将新创建的bean实例并放在BeanWrapper中返回。

  • populateBean的调用

1、先是执行InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation,这里会有实例创建后的一个回调,如果是false会退出整个过程。

2、完成对象的注入autowireByName/autowireByType,这里就看到很眼熟的东西了吧“autowire”。

3、调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues

4、最后是applyPropertyValues方法,把属性值都写入

  • initializeBean的调用

1、先是invokeAwareMethods,完成BeanNameAware、BeanClassLoaderAware、BeanFactoryAware的注入

2、然后是BeanPostProcessor的postProcessBeforeInitialization调用

3、完成InitializingBean的调用

4、然后是BeanPostProcessor的postProcessAfterInitialization调用,这里就是后面Aop中Advistor接管对象的地方啦

可以看到initializeBean方法最后返回的是wrappedBean有两种可能,一种是经过BeanPostProcessor生成的对象,如果当前bean没有注册BeanPostProcessor的话就直接返回bean自己。aop中就运用这个方法来完成代码的切面编程。

小结

写到这Ioc创建一个对象的过程就说完了,过程中发现了很多Ioc容器对一个对象生成过程的技巧与奥秘,也理解了为什么可以在spring中灵活的完成对对象创建的各种“干预”。

二、Advisor与Ioc的联系

好了,接下来开始进入spring aop部分,前面的例子做一些补充加入aop的功能,spring.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 声明被代理的目标对象 -->
<bean id="sayImpl" class="aop.demo.SayImpl"></bean>
<!-- 声明用于增强的拦截器对象 -->
<bean id="sayImplAroundAdvice" class="aop.demo.SayImplAroundAdvice"></bean> <!-- 配置一个切面 -->
<bean id="sayAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="sayImplAroundAdvice"/> <!-- 增强 -->
<property name="pattern" value="aop.demo.SayImpl.s.*"/> <!-- 切点(正则表达式) -->
</bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean>
</beans>

这里有一个环绕增强及一个切面,最后使用DefaultAdvisorAutoProxyCreator来完成自动代理。前面在学习Ioc时使用的是XmlBeanFactory,是一个基本的基于xml的ioc容器,那么我们再使用ClassPathXmlApplicationContext吧,看看会发生什么变化:

public class Client {

	@SuppressWarnings("resource")
public static void main(String[] args) {
XmlBeanFactory benBeanFactory = new XmlBeanFactory(new ClassPathResource("aop/demo/spring.xml"));
ISay say1 = (ISay) benBeanFactory.getBean("sayImpl");
say1.say();
say1.noaop(); ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml");
ISay say = (ISay)context.getBean("sayImpl");
say.say();
say.noaop();
}
}

执行结果:

我是5207.别aop我

大家好:我是5207.希望大家多多点赞.

别aop我

可以发现使用ClassPathXmlApplicationContext容器时aop的功能才有用,有点奇怪啊,都是读取的配置为啥结果不同呢?这里主要是BeanFactory和ApplicationContext的主要区别之一。查看XmlBeanFactory的加载过程发现其只是调用了一个XmlBeanDefinitionReader读取了spring.xml并解析为BeanDefinition,然后具体的bean都要到getBean时才会去实例化,而且bean的依赖也要自己去处理。

那applicationContext呢?从ClassPathXmlApplicationContext的代码发现就复杂一些,在构造函数中有一个调用比较特别:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}

看到这里发现多了一个refresh()的过程,进去一看一堆的处理。其实主要过程就是初始化Ioc容器,完成容器的配置,加载好并注册好各种回调。比如前面提到的BeanPostProcessor就是在这个refresh里就完成了注册。

正因为refresh这个过程我们用起spring来才会觉得简单好用。

先从DefaultAdvisorAutoProxyCreator说起

有了上面的基础再来看DefaultAdvisorAutoProxyCreator就会简单的多。我们可以看一下DefaultAdvisorAutoProxyCreator的类继承关系,其中有一个AbstractAutoProxyCreator需要特别关注。这个AbstractAutoProxyCreator实现了一堆的接口:

public abstract class AbstractAutoProxyCreator extends ProxyConfig
implements SmartInstantiationAwareBeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
Ordered, AopInfrastructureBean {

其中SmartInstantiationAwareBeanPostProcessor继承关系中最底层的是BeanPostProcessor,前面说明Ioc的时候提到过,BeanPostProcessor可以在getBean时返回BeanPostProcessor加工过的对象。好了,奥秘就在这里啦,看一下AbstractAutoProxyCreator中如何实现的吧:

public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
} /**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

其中Before方法啥也没做,都在After中完成的。在After中有一个wrapIfNecessary方法,这个方法的作用是如果需要进行代理则生成代理并返回代理对象实例。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
} // Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

方法中使用了一个缓存advisedBeans存取bean是否需要advise,如果是需要的话就会存TRUE,否则就是FALSE,这样下次再来取时不用再判断直接返回结果就行。当然最重要的是如果是需要为bean生成代理时则会创建代理啦。至于如何判断是否需要代理是通过一个getAdvicesAndAdvisorsForBean方法来完成的,如果getAdvicesAndAdvisorsForBean返回的是DO_NOT_PROXY则表示不需要代理。为了扩展getAdvicesAndAdvisorsForBean是抽象方法。从DefaultAdvisorAutoProxyCreator的父类AbstractAdvisorAutoProxyCreator中找到了getAdvicesAndAdvisorsForBean的实现:

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
List advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}

这里面会查找所有的Advisors并返回,当然这个过程中因为Advisor是包含了Advice的,所以Aop的基本要素就到齐了。然后回到wrapIfNecessary中,接着就会调用createProxy来生成代理对象啦。

protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this); if (!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
for (Class<?> targetInterface : targetInterfaces) {
proxyFactory.addInterface(targetInterface);
}
} Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
} proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
} return proxyFactory.getProxy(this.proxyClassLoader);
}

createProxy过程主要是通过ProxyFactory来生成代理对象,在之前的《学习AOP之深入一点Spring Aop》里已经讲过。最后将生成的代理对象放入缓存并返回给Ioc容器。

从些之后,getBean时获得的已经不是beanName的实际对象,而是代理对象。

学习AOP之透过Spring的Ioc理解Advisor的更多相关文章

  1. Spring的IOC理解(转载)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  2. Spring学习(二)--Spring的IOC

    1.依赖反转模式 依赖反转:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口.抽象接口不应该依赖于具体实现.而具体实现则应该依赖于抽象接口. 在面向对象编程领域中,依赖反转原则(Depe ...

  3. Spring学习(六)--Spring的IOC

    1.autowiring(自动依赖装配)的实现 自动装配中不需要对Bean属性做显示的依赖管理方式,只需要配置好autowiring的属性就可以,IOC容器会自动根据这个属性的配置通过反射自动找到属性 ...

  4. Spring学习(四)--Spring的IOC

    1.BeaDefinition的Resource定位 (1)直接使用BeanDefinitionFactory 定义一个Resource来定位容器使用的BeanDefinition. Resource ...

  5. Spring学习(三)--Spring的IOC

    1.BeanFactory和FactoryBean BeanFactory是一个接口类,定义了IOC容器最基本的形式,提供了IOC容器所应该遵守的基本服务契约. FactoryBean是一个能产生或者 ...

  6. Spring的Ioc理解

    1.Ioc=控制反转和依赖注入(DI),两个是一回事 控制反转的好处: 把对象的创建和依赖定义在xml中,改变子类的实现变得很简单 控制反转减轻了对象之间的耦合度,减轻了对象之间的依赖关系,增加了系统 ...

  7. Spring学习(五)--Spring的IOC

    1.BeanDefinition在IOC的注册 当BeanDefinition完成载入和解析之后,用户定义的BeanDefinition在IOC容器中已经建立自己的数据结构和数据表示,但是无法使用,需 ...

  8. 浅析对spring中IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  9. spring IOC理解

    用spring做了几个项目后发现,对spring的IOC理解还是不够清晰,今天就来总结下自己的理解(个人的一些见解) 以前用jsp+servlet做网站时,只是分了显示层(jsp),控制层(servl ...

随机推荐

  1. NPM (node package manager) 入门 - 基础使用

    什么是npm ? npm 是 nodejs 的包管理和分发工具.它可以让 javascript 开发者能够更加轻松的共享代码和共用代码片段,并且通过 npm 管理你分享的代码也很方便快捷和简单. 截至 ...

  2. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  3. Windows 7上执行Cake 报错原因是Powershell 版本问题

    在Windows 7 SP1 电脑上执行Cake的的例子 http://cakebuild.net/docs/tutorials/getting-started ,运行./Build.ps1 报下面的 ...

  4. [APUE]系统数据文件与信息

    一.口令文件 UNIX口令文件包含下表中的各个字段,这些字段包含在 由于历史原因,口令文件是/bin/passwd,而且是一个文本文件,每一行都包括了上表中的七个字段,字段之间用":&quo ...

  5. jQuery学习之路(3)- 事件

    ▓▓▓▓▓▓ 大致介绍 jQuery增加了并扩展了基本的事件处理机制,不但提供了更加优雅的事件处理语法,而且极大地增强了事件处理能力 ▓▓▓▓▓▓ jQuery中的事件 ▓▓▓▓▓▓ 加载DOM 在j ...

  6. Linux 常用命令(持续补充)

    常用命令: command &:将进程放在后台执行 ctrl + z:暂停当前进程 并放入后台 jobs:查看当前后台任务 bg( %id):将任务转为后台执行 fg( %id):将任务调回前 ...

  7. JavaScript基础知识总结(一)

    当我们接触一种新语言时,首先要先了解它,对它有一定的理论认识. 那么,什么是JavaScript呢? JavaScript是一种脚本语言,由web浏览器进行解释和执行.它包括ECMAScript.DO ...

  8. 代码的坏味道(21)——中间人(Middle Man)

    坏味道--中间人(Middle Man) 特征 如果一个类的作用仅仅是指向另一个类的委托,为什么要存在呢? 问题原因 对象的基本特征之一就是封装:对外部世界隐藏其内部细节.封装往往伴随委托.但是人们可 ...

  9. 【C#公共帮助类】 Utils 10年代码,最全的系统帮助类

    为大家分享一下个人的一个Utils系统帮助类,可能有些现在有新的技术替代,自行修改哈~ 这个帮助类主要包含:对象转换处理 .分割字符串.截取字符串.删除最后结尾的一个逗号. 删除最后结尾的指定字符后的 ...

  10. Android Socket连接PC出错问题及解决

    最近测试问题:Android 通过Socket链接电脑,ip和端口都是正确的,也在同一网段,可android端就是报异常如下: 解决办法:测试电脑的防火墙可能开着,在控制面板把防火墙打开即可.