spring aop之父子容器
AuthController
@RestController
public class AuthController { @PostMapping("authentication")
@TokenChecker
public Result getToken(String username,String password){
return null;
}
}
@Aspect
@Component
public class TokenAspect {
//匹配指定类下的有指定注解的方法。只要在要切入的方法上加上注解就可以将该方法作为切入点。
@Pointcut("execution (public * com.cotroller..*.*(..)) && @annotation(com.annotation.TokenChecker)")
public void addAdvice() {
} @Before("addAdvice()")
public void before() {
System.err.println("before");
} @After("addAdvice()")
public void after() {
System.err.println("after");
} }
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
applicationContext.xml关键配置信息
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com"
name-generator="com.xxx">
<!-- 不扫描Controller注解 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation"
expression="org.springframework.context.annotation.Configuration" />
</context:component-scan>
applicationContext-mvc.xml关键配置信息
<context:component-scan base-package="com"
use-default-filters="false">
<!-- 扫描controller注解 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation"
expression="org.springframework.context.annotation.Configuration" />
</context:component-scan>
需要具备知识:
Spring 框架允许在一个应用中创建多个上下文容器。但是建议容器之间有父子关系。可以通过 ConfigurableApplicationContext 接口中定义的 setParent 方法设置父容器。一旦设置父子关系,则可以通过子容器获取父容器中除 PropertyPlaceHolder 以外的所有资源,父容器不能获取子容器中的任意资源(类似 Java 中的类型继承)。
1. 对于传统的web项目来说,通常使用spring和springmvc,因此对于这种项目来讲,他是有两个容器的,一个是spring容器,一般我们会把Service层的东西注入到spring容器中,另一个是springmvc的容器,通常这个容器里注入的是Controller层的东西,这里我们认为spring容器是父容器,springmvc是子容器的概念,然后我们大家都知道通过父子继承关系可知,子容器是可以读取到父容器中的东西,但是父容器是无法读到子容器中的内容,因此基于这个场景,有的同学,把Aop的实现类注入到了spring容器中,并且将Aop的切点表达式配置<aop:config> <aop:pointcut的execution也配到了spring容器的xml,而巧了这位同学要切的类方法,正好是Controller,也就是springmvc容器中的东西,那么这时候问题就来了,aop在初始化时会在自己的容器中寻找能够匹配的类方法,然后给他套上一层代理,此时他在自己能够访问到的spring容器中根本找不到与之匹配的类和方法,因为这些类和方法是在springmvc容器中管理的,因此就没有代理成功。
那么对于上述问题要怎么修改呢?只需要确保你要Aop切的类和方法与你Aop配置切点<aop:config> <aop:pointcut的execution表达式声明是在同一个容器中即可,此时只需要讲这个配置移到springmvc容器的xml中即可
2. 第二种情况与第一种情况有些许的类似,但并不相同,是关于重复扫描的,比如你在spring容器中配置了一个Aop,并且把他托管给spring容器管理,而且execution表达式切的也是spring容器中管理的类和方法,理论上这个时候是好用的,这批execution切到的类都被加了代理,但是巧了,springmvc容器中由于配置的是包路径扫描,恰好把execution表达式切的这一批对象又扫了一遍,又都托管给了springmvc容器,而此时扫到的这批对象,是重新new出来交给springmvc管理的,因此并没有被aop代理,所以在使用时,注入进来的可能是springmvc容器管理的这批对象,因此使用时发现Aop代理失效了。
这个问题的解决方案,就是避免两个容器重复扫描。
3. 第三个问题就比较简单了,他的现象是有些方法被Aop代理成功了,但有个别方法没有代理成功,究其原因发现这部分没有代理成功的方法并不是通过代理对象调用的,而是自身调用的,故被调用的方法没有被Aop代理,无法织入横切逻辑。
这个问题如果理解起来困难的话我举个例子,比如A.a(),A.b()是被代理的类和方法,那么当我调用A.a()时,此时a被代理了,成功执行代理类的内容,但还没有完,a()方法中调用了自身的方法b(),此时我们以为b也会被代理类代理,但实际上并没有,因为他是自身方法调用了并不是通过代理类A调用的,如果通过A.b()这种调用方式,那么b是可以被成功代理的。
分析:
1.我们的Controller类,使用注解@RestController,在applicationContext.xml中不扫描Controller注解的类,在applicationContext-mvc扫描Controller注解的类,结合web.xml可知我们的Controller AuthController被子容器管理。
2.我们的切面使用的注解是@Aspect和@Component, 结合 applicationContext.xml与web.xml可知,切面实例被父容器管理。
3.解决 因为我们切面类中使用的@Component注解会被Spring父容器管理,不适用注解,在application-mvc.xml中使用xml方法方式实例化切面类。这样Controller和切面类在同一个容器中管理,就可以正常的进行aop代理。
错误的尝试:
1.Controller使用@Component注解,想让其鬼父容器管理这样Controller和切面就归同一个容器管理了:这时候通过postman访问接口请求失败。
2.切面类,使用@Controller 还是没有进入切面类。看到配置中不但把Controller注解还有Configuration注解都归子容器管理,把切面类的注解改为@Configuration同样还是没有进入切面。
总结:
1.分析的问题的时候,注意web.xml的重要性,好多问题都可以从web.xml 这个配置文件开始往后面推。
2.深刻的理解一下父子容器的关系。哪些注解的类初始化后被父容器管理,哪些被子容器管理。
3. 动态代理 JDK与CGLIB的区别。我这个项目中就有一个被final修饰的Controller。导致在设置代理方式的出错。在不同的配置文件中设置代理方式应该是对加载这个配置的容器管理的类有影响,对别的容器不影响。
spring aop之父子容器的更多相关文章
- spring与springmvc父子容器
转载地址:http://www.tianshouzhi.com/api/tutorials/spring 1.spring和springmvc父子容器概念介绍 在spring和springmvc进行整 ...
- Spring和springMVC父子容器的关系
部分转载自:https://www.cnblogs.com/ljdblog/p/7461854.html springMVC容器和Spring容器 为什么一定要在web.xml中配置spring的li ...
- (转载)Spring与SpringMVC父子容器的关系与初始化
转自 https://blog.csdn.net/dhaiuda/article/details/80026354 Spring和SpringMVC的容器具有父子关系,Spring容器为父容器,Spr ...
- 1、spring与springmvc父子容器
转载于http://www.tianshouzhi.com/api/tutorials/spring 1.0 spring与springmvc父子容器 1.spring和springmvc父子容器概念 ...
- spring和springmvc父子容器关系
一般来说,我们在整合spring和SpringMVC这两个框架中,web.xml会这样写到: <!-- 加载spring容器 --> <!-- 初始化加载application.xm ...
- 面试高频题:说一说对Spring和SpringMvc父子容器的理解?
引言 以前写了几篇关于SpringBoot的文章<面试高频题:springBoot自动装配的原理你能说出来吗>.<保姆级教程,手把手教你实现一个SpringBoot的starter& ...
- Spring和SpringMVC父子容器关系初窥
一.背景 最近由于项目的包扫描出现了问题,在解决问题的过程中,偶然发现了Spring和SpringMVC是有父子容器关系的,而且正是因为这个才往往会出现包扫描的问题,我们在此来分析和理解Spring和 ...
- 转:spring的启动过程-spring和springMVC父子容器的原理
要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...
- Spring和springmvc父子容器注解扫描问题详解
一.Spring容器和springmvc容器的关系如下图所示: Spring和springmvc和作为两个独立的容器,会把扫描到的注解对象分别放到两个不同的容器中, Springmvc容器是spr ...
随机推荐
- jquery disabled选择器 语法
jquery disabled选择器 语法 作用:disabled 选择器选取所有禁用的表单元素.大理石平台价格表 语法:$(":disabled") jquery disable ...
- 文件操作:fseek()
int fseek(FILE *stream, long offset, int fromwhere); fseek 用于二进制方式打开的文件,移动文件读写指针位置. int fseek( FIL ...
- 'vue' 不是内部或外部命令,也不是可运行的程序 或批处理文件
解决方案:找到npm i xxx -g 下载后存放的路径,将路径添加到环境变量中,即可.1.npm config list 查看一下npm 的配置信息 2.打开路径看看里面的命令.window用户wi ...
- Compress Words
E. Compress Words 直接套 KMP 即可(那为什么打 cf 的时候没有想到...),求出后一个单词(word)的前缀数组,然后从前面已得的字符串的末尾 - word. length ( ...
- 客户端框架-MVP
MVP Model-View-Presenter MVP是把MVC中的Controller换成了Presenter(呈现),目的就是为了完全切断View跟Model之间的联系,由Presenter充当 ...
- 设置Google浏览器不缓存JS
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- qmake生成VS的vcproj/sln工程文件
qmake 生成的vs工程与环境变量中的 qmakespec相关,可以有两种方法: 1.默认情况下,即环境变量qmakespec为你装的qt for vs的版本,默认生成的为该版本的vs工程,如,你装 ...
- Python学习笔记:类
类可以将数据与函数封装起来,用一个例子解释,先定义一个类: class athlete: def __init__(self,a_name,a_dob=None,a_times=[]): self.n ...
- openssl生成秘钥对
openssl genrsa -out pri.pem openssl rsa -in pri.pem -out pub.pem -pubout 这样就生成秘钥对了,其中pri.pem是私钥,pub. ...
- ControlTemplate in WPF —— TreeView
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x ...