学习AOP之透过Spring的Ioc理解Advisor
花了几天时间来学习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的更多相关文章
- Spring的IOC理解(转载)
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- Spring学习(二)--Spring的IOC
1.依赖反转模式 依赖反转:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口.抽象接口不应该依赖于具体实现.而具体实现则应该依赖于抽象接口. 在面向对象编程领域中,依赖反转原则(Depe ...
- Spring学习(六)--Spring的IOC
1.autowiring(自动依赖装配)的实现 自动装配中不需要对Bean属性做显示的依赖管理方式,只需要配置好autowiring的属性就可以,IOC容器会自动根据这个属性的配置通过反射自动找到属性 ...
- Spring学习(四)--Spring的IOC
1.BeaDefinition的Resource定位 (1)直接使用BeanDefinitionFactory 定义一个Resource来定位容器使用的BeanDefinition. Resource ...
- Spring学习(三)--Spring的IOC
1.BeanFactory和FactoryBean BeanFactory是一个接口类,定义了IOC容器最基本的形式,提供了IOC容器所应该遵守的基本服务契约. FactoryBean是一个能产生或者 ...
- Spring的Ioc理解
1.Ioc=控制反转和依赖注入(DI),两个是一回事 控制反转的好处: 把对象的创建和依赖定义在xml中,改变子类的实现变得很简单 控制反转减轻了对象之间的耦合度,减轻了对象之间的依赖关系,增加了系统 ...
- Spring学习(五)--Spring的IOC
1.BeanDefinition在IOC的注册 当BeanDefinition完成载入和解析之后,用户定义的BeanDefinition在IOC容器中已经建立自己的数据结构和数据表示,但是无法使用,需 ...
- 浅析对spring中IOC的理解
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- spring IOC理解
用spring做了几个项目后发现,对spring的IOC理解还是不够清晰,今天就来总结下自己的理解(个人的一些见解) 以前用jsp+servlet做网站时,只是分了显示层(jsp),控制层(servl ...
随机推荐
- 玩转spring boot——结合AngularJs和JDBC
参考官方例子:http://spring.io/guides/gs/relational-data-access/ 一.项目准备 在建立mysql数据库后新建表“t_order” ; -- ----- ...
- 以项目谈WebGIS中Web制图的设计和实现
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景介绍 一般WebGIS项目中,前端展示数据的流程基本是先做数据入 ...
- CSS 3学习——box-sizing和背景
box-sizing 在CSS 2中设置元素的width和height仅仅是设置了元素内容区的宽和高,元素实际的尺寸是margin + border + padding + 内容区. CSS 3(截止 ...
- ASP.NET Core 中文文档 第四章 MVC(3.7 )局部视图(partial)
原文:Partial Views 作者:Steve Smith 翻译:张海龙(jiechen).刘怡(AlexLEWIS) 校对:许登洋(Seay).何镇汐.魏美娟(初见) ASP.NET Core ...
- GJM : C#设计模式(1)——单例模式
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- class-dump 反编译私有的库和应用
一.下载并安装class-dump 下载class-dump-3.5.dmg 点击下载 下载完成以后双击.dmg的文件,将里面的class-dump拷贝到/usr/local/bin 设置权限chm ...
- Android 微信第三方登录(个人笔记)
今天在写微信登录,花了半天时间搞定.然后写下自己的笔记,希望帮助更多的人...欢迎各位指教. 微信授权登录,官方说的不是很清楚.所以导致有一部分的坑. 微信注册应用平台的应用签名,下载 微信签名生成工 ...
- 自建git node pm2 (不赘述,就说遇见的问题)
//======================[git]部分 主题部分还是按照网上的办法进行安装. 安装的话 分为两个办法(一个是yum (contos办法) 或者sudo(ubuntu办法) ...
- SQL 数据优化索引建suo避免全表扫描
首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...
- Handler
1.1 继承AbstractController优点:能定制请求方式 package cn.happyl.controller; import javax.servlet.http.HttpServl ...