博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring学习(9)-AOP之使用aop:config标签
阅读量:3928 次
发布时间:2019-05-23

本文共 5961 字,大约阅读时间需要 19 分钟。

1 导包

最开始还是贴出用到了哪些包(相比上一篇博文多了一个aspectjweaver.jar,java切面织入包)

在这里插入图片描述
下载地址:

2 AspectJ介绍

AspectJ 是一个基于 Java 语言的 AOP 框架,它扩展了 Java 语言。Spring 2.0 以后,新增了对 AspectJ 方式的支持,新版本的 Spring 框架,建议使用 AspectJ 方式开发 AOP。

使用 AspectJ 开发 AOP 通常有两种方式:

  • 基于 XML 的声明式。(支持Bean)
  • 基于 Annotation 的声明式。(支持注解)

为什么要使用这个AspectJ呢?

Spring aop为我们提供了前置通知MethodBeforeAdvice后置通知AfterReturningAdvice环绕通知MethodInterceptor(需要aopalliance-1.0.jar)等通知接口,我们只需要在写的通知类中实现想要的通知接口,在xml中使用代理工厂ProxyFactoryBean,就可以简单实现切面的织入。

那么对一个POJO(简单java类,即不实现通知接口)中的方法可以放到代理工厂作为切面类吗,是不行的。
如果我们需要一个POJO转换成一个切面,可以使用spring提供的aop标签来实现,而AspectJ便是为了让这个POJO具备和继承了通知父类一样的能力。

3 使用<aop:config>标签

用一个具体例子来说明如何使用

项目结构

在这里插入图片描述

3.1 首先需要在xml头部引入aop标签

3.2 定义一个切面类

可以看到这个类并无继承任何父类或实现接口,只有单纯的方法

我在其中定义了常用的五种通知方法:前置、环绕、后置、最终、异常通知;
这些切面方法如果想要进一步对切入点进行控制,就需要使用到Aspectj来作为方法参数,比如前置通知方法有参数JoinPoint jp,环绕通知需要参数ProceedingJoinPoint pjp等,具体解释在代码中。

package com.cheng.aop.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import java.util.Arrays;//FirstAspect 切面类public class FirstAspect {
/** * 前置通知:数据校验,身份校验 * @param jp 连接点参数Joinpoint,必须放在方法的第一位,可对切入点进行分析 * 比如拿连接点的名字,连接点的所在类,连接点的参数 */ public void before(JoinPoint jp){
System.out.println("1...前置通知,切入类"+jp.getTarget().getClass()); System.out.println("1...前置通知,切入方法"+jp.getSignature().getName()); System.out.println("1...前置通知,参数:"+ Arrays.toString(jp.getArgs())); } /** * 环绕通知:事务控制,权限控制,返回对象 * @param pjp 对连接点的方法内容进行整体控制 * @return 返回连接点的返回值 * @throws Throwable 连接点有异常则抛出 */ public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("2...环绕前置, 在执行用辅助业务"); Object obj = pjp.proceed(); //调用核心业务 System.out.println("2...环绕返回对象:"+obj.toString()); System.out.println("2...环绕后置, 在执行用辅助业务"); return obj; } /** * 后置通知:日志记录,返回对象 * @param jp 连接点的基本信息 * @param result 获取连接点的返回对象 */ public void afterReturning(JoinPoint jp,Object result){
System.out.println("3...后置通知,切入类"+jp.getTarget().getClass()); System.out.println("3...后置通知,切入方法"+jp.getSignature().getName()); System.out.println("3...后置通知,返回对象为:"+result); } /** * 最终通知:在方法最后执行 * @param jp 连接点的基本信息 */ public void after(JoinPoint jp){
System.out.println("4...最终通知,切入类"+jp.getTarget().getClass()); System.out.println("4...最终通知,切入方法"+jp.getSignature().getName()); } /** * 异常通知:异常处理,事务控制 * 在目标方法抛出异常时执行的通知 * @param jp 连接点的基本信息 * @param e 连接点的异常信息 */ public void afterThrow(JoinPoint jp,Throwable e){
System.out.println("5...异常通知,切入类"+jp.getTarget().getClass()); System.out.println("5...异常通知,切入方法"+jp.getSignature().getName()); System.out.println(e.getMessage()); }}

3.3 服务接口和实现类

