写在前面

  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的配置的背后的配置的更多相关文章

  1. spring tx:advice 和 aop:config 配置事务

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. spring AOP 之二:@AspectJ注解的3种配置

    @AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...

  3. [转]spring tx:advice 和 aop:config 配置事务

      <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www. ...

  4. 《Java Spring框架》Spring切面(AOP)配置详解

    1.  Spring 基本概念 AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2 ...

  5. Spring之AOP原理、代码、使用详解(XML配置方式)

    Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理.AOP代码以及AOP使用三个方向来讲AOP.先给出一张AOP相关的结构图,可以放大查看. 一.Spring AOP 接口设计 1.P ...

  6. 阶段3 2.Spring_08.面向切面编程 AOP_9 spring基于注解的AOP配置

    复制依赖和改jar包方式 src下的都复制过来. 复制到新项目里了 bean.xml里面复制上面一行代码到下面.把aop改成context. 配置spring容器创建时要扫描的包 Service的配置 ...

  7. Spring MVC 中使用AOP 进行统一日志管理--XML配置实现

    1.介绍 上一篇博客写了使用AOP进行统一日志管理的注解版实现,今天写一下使用XML配置实现版本,与上篇不同的是上次我们记录的Controller层日志,这次我们记录的是Service层的日志.使用的 ...

  8. 死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  9. Spring的IOC和AOP之深剖

    今天,既然讲到了Spring 的IOC和AOP,我们就必须要知道 Spring主要是两件事: 1.开发Bean:2.配置Bean.对于Spring框架来说,它要做的,就是根据配置文件来创建bean实例 ...

随机推荐

  1. overridePendingTransition()使用

    实现两个 Activity 切换时的动画.在Activity中使用有两个参数:进入动画和出去的动画. 注意1.必须在 StartActivity()  或 finish() 之后立即调用.2.而且在 ...

  2. 【前端】上拉加载更多dropload.min.js的使用

    代码如下:入职代码修改接口及html为自己的即可(下面主要展示js部分) <!DOCTYPE html><html> <head> <meta charset ...

  3. 简单的UDP接受程序

    //功能:客服端发送UDP包,服务器接受到并打印出来//2015.9.13成功 #include <stdio.h>#include <sys/socket.h>#includ ...

  4. 关于SizeOf、Length

    结论: 到底什么时候用Length,SizeOf呢,我总结下使用Length,Sizeof的场景 1.Length(静态数组或动态数组)----没有问题 2.Length(string/shortst ...

  5. Effective STL 学习笔记 39 ~ 41

    Effective STL 学习笔记 39 ~ 41 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...

  6. 虚拟机的安装以及Linux的学习

    安装虚拟机 对虚拟机的认识 其实初中的时候我就听说过虚拟机这个名词,当时的我还小,也不知道虚拟机是个什么东西,那时我傻傻的认为虚拟机只不过是电脑中的一个虚拟的计算机,没有什么实在的作用.后来随着大学课 ...

  7. SCTF 2015 pwn试题分析

    Re1 是一个简单的字符串加密.程序使用了多个线程,然后进行同步.等加密线程加密好了之后才会启动验证线程.这个题比较坑的是IDA F5出来的结果不对,不知道是不是混淆机制. 刚开始看的是F5后的伪代码 ...

  8. 使用mybatis-generator-core自动生成代码

    SSM框架可以使用mybatis-generator-core-1.3.2.jar来自动生成代码,以下是配置和使用的代码. generatorConfig.xml <?xml version=& ...

  9. SQL技巧两则:选择一个表的字段插入另一个表,根据其它表的字段更新本表内容

    最近,在作django数据表迁移时用到的. 因为在django中,我把本来一个字符型字段,更改成了外键, 于是,哦喝~~~字符型字段相当于被删除了, 为了能导入这些字段的外键信息,于是出此下策. 其实 ...

  10. day1作业:编写登录窗口一个文件实现

    思路: 1.参考模型,这个作业我参考了linux的登录认证流程以及结合网上银行支付宝等锁定规则: 1)认证流程参考的是Linux的登录:当你输入完用户名密码后再验证用户名是否存在用户是否被锁定,然后在 ...