APP下载

在AOP切面类中获取Session对象的实现方法

2023-05-08罗瑞明

电脑知识与技术 2023年9期
关键词:通知

罗瑞明

关键词:Spring;面向切面编程;Request;Session;切面;通知;目标对象

0 引言

AOP(Aspect Oriented Programming) 是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性[1]。

本文讨论的是在Spring框架下,编写AOP切面程序时,如何访问与具体业务无关的客户端浏览器请求和会话的问题。

1 正文

1.1 问题描述

举一个简单实例,比如在一个程序应用中,多处需要判断用户是否已经登录,这种与具体业务无关的逻辑或责任可以单独封装起来并被多个业务模块共同调用。通过Spring AOP面向切片编程技术来实现,可以降低业务逻辑之间的耦合性,提高程序的可重用性和开发效率。

1.2 分析设计

通常用户在应用程序的登录模块中完成登录后,其登录状态需要在执行其他应用程序模块时一直被检查。这种信息通常被存储在Session对象中,并在Web页面进行跳转的过程中,可以保存数据[2]。因此,在AOP切面类中需要放置与具体业务无直接耦合关系的代码,以检查用户是否登录,并在业务模块运行中切入执行,从而实现完整的业务逻辑。

如何在切面类中获取存储用户登录信息的Ses⁃sion对象是实现该AOP应用的关键问题。本文将以此作为例子来探讨解决这个问题的几种思路和方法,并供读者进行研究和交流。图1展示了本示例应用的界面:

该应用包含两个主要的业务模块:第一个是用户登录模块,用户需要输入用户名和密码才能登录;另一个是用户查询模块,可以根据用户ID查询用户信息。用户查询模块只有在成功登录后才能进行,否则将被禁止查询。用户查询模块的界面参见图2。

应用主要由以下组件组成:

1) POJO实体类Customer。

2) 基于Customer 实体类的DAO 接口和Mapping映射文件。

3) 控制器CustomerController类,主要包含用户登录和根据用户ID查询用户的方法。

4) Service接口和实现类,主要包含根据ID查找用户和根据用户名及密码查找用户的业务逻辑方法。

5) 一个切面(Aspect) 类,包含用于检查用户是否登录的通知(Advice) 方法。

6) 目标(Target) 对象可以是Controller类或者Ser⁃vice 类。根据目标对象的实际情况,对应的连接点(JoinPoint) 和切入点(Pointcut) 也不同。

1.3 方法实现

实现Spring AOP有多种方式,其中两种主要方式分别是通过Spring API和AspectJ框架实现AOP。在AspectJ框架中,可以采用基于XML和基于注解(Anno⁃tation) 两种配置方式。使用注解方式的配置,无须配置文件,只需要通过添加“注释代码”来完成,简化了Spring的开发,容易对方法进行拦截[3]。本文中的AOP实现以基于AspectJ框架的注解配置方式为例,使用Spring MVC作为控制层框架,MyBatis作为持久层框架,讨论如何通过面向切面编程,在各业务模块中实现自动检查用户是否已登录。同时,提出了几种在切面类中获取Session对象的方法。

1) 通过目标方法所带参数获取Session对象

以下为CustomerController类的主要代码,其用户登录业务方法login()会通过Service的findCustomerBy⁃NamePwd()方法来查询用户名和密码,如果查询到用户信息则将其存入Session对象中,否則会返回异常页面。另一个方法findCustomerById()则是按照用户编号查询用户信息的业务方法。其代码如下所示:

根据前面的分析,用户查询模块必须先要求用户登录以便进行查询操作。因此,需要通过AOP切面类获取Session对象的信息来判断用户是否已经登录。切面类的通知(Advice) 方法可以利用JoinPoint或Pro⁃ceedingJoinPoint类型的参数获取目标方法的参数信息,因此,可以设计目标方法增加Session类型的参数,使切面类的通知(Advice) 方法获得Session对象,进而完成登录判断。

