最近有小伙伴儿遇到了一个问题来咨询我,问题大致如下:

他在Service层利用Aspect设置了一个Spring AOP代理,在单元测试以及在service层代码上添加代理的时候均没有发现问题,但是在web服务中的controller层代码添加代理的时候却不成功。

其代码大概如下:

@Component
public class CoreBusiness {
public void doSomething() {
System.out.println("I did something");
}
} @Controller
public class CoreController {
public void doSomething() {
System.out.println("I did something");
}
} @Component
@Aspect
public class CrossCuttingConcern {
@Before("execution(* com.test.CoreBusiness.*(..)) || execution(* com.test.CoreController.*(..))")
public void doCrossCutStuff(){
System.out.println("Doing the cross cutting concern now");
}
}

同时在Service层有如下的配置:

<aop:aspectj-autoproxy expose-proxy="true"/>

其实要弄清楚这个问题需要明白两点:

1、双上下文的概念

2、AOP Proxy的创建机制

我们先来看看什么情况下我们会用到双上下文

场景1:

现在假设我们有一个客户端程序,

private static ApplicationContext context = new ClassPathXmlApplicationContext("client.xml");
context.getBean(name);

在上面的程序中,我们会通过一个上下文加载所有的bean,所以不会涉及到双上下文的问题。

场景2:

接下来,我们需要开发一个web应用程序,并且使用Tomcat容器,在web.xml中我们添加了如下的配置:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

添加该配置后,web应用在启动时会加载applicationContext.xml中定义的所有bean,这里也不会涉及到双上下文。

场景3:

我们在web应用程序中引入了Spring MVC框架,并在web.xml中进行了如下的配置,

<servlet>
<servlet-name>springweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet> <servlet-mapping>
<servlet-name>springweb</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

如此一来,tomcat启动的时候会将springweb-servlet.xml中定义的bean进行初始化。

这个场景下依然没有双上下文的介入。

场景4:

这一次我们希望解耦web框架与底层的业务逻辑框架,因此我们又对web.xml进行了如下的修改,

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<servlet-name>springweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping>
<servlet-name>springweb</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

如此一来,就会出现双上下文的情形。

因为tomcat启动时,ContextLoaderListener会初始化所有在applicationContext.xml中定义的beans。

而FrameworkServlet会初始化springweb-servlet.xml中定义的beans。

这时就出现了两个应用上下文,那么这两个上下文是什么关系呢?

我们来看看FrameworkServlet是如何初始化servlet所需要的上下文的。

	protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null; if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
......
}

我们可以看到,initWebApplicationContext方法中的第一句就是获取根上下文,有兴趣的同学可以继续跟踪下去,你会发现这个根上下文就是通过ContextLoaderListener加载的应用上下文,到这里我们可以知道这两个上下文是父子的关系。

而且,根据Spring的官方文档,我们也可以获悉到如下的信息:

As detailed in Section 3.13, “Additional Capabilities of the ApplicationContext”, ApplicationContext instances in Spring can be scoped. In the Web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given servlet instance.

到这里我们已经弄清楚了第一个问题,那么既然所有的child applicationcontext都可以继承root applicationcontext,为什么AOP代理在Controller层不生效呢?

其实原因很简单,因为Spring AOP Proxy是在上下文初始化的时候通过BeanPostProcessor这个扩展点创建的。而aspectj autoproxy的配置是放在Service层的,那么也就是根上下文初始化的时候会创建相应的AOP proxy,当Controller层代码所在的servlet webapplicationcontext初始化时,AOP proxy已经创建完毕了。这时servlet webapplicationcontext并不会感知到根上下文中创建的AOP proxy。到这里为止就出现了文中开始处提到问题。

问题的原因已经找到了,那么我们该如何进行处理呢?还是像以往一样,留给读者自己思考吧。

												

