spring---aop(5)---Spring AOP的配置的背后的配置
写在前面
Spring AOP中Pointcut,dvice 和 Advisor三个概念
1)切入点 Pointcut
在介绍Pointcut之前,有必要先介绍 Join Point(连接点)概念。
连接点:程序运行中的某个阶段点,比如方法的调用、异常的抛出等。比如方法doSome();Pointcut是JoinPoint的集合,它是程序中需要注入Advice 的位置的集合,指明Advice要在什么样的条件下才能被触发。org.springframework.aop.Pointcut接口用来指定到特定的类和方法。
2)通知Advice
它是某个连接点所采用的处理逻辑,也就是向连接点注入的代码。例如:输出的日志信息 就是一个Advice。
3)Advisor
Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码。
<aop:aspectj-autoproxy/>
<aop:config proxy-target-class="true">
<aop:pointcut id="servicePointcut" expression="execution(* com.cpic..*Service.*(..))" />
<aop:advisor pointcut-ref="servicePointcut" advice-ref="txAdvice" order="3" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" />
</tx:attributes>
</tx:advice>
接下来就需要看下配置完成之后是如何生成代理对象的
还是要从对xml中的配置<aop:config>标签的解析来入手。同样是从标签解析接口开始,即找BeanDefinitionParser的实现类,最终我们会找到AspectJAutoProxyBeanDefinitionParser是用来处理aspectj-autoproxy标签的,而ConfigBeanDefinitionParser则是用来处理aop:config标签的。看下ConfigBeanDefinitionParser的解析过程:
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
以上过程比较费劲,有兴趣的可以弄清楚。这里主要注册一些Advisor,同时注册了一个AspectJAwareAdvisorAutoProxyCreator,并且设置xml中所配置的proxy-target-class和expose-proxy到它的属性中。AspectJAwareAdvisorAutoProxyCreator本身存储着配置信息,然后使用这些配置创建出来代理对象,在它的父类AbstractAutoProxyCreator的createProxy方法中:
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
//重点1 proxyFactory.copyFrom(this);将ProxyConfig信息复制到ProxyFactory 中。ProxyFactory、AspectJAwareAdvisorAutoProxyCreator都继承了ProxyConfig,ProxyConfig拥有代理的一些配置信息。看下ProxyConfig:
proxyFactory.copyFrom(this);
//重点2
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
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);
}
//重点3
return proxyFactory.getProxy(this.proxyClassLoader);
}
然后我们就详细的说明下整个过程:
重点1:proxyFactory.copyFrom(this);将ProxyConfig信息复制到ProxyFactory 中。ProxyFactory、AspectJAwareAdvisorAutoProxyCreator都继承了ProxyConfig,ProxyConfig拥有代理的一些配置信息。
看下ProxyConfig:
public class ProxyConfig implements Serializable {
//是否强制使用CGLIB 代理对象
private boolean proxyTargetClass = false;
//是否优化
private boolean optimize = false;
boolean opaque = false;
//是否在线程内部暴露出代理对象
boolean exposeProxy = false;
private boolean frozen = false;
}
重点2:复制完配置信息后,看下proxyTargetClass 属性是否为false,则查看目标类是否含有接口,若无则仍然设置proxyTargetClass为true,若有则把接口设置到ProxyFactory中。然后在设置些Advisor、targetSource等其他参数,为创建代理对象做准备。来看下上述Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);的具体内容:
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
// Handle prototypes correctly...
Advisor[] commonInterceptors = resolveInterceptorNames();
List<Object> allInterceptors = new ArrayList<Object>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors != null) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
////重点重点重点重点重点重点重点(对配置信息中的specificInterceptors全部封装成Advisor。再看下具体的封装过程,在上述wrap方法中 )
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
具体的封装过程(如果是Advisor直接返回不处理,接下来必须是Advice,然后通过MethodInterceptor和AdvisorAdapter 对Advice进行包装。)
@Override
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
重点3:使用DefaultAopProxyFactory来创建AopProxy,有了AopProxy我们就能创建代理对象了。看下AopProxy的创建过程:
@Override
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.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
这里决定着到底采用jdk动态代理还是cglib方式来创建代理对象。
条件1:config.isOptimize()是否进行优化,默认是false。
条件2:config.isProxyTargetClass()就是ProxyConfig的proxyTargetClass属性,是否强制使用cglib代理。但它为true也不是肯定就采用cglib,因为下面还有一个判断条件,即目标类是接口,则使用jdk动态代理的方式。
条件3:hasNoUserSuppliedProxyInterfaces(config)目标类没有实现接口,或者有但是是接口类型是SpringProxy
只要上述三个条件有一个为true并且目标类不是接口就会采用cglib方式来创建代理对象,其他情况使用jdk动态代理的方式来创建。
有了JdkDynamicAopProxy和ObjenesisCglibAopProxy则可以顺利创建出代理对象,便可以跳到这篇文章http://www.cnblogs.com/chihirotan/p/7063781.html,至此整个过程就连接通了。
总结
我们先看下ProxyFactory是什么东西。 把下面的图理解透了,就掌握了SpringAOP的整个运行机制
spring---aop(5)---Spring AOP的配置的背后的配置的更多相关文章
- spring tx:advice 和 aop:config 配置事务
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- spring AOP 之二:@AspectJ注解的3种配置
@AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...
- [转]spring tx:advice 和 aop:config 配置事务
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www. ...
- 《Java Spring框架》Spring切面(AOP)配置详解
1. Spring 基本概念 AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2 ...
- Spring之AOP原理、代码、使用详解(XML配置方式)
Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理.AOP代码以及AOP使用三个方向来讲AOP.先给出一张AOP相关的结构图,可以放大查看. 一.Spring AOP 接口设计 1.P ...
- 阶段3 2.Spring_08.面向切面编程 AOP_9 spring基于注解的AOP配置
复制依赖和改jar包方式 src下的都复制过来. 复制到新项目里了 bean.xml里面复制上面一行代码到下面.把aop改成context. 配置spring容器创建时要扫描的包 Service的配置 ...
- Spring MVC 中使用AOP 进行统一日志管理--XML配置实现
1.介绍 上一篇博客写了使用AOP进行统一日志管理的注解版实现,今天写一下使用XML配置实现版本,与上篇不同的是上次我们记录的Controller层日志,这次我们记录的是Service层的日志.使用的 ...
- 死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- Spring的IOC和AOP之深剖
今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...
随机推荐
- Windows版Oracle重建EM---备注
前提条件添加环境变量 ORACLE_HOSTNAME=<主机名:如:DESKTOP-P6J1a>ORACLE_SID=orclORACLE_UNQNAME=orcl 执行删除命令 C:\U ...
- tensorflow session 和 graph
graph即tf.Graph(),session即tf.Session(),很多人经常将两者混淆,其实二者完全不是同一个东西. graph定义了计算方式,是一些加减乘除等运算的组合,类似于一个函数.它 ...
- 编译环境搭建:Makefile
前言 长久以来,笔者一直想用一种管理工具,将所编写的测试程序.算法代码以及工程代码统一管理起来.因为有些是用Java写的有些是用C++写的.虽有想法,但却无行动.这又让我想起了昨天晚上看到一部电影里所 ...
- ASP.NET中Literal,只增加纯粹的内容,不附加产生html代码
页面代码 <div style="float: right; color: #666; line-height: 30px; margin-right: 12px;" id= ...
- 左列動態添加菜單Repeater
前台代碼: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="left.aspx. ...
- poj 3270(置换群+贪心)
Cow Sorting Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6993 Accepted: 2754 Descr ...
- MIT6.006Lec02:DocumentDistance
MIT6.006是算法导论,Lec02讲的是Document Distance(文档距离),比如比较两个文档相似度或者搜索引擎中都会用到. 计算步骤为: 1.将每个文档分离为单词 2.统计词频 3.计 ...
- KnockoutJs学习笔记(四)
由于Writable computed observables和How dependency tracking works的要求相对较高,我先跳过这两篇,学习Pure computed observa ...
- 8-13 Just Finish it up uva11093
题意:环形跑道上有n n<=100000 个加油站 编号为1-n 第i个加油站可以加油pi加仑 从加油站i开到下一站需要qi加仑 你可以选择一个加油站作为起点 初始油箱为空 如果 ...
- Oracle登录
一.安装Oracle 二.打开Database Configuration Assistant 三.打开SQL Plus 1.打开SQL Plus 2.输入用户名sys 3.输入口令1 as sysd ...