Spring MVC 前端控制器(DispatcherServlet) 可以拦截和处理HTTP请求,并将请求发送给指定的Con⁃troller方法,请求会将其负载的参数传递给该方法,并等待方法处理这些信息[4]。因此,在控制器中的find⁃CustomerById()方法中,可以添加一个Session参数来接收前端控制器转发的客户端会话请求。然后在切面类的通知方法中,就可以获取目标方法中的Session参数信息。

目标方法增加了一个类型为HttpSession的形参,用于供切面类的通知方法获取Session信息。尽管该形参在用户查询业务中并没有实际作用,但在切面类中却能发挥重要作用。

通知方法需要根据用户登录状态的判断来决定是否继续执行目标方法。因此,在编写切面类MyAs⁃pect时,应该采用环绕通知(around) 方法来实现。代码如下所示:

上述代码中,首先使用@Aspect注解定义了切面类,该类作为Spring 中的组件需添加@Component 注解。接着,使用@Pointcut注解来配置和定义切入点。然后,通过@Around注解定义环绕通知,并通过Pro⁃ceedingJoinPoint类型的参数调用getArgs()方法获取目标方法的参数对象列表,其中应该包括Session对象。最后,对参数类型进行判断,确定所需的Session对象,并通过Session对象中的loginUser属性值来判断用户是否已经登录。

2) 通过RequestContextHolder类获取Session对象在上一种方法中,切面类植入的是控制层Con⁃troller 类的方法,因为在控制层中获取Request,Re⁃sponse和Session是很方便的,所以切面类的通知方法可方便地通过目标方法的参数获取Session对象。但是如果切面类织入的是Service层的方法,那么获取session对象将更加困难。这是本例根据ID查询用户的Service方法:

Service 层无法直接获取Request 对象,Spring 的Web模块提供了一个工具类RequestContextHolder来处理这个问题。RequestContextHolder持有上下文中的Request容器,提供了两个ThreadLocal对象,用于保存当前线程下的Request。可以通过调用RequestCon⁃textHolder的静态方法getRequestAttributes()獲得Serv⁃letRequestAttributes 对象,然后得到Request 对象和Session对象。修改切面类代码如下所示:

3) 在切面类中直接注入Request、Response和Ses⁃sion对象

Spring 中AOP 代理由Spring 的IOC 容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其他bean实例作为目标,这种关系可由IOC容器的依赖注入提供[5]。既然Spring提供了工具类RequestContextHolder 来访问外部对象Request,那么在切面类中直接注入Request等对象来实现访问是否可行?经研究发现,Spring确实可以像注入普通bean一样注入Request等实例,通过代理的方式,在启动时注册Web 环境相关的依赖对象。上面的切面类代码修改后如下所示:

在上述代码中,切面类通过@Autowired注解,直接注入HttpSession,在环绕通知(Advice) 中即可直接使用,非常方便。

2 总结

在Spring框架下,进行面向切面的编程(AOP) 时,切面类有时候需要进行一些与具体业务无关的操作,例如读取用户登录信息、将用户信息写入日志、获取客户端IP等。为此,切面类需要使用Request、Session等对象。获取这些对象的方法可以有两种思路:通过目标方法获取或者通过Spring框架提供的API获取。

具体有如下几种方法:

1) 通过访问目标方法的参数来获取。这要求目标方法的参数列表中应该含有HttpSession 或HttpServletRequest类型的参数,通常目标方法应该在Controller控制层。

2) 通过Spring框架提供的工具类RequestContext⁃Holder来获取。

3) 在切面类中直接注入(本质上也是通过Spring框架提供的API获取)。

本文虽然以Spring MVC控制层框架为例,讲解如何使用AOP切面类访问Session对象,但对于使用其他MVC 控制层框架的开发也有一定参考价值。例如,Struts和Struts2等框架也可以通过目标方法参数或框架底层API获取Request和Session等对象。

猜你喜欢

通知
遗嘱中指定保险受益人的有效性及方式
生活处处有语文