需求;项目对外提供接口,要求每个对外接口都要进行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. C# SqlDataReader

    var reader = MSSQLHelper.ExecuteReader(sb.ToString(), out erro, ConnectionString); //SqlDataReader / ...

  2. c++11:lambda表达式的使用

    lambda表达式的一般形式: [capture list] (parameter list) -> return type{function body}; 其中,capture list (捕 ...

  3. VS2015安装QT插件

    下载安装完后直接重新启动vs

  4. Linux iptables 防火墙设置

    1.查看防火墙iptables -L -niptablesb -L -n --line-number  显示规则行号看到 INPUT ACCEPT, FORWARD ACCEPT , OUTPUT A ...

  5. websocket协议解决消息发送问题 Could not decode a text frame as UTF-8.

    在使用websocket 时出现了Could not decode a text frame as UTF-8 首次连接成功当发送消息时,出现了编码作错误 第一请求页面时,连接成功 当发送消息时,不加 ...

  6. 使用Hive-JDBC遇到的一些问题解决

    使用Hive-JDBC遇到的一些问题解决(转) 标签(空格分隔): Hive 1,java.lang.NoClassDefFoundError: org/apache/hive/service/cli ...

  7. linux下面实时查看进程,内存以及cpu使用情况使用命令

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器 可以直接使用top命令查看整体情况,如图: 但是这样虽然看的东西多,但是闲的比较 ...

  8. 分布式-信息方式-ActiveMQ支持的传输协议和配置

                             ActiveMQ支持的传输协议和配置■ Connector: ActiveMQ提供的,用来实现连接通讯的功能.包括: client-to-broker ...

  9. Java并发编程的艺术笔记(六)——HashMap、ConcurentHashMap的原理与实现

    一.线程不安全的HashMap 多线程环境下,使用HashMap进行put操作会引起死循环(jdk1.7 Entry链表形成环形数据结构),导致CPU利用率接近100%. 结构:数组 table[]+ ...

  10. 关于Java 8 forEach

    1. forEach and Map 1.1 通常这样遍历一个Map Map<String, Integer> items = new HashMap<>(); items.p ...