package com.cheng.aop.service;//用户服务接口public interface UserService {
//用户新增 public void addUser(String name); //用户修改 public String updateUser(); //用户删除 public String deleteUser(); //用户查询 public void queryUser();}
package com.cheng.aop.serviceImpl;import com.cheng.aop.service.UserService;public class UserServiceImpl implements UserService {
@Override public void addUser(String name) {
System.out.println("新增用户:"+name); } @Override public String updateUser() {
System.out.println("用户修改业务..."); return "已修改"; } @Override public String deleteUser() {
System.out.println("用户删除业务..."); return "已删除"; } @Override public void queryUser() {
System.out.println("用户查询业务..."); System.out.println(0/20);//抛出异常 }}

3.4 在xml中对切点和切面进行配置

  1. 目标对象(通知需要切入的类)和切面(通知类)注册bean
  2. 通过<aop:pointcut>配置切入点,使用expression中的execution()方法来实现对切入点的范围控制
  3. 通过<aop:aspect ref="切面id">来实现切面类中的切面方法需要织入到哪一个切入点中

最重要的地方就是第二步,使用expression中的execution()方法

这个方法是为了找到切面需要织入的那个地方,可以是单个/多个方法,可以是整个类的所有方法
它的具体写法要求如下:
execution(
     modifiers-pattern? —修饰符,比如public
     ret-type-pattern —标识方法的返回值
     declaring-type-pattern? —声明类型模式
     name-pattern/param-pattern —指定方法名/指定方法参数的路径
     throws-pattern? —抛出模式
     )
     
ret-type-pattern,name-pattern(param-pattern)是必须的.

  • ret-type-pattern:标识方法的返回值,需要使用全路径的类名如java.lang.String,也可以为*表示任何返回值;
  • name-pattern:指定方法名,*代表所有,例如set*,代表以set开头的所有方法.
  • param-pattern:指定方法参数(声明的类型):
    1. (..)代表所有参数
    2. (*)代表一个参数
    3. (*,String)代表第一个参数为任何值,第二个为String类型.

表达式例子如下:

  • 任意公共方法的执行:
        execution(public * *(..))
  • 任何一个以“set”开始的方法的执行:
        execution(* set*(..))
  • UserService接口的任意方法的执行:
        execution(* com.cheng.aop.service.UserService.*(..))
  • 定义在service包里的任意类和任意方法的执行:
        execution(* com.cheng.aop.service.*.*(..))
  • 定义在service包和所有子包里的任意类的任意方法的执行(比上面多了个点):
        execution(* com.cheng.aop.service..*.*(..))
  • 定义在pointcutexp包和所有子包里的UserService的的任意方法的执行:
        execution(* com.cheng.aop.service..UserService.*(..))

我想要的是:共5个切面方法,织入到4个切入点中,其中after和afterReturning通知织入到一个切入点中

3.5 测试结果

package com.cheng.aop.test;import com.cheng.aop.service.UserService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AOPTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService)context.getBean("userService"); userService.addUser("此成"); //userService.updateUser(); //userService.deleteUser(); //userService.queryUser(); }}

1.addUser(“此成”)方法执行,它被织入了before()前置通知方法

在这里插入图片描述
2.updateUser()方法执行,它被织入了around()环绕通知方法
在这里插入图片描述
3.deleteUser()方法执行,它被织入了afterReturning()后置通知方法和after()最终通知方法
在这里插入图片描述
4.queryUser()方法执行,它被织入了afterThrow()异常通知方法
在这里插入图片描述

4 总结

通过使用<aop:config>标签来实现对POJO的切面化和织入过程

通过<aop:pointcut>配置切入点,使用expression中的execution()方法来实现对切入点的范围控制
通过<aop:aspect ref="切面id">来实现切面类中的切面方法需要织入到哪一个切入点中


文毕,如有助,赞之,乐乎

转载地址:http://klign.baihongyu.com/

你可能感兴趣的文章
JAVA算法:递归求解母牛问题(JAVA代码)
查看>>
JAVA算法:欧几里德算法(GCD)又称辗转相除法计算两个整数a,b的最大公约数(JAVA代码)
查看>>
JAVA算法:李白遇花喝酒游戏JAVA DFS 算法设计
查看>>
JAVA算法:括号配对问题与卡塔兰数(Catalan number)
查看>>
JAVA算法:最长重复子串问题(JAVA解题)
查看>>
JAVA算法:最长重复子序列(JAVA)
查看>>
LeetCode刷题:20. Valid Parentheses
查看>>
LeetCode刷题:1003. Check If Word Is Valid After Substitutions
查看>>
LeetCode刷题:301. Remove Invalid Parentheses (DFS算法设计和BFS算法设计)
查看>>
LeetCode刷题:403. Frog Jump 青蛙过河问题JAVA算法设计
查看>>
JAVA算法:贪心算法典型题目详解 (JAVA版本 共6题)
查看>>
JAVA算法趣味题目求解
查看>>
JAVA算法:创建图算法(构建图)
查看>>
JAVA算法:合并石子堆——JAVA版本算法(动态规划算法)
查看>>
JAVA算法:三角形周长(JAVA版本算法)
查看>>
JAVA算法:House Robber(JAVA版本解题)
查看>>
JAVA算法:House Robber I&II&III JAVA版本解题
查看>>
JAVA算法:矩阵旋转(JAVA代码)
查看>>
JAVA算法:回形矩阵输出
查看>>
JAVA算法:在给定的数组中查找出现次数最多的元素(JAVA版本)
查看>>