需求;项目对外提供接口,要求每个对外接口都要进行token认证。
解决办法:写一个token认证的工具类,在每个需要认证的接口方法开始的地方,调用工具类中的token认证方法。
问题:因为要满足指定条件才可以进行下一步,在接口中调用token认证的时候,要返回结果进行判断,不止是一行代码可以解决问题。
优化:使用aop 切面编程,在执行接口的内容之前,进行认证。通过注解的方式,织入到切点中。这样在需要进行认证的接口上只需要添加一个注解就可以token认证功能。
问题:基于Aspect注解实现该功能,在程序启动时,访问接口,直接进入接口,没有进入切面。
 
AuthController 
@RestController
public class AuthController { @PostMapping("authentication")
@TokenChecker
public Result getToken(String username,String password){
return null;
}
}
切面类:execution 表达式没有任何问题。
@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");
} }
 
wen.xml配置文件,关键的两个配置
<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 中的类型继承)。

  典型的父子容器: spring 和 springmvc 同时使用的时候。ContextLoaderListener 创建的容器是父容器,DispatcherServlet 创建的容器是子容器。保证一个 JVM 中,只有一个树状结构的容器树。可以通过子容器访问父容器资源。
 
常见场景:

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之父子容器的更多相关文章

  1. spring与springmvc父子容器

    转载地址:http://www.tianshouzhi.com/api/tutorials/spring 1.spring和springmvc父子容器概念介绍 在spring和springmvc进行整 ...

  2. Spring和springMVC父子容器的关系

    部分转载自:https://www.cnblogs.com/ljdblog/p/7461854.html springMVC容器和Spring容器 为什么一定要在web.xml中配置spring的li ...

  3. (转载)Spring与SpringMVC父子容器的关系与初始化

    转自 https://blog.csdn.net/dhaiuda/article/details/80026354 Spring和SpringMVC的容器具有父子关系,Spring容器为父容器,Spr ...

  4. 1、spring与springmvc父子容器

    转载于http://www.tianshouzhi.com/api/tutorials/spring 1.0 spring与springmvc父子容器 1.spring和springmvc父子容器概念 ...

  5. spring和springmvc父子容器关系

    一般来说,我们在整合spring和SpringMVC这两个框架中,web.xml会这样写到: <!-- 加载spring容器 --> <!-- 初始化加载application.xm ...

  6. 面试高频题:说一说对Spring和SpringMvc父子容器的理解?

    引言 以前写了几篇关于SpringBoot的文章<面试高频题:springBoot自动装配的原理你能说出来吗>.<保姆级教程,手把手教你实现一个SpringBoot的starter& ...

  7. Spring和SpringMVC父子容器关系初窥

    一.背景 最近由于项目的包扫描出现了问题,在解决问题的过程中,偶然发现了Spring和SpringMVC是有父子容器关系的,而且正是因为这个才往往会出现包扫描的问题,我们在此来分析和理解Spring和 ...

  8. 转:spring的启动过程-spring和springMVC父子容器的原理

    要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...

  9. Spring和springmvc父子容器注解扫描问题详解

      一.Spring容器和springmvc容器的关系如下图所示: Spring和springmvc和作为两个独立的容器,会把扫描到的注解对象分别放到两个不同的容器中, Springmvc容器是spr ...

随机推荐

  1. maven项目创建4

    运行maven项目,首先要不最根项目添加到maven本地仓库,执行  项目-->右键-->Run as-->Maven install 注:创建war包项目,本地测试,创建index ...

  2. hdu 4451 Dressing 衣服裤子鞋 简单容斥

    Dressing Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  3. CodeForces 349B--Color the Fence(贪心)

    B. Color the Fence time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  4. Xcode 4.1实用小工具:模拟网络连接和带宽

    暂无评论 适用于Mac OS X Lion的开发套件Xcode 4.1中,有个新鲜的小工具叫做Network Link Conditioner(网络连接调节器),是一款具有高度可定制性的辅助工具,让用 ...

  5. legend3---lavarel常用操作代码

    legend3---lavarel常用操作代码 一.总结 一句话总结: 要自己总结一下常用代码,这样才方便,也才有收获 1.路由示例:Route::get('/login','Home\Login\L ...

  6. 后盾网lavarel视频项目---lavarel多表关联一对多操作实例

    后盾网lavarel视频项目---lavarel多表关联一对多操作实例 一.总结 一句话总结: 1.一对多中多那个部分的数据前端通过json弄到服务器 2.所有通过一操作多的时候,都要用上模型中定义的 ...

  7. oracle数据库连接数反推公式

    sessions=1.1*processes+5,transactions=1.1*sessions.

  8. PHP CI 框架初识(一)

    CodeIgniter 是一个简单快速的PHP MVC框架.EllisLab 的工作人员发布了 CodeIgniter.CodeIgniter 是一套小巧但功能强大的.给 PHP 网站开发者使用的 W ...

  9. 【转】C++友元

    转自:https://www.cnblogs.com/BeyondAnyTime/archive/2012/06/04/2535305.html 1.友元函数的简单介绍 1.1为什么要使用友元函数 在 ...

  10. python接口测试之mock(一)

    在现在的软件开发过程中,特别是app的部分,需要的很多数据以及内容,都是来自server端的API,但是不能保证在客户端开发的时候,api在 server端已经开发完成,专门等着前端来调用,理想的情况 ...