spring源码 — 三、AOP代理生成
AOP代理生成
AOP就是面向切面编程,主要作用就是抽取公共代码,无侵入的增强现有类的功能。从一个简单的spring AOP配置开始:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 定义target -->
<bean id="human" class="org.lep.springtest.aop.Human">
</bean>
<!-- 定义advice -->
<bean id="sleepHlper" class="org.lep.springtest.aop.SleepHelper">
</bean>
<!-- 定义切点 -->
<bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*sleep"></property>
</bean>
<!-- 定义advisor -->
<bean id="sleepAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="sleepHlper"></property>
<property name="pointcut" ref="sleepPointcut"></property>
</bean>
<!-- 定义代理 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="human"></property>
<property name="interceptorNames" value="sleepAdvisor"></property>
</bean>
</beans>
上面的配置涉及到AOP几个重要的概念:
- pointcut:切点,定义具体什么地方需要增强
- advice:通知,定义在切点处进行哪些增强,也就是在切点处干的事
- advisor:通知器,将pointcut和advice结合起来,也就组成了一个切面,定义了在什么地方做什么事
- target:需要进行增强的目标类,定义了对谁进行增强
- proxy:将切面应用在target上,说明对谁(target)做什么事(advisor)
从上面可以看出是proxy把这些东西结合起来,那么proxy是怎么实现的呢,比如:
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
Sleep sleeper =(Sleep) context.getBean("proxy");
sleeper.sleep();
上面有两个问题:
- 定义的proxy是ProxyFactoryBean类型的,为什么可以强制转化为Sleep接口类型的
- 在调试的时候为什么 sleeper.sleep() 直接跳进了JdkDynamicAopProxy的invoke
针对第一个问题我们分析getBean的过程,也就是ProxyFactoryBean的初始化
ProxyFactoryBean初始化
在spring IoC容器初始化之后我们分析了bean的初始化,最后提到了FactoryBean和BeanFactory的区别,但是没有详细分析涉及到的factoryBean的初始化过程,具体如下:

ProxyFactoryBean的初始化就是先当做普通的bean初始化,之后再获取具备factory能力的bean,这里分为代理对象的生成和获得真正的代理
- createAopProxy
- getProxy
上面图中在ProxyFactoryBean的getSingletonInstance方法中
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
// createAopProxy里面会调用DefaultAopProxyFactory的createAopProxy方法来获取AopProxy
// getProxy会利用上面生成的AopProxy来生成具体的代理对象
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
DefaultAopProxyFactory的createAopProxy如下:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目标类实现了接口则使用jdk生成proxy
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
// 如果没有实现接口,则使用cglib来生成proxy,因为jdk的生成方法只支持实现了接口的类的proxy生成
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
在ProxyFactoryBean的getProxy方法中会调用上面获得到的AopProxy来生成proxy,生成的proxy返回,也就是最后getBean获得到对象,这个对象是实现了Sleep接口的类,只不过这个类存在内存里面,由JdkDynamicProxy动态生成的(也就是动态代理),所以可以向上转型为Sleep
将动态代理类保存在本地,反编译得到:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.lep.springtest.aop.Sleep;
public final class $Proxy0 extends Proxy implements Sleep {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sleep() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("org.lep.springtest.aop.Sleep").getMethod("sleep", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
为什么调用到JdkDynamicProxy的invoke方法
上面已经说到了,因为是在内存中存在的动态代理,这个代理实现了Sleep接口,也就是说调试的时候应该跳转到的是这个代理对象的sleep方法,在sleep方法中调用了InvocationHandler的invoke方法,而JdkDynamicProxy实现了InvocationHandler接口,在JdkDynamicProxy的getProxy方法中
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 把JdkDynamicProxy自身传入,在proxy中调用的就是JdkDynamicProxy的invoke方法
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
但是IDE不知道内存中的代理类类,所以就直接跳转到了JdkDynamicProxy的invoke方法
spring源码 — 三、AOP代理生成的更多相关文章
- Spring 源码学习——Aop
Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...
- Spring源码之AOP的使用
Spring往期精彩文章 Spring源码搭建 Spring源码阅读一 前言 我们都知道Java是一门面向对象(OOP)的语言,所谓万物皆对象.但是它也存在着一些个弊端:当你需要给多个不具有继承关系的 ...
- 专治不会看源码的毛病--spring源码解析AOP篇
昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些.原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们,不 ...
- 面试真题--------spring源码解析AOP
接着上一章对IOC的理解之后,再看看AOP的底层是如何工作的. 1.实现AOP的过程 首先我们要明白,Spring中实现AOP,就是生成一个代理,然后在使用的时候调用代理. 1.1 创建代理工厂 ...
- Spring源码解析-AOP简单分析
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等,不需要去修改业务相关的代码. 对于这部分内容,同样采用一个简单的例子和源码来说明. 接口 public ...
- spring源码解读-aop
aop是指面向切面编程,ProxyFactoryBean是spring aop的底层实现与源头,为什么这么说呢?首先我们看一段配置: 1.target是目标对象,需要对其进行切面增强 2.proxyI ...
- 读spring源码(三)-ClassPathXmlApplicationContext-getBean
这次主要看了下bean的生成过程,发现个画时序图很好用的软件plantuml,充分发挥程序员的能力,能用代码解决的别叨叨别的
- Spring源码学习
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
随机推荐
- easyui中tree使用simpleData的形式加载数据
了解了zTree的使用, 发现它的simpleData是非常好用的, 由后台返回一个扁平数据, 直接在前台解析成树形菜单, 网上查了一下, easyui也可以简单实现, 不过....没看懂, 先记录一 ...
- python多进程
一.多进程池 from multiprocessing import Pool import time pool = Pool(processes=3) result=[];lr=range(t);a ...
- 20145225《Java程序设计》 第5周学习总结
20145225<Java程序设计> 第5周学习总结 教材学习内容总结 第八章 异常处理 8.1语法与继承架构 try.catch:try.catch代表错误的对象后做一些处理. 异常继承 ...
- Mysql基础1
一.数据库简介1.Structured Query Language (结构化查询语言)2.SQL:工业标准.(各个数据库厂商都支持)SQL-Server:对标准进行了扩展.TSQL 方言Oracle ...
- Action类为何要继承ActionSupport
Action类为何要继承ActionSupport 理论上Struts 2.0的Action无须实现任何接口或继承任何类型,但是,我们为了方便实现Action,大多数情况下都会继承com.open ...
- ARM Linux 3.x的设备树(Device Tree)
http://blog.csdn.net/21cnbao/article/details/8457546 宋宝华 Barry Song <21cnbao@gmail.com> 1. ...
- stringgrid事件大全
3.StringGrid之Event事件 property LiveBinding //把对象之间的属性绑定起来(XE2新增功能) property OnApplyStyleLookup: TNo ...
- 编译器问题:运行maven,报错-Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable and mvn script match.
1.新建环境变量M2_HOME 2.指向你的maven安装目录 例如 :M2_HOME=D:\Apps\apache-maven-3.3.9 3.进入Myeclipse进行修改,Window-> ...
- JavaScript-setTimeout
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- Jquery DOM元素的方法
jQuery DOM 元素方法 函数 描述 .get() 获得由选择器指定的 DOM 元素. .index() 返回指定元素相对于其他指定元素的 index 位置. .size() 返回被 jQuer ...