“AOP代理”遇到“双上下文”的更多相关文章

  1. [转]使用ProxyFactoryBean创建AOP代理

    http://doc.javanb.com/spring-framework-reference-zh-2-0-5/ 7.5. 使用ProxyFactoryBean创建AOP代理 - Spring F ...

  2. spring源码 — 三、AOP代理生成

    AOP代理生成 AOP就是面向切面编程,主要作用就是抽取公共代码,无侵入的增强现有类的功能.从一个简单的spring AOP配置开始: <?xml version="1.0" ...

  3. Spring AOP代理时 ClassCastException: $Proxy0 cannot be cast to (类型转换错误)

    Spring AOP代理时 ClassCastException: $Proxy0 cannot be cast to (类型转换错误) 问题: 今天在用AfterReturningAdvice时,a ...

  4. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  5. jdk动态代理与cglib代理、spring aop代理实现原理解析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  6. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  7. JavaEE中的MVC(四)AOP代理

    咱们来吹牛,JDK的动态代理在AOP(Aspect Oriented Programming,面向切面编程)中被称为AOP代理,而AOP是Spring框架中的重要组成部分. 代理模式 但是什么是代理模 ...

  8. AOP代理对象生成

    AOP(Aspect-OrientedProgramming,面向方面编程)是OOP(Object-Oriented Programing,面向对象编程)的良好补充与完善,后者侧重于解决 从上到下的存 ...

  9. 记一次Spring的aop代理Mybatis的DAO所遇到的问题

    由来 项目中需要实现某个订单的状态改变后然后推送给第三方的功能,由于更改状态的项目和推送的项目不是同一个项目,所以为了不改变原项目的代码,我们考虑用spring的aop来实现. 项目用的是spring ...

随机推荐

  1. spring学习笔记1

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAATQAAAEBCAIAAAB5VM7WAAAgAElEQVR4nOy9Z3gc13n3zZT3efPESZ

  2. javascript一些比较难理解的知识点

    原文出处:https://segmentfault.com/a/1190000010371988 看了一下这篇文章,自己也手敲了一遍 //1. 立即执行函数 作用:将var变量的作用域限制于函数内,这 ...

  3. js实现每次程序发送一个数据 ,多次发送不一样,5秒后继续执行多次程序,判断如果五秒后发送过来的数据和上次不一样,少的删除多的增加

    /*存储设备ID*/var IDSNew = new Array();//判断是否已经启用服务var isopen = true;//需要放到接收设备数据处IDSNew[client.deviceId ...

  4. 【Java学习笔记之三十一】详解Java8 lambda表达式

    Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...

  5. Java中面向字符的输入流

    Java中面向字符的输入流 2016-12-04 Java程序员联盟 Java程序员联盟 Java程序员联盟 微信号 javalm 功能介绍 莫道君行早,更有早行人 全心敲代码,天道自酬勤 字符流是针 ...

  6. Python 的经典入门书籍有哪些?

    是不是很多人跟你说,学Python开发就该老老实实地找书来看,再配合死命敲代码?电脑有了,软件也有了,心也收回来了?万事俱备,唯独只欠书籍?没找到到合适的书籍?可以看看这些. 1.Python基础教程 ...

  7. 使用DOM解析XML文件,、读取xml文件、保存xml、增加节点、修改节点属性、删除节点

    使用的xml文件 <?xml version="1.0" encoding="GB2312" ?> <PhoneInfo> <Br ...

  8. java中super关键字

    1.子类的构造函数如果要引用super的话,必须把super放在函数的首位,如果想用super继承父类构造的方法,但是没有放在第一行的话,那么在super之前的语句,肯定是为了满足自己想要完成某些行为 ...

  9. LINUX下C语言编程调用函数、链接头文件以及库文件

    LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...

  10. mysql添加外键约束变为索引

    今天有位自己填上一坑:mysql储存引擎 原因就是数据库表引擎为:MyISAM,建立主外键关系需要是InnoDB: 解决方案:alter  table table_name1  engine=inno ...