一起学Spring之AOP
概述
在软件开发中,我们重点关注的是业务逻辑代码,但在实际开发中,需要写的代码却不仅仅是业务逻辑,还需要处理记录日志,异常处理,事务控制等一些与业务无关的事情。而且这些代码也是服务端必须的,类似这样的代码分散在系统中的各个地方,如:几乎所有的重要操作方法前面都会加上日志记录代码,这样的代码写起来繁琐,又占用开发时间和精力,而且不容易维护。我们统一把这类代码成为【切面代码】,如何让我们从这些繁琐的工作中抽身而退,更加专注于业务逻辑,这就需要用到Spring的AOP技术。
AOP原理:将复杂的需求分解成不同的方面,将散落在系统中的公共功能集中解决,如下图所示:

通知(Advice)的分类
分类如下:
- 前置通知:在某个切入点之前执行的通知
- 后置通知:在某个切入点之后执行的通知
- 异常通知:在某个切入点出现异常时候的通知
- 环绕通知:包围某个切入点的通知,功能最强大
准备工作
AOP需要的jar包
除Spring必备的五个jar包外,还需要以下三个来支撑AOP:
- aopalliance-1.0.jar
- aspectjweaver-1.5.3.jar
- spring-aop-4.0.6.RELEASE.jar
定义一个接口和实现类
如下所示:
IStudentService接口 代码如下:
package com.hex.second; /**
* 学生服务接口
* @author Administrator
*
*/
public interface IStudentService { /**
* 新增学生
* @param student
*/
void addStudent(Student student);
/**
* 删除学生
* @param id
*/
void deleteStudent(int id); /**
* 修改学生
* @param id
*/
void updateStudent(int id);
}
StudentServiceImpl类 代码如下:
package com.hex.second; /**
* 学生服务事项类
* @author Administrator
*
*/
public class StudentServiceImpl implements IStudentService { /**
* 新增学生
*/
public void addStudent(Student student) {
// TODO Auto-generated method stub
System.out.println("新增加学生。。。");
} /**
* 删除学生
*/
@Override
public void deleteStudent(int id) {
// TODO Auto-generated method stub
System.out.println("删除学生。。。");
} /**
* 修改学生
*/
public void updateStudent(int id) {
// TODO Auto-generated method stub
System.out.println("修改学生");
int i=1/0;
}
}
前置通知
1. 实现接口
前置通知类,需要实现【MethodBeforeAdvice】接口中的before方法,如下所示:
Method method 表示执行的目标方法
Object[] args 表示传入的参数数组
Object target 表示目标对象,即切入点所示的对象
package com.hex.second;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class LogBefore implements MethodBeforeAdvice {
/***
* 前置通知
* method:表示调用的方法,即切入点
* args:表示调用方法的参数
* target:表示方法所在的目标对象
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("前置通知。。。");
System.out.println("method="+method+",args数量="+args.length+",target="+target);
}
}
2. 配置applicationContext.xml文件
如果要支持AOP,需要引入命名空间,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
3. 配置两个类对应的bean
<!-- 服务类 -->
<bean id="studentService" class="com.hex.second.StudentServiceImpl"></bean>
<!-- 前置通知类 -->
<bean id="logBefore" class="com.hex.second.LogBefore"></bean>
4. 配置AOP
通过AOP配置,将通知类和业务逻辑类进行关联,说明如下:
一个配置文件中,可以有多个<aop:config>配置,每一个aop:config中只能有一个aop:pointcut配置,如果有多个切入点需要配置expression,且切入点必须是全路径配置。如下所示:
<!-- 将addStudent和通知进行关联 -->
<aop:config>
<!-- 每一个config只有一个poingcut,如果有多个,则需要配置多个config -->
<!-- 配置切入点 id自定义,expression表示切入点的函数名-->
<aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))" id="pc"/>
<!-- 配置通知 -->
<aop:advisor advice-ref="logBefore" pointcut-ref="pc"/>
</aop:config>
后置通知
1. 实现接口
需要实现【AfterReturningAdvice】接口【afterReturning】方法中的 如下所示:
package com.hex.second; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /**
* 通过实现接口将普通类变成后置通知
* @author Administrator
*
*/
public class LogAfter implements AfterReturningAdvice { /**
* 后置通知实现类
* returnValue:返回值
* method:表示调用的方法,即切入点
* args:表示调用方法的参数
* target:表示方法所在的目标对象
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("后置通知。。。");
System.out.println("returnValue="+returnValue+",method="+method+",args数量="+args.length+",target="+target);
} }
2. 配置切入点和通知的Bean
<bean id="studentService" class="com.hex.second.StudentServiceImpl"></bean>
<bean id="logAfter" class="com.hex.second.LogAfter"></bean>
3. AOP配置
如果前置通知和后置通知为同一个切入点,则可以配置在一个aop:config节点中,如下所示:
多个切入点用or连接,多个通知就配置多个aop:advisor
<!-- 将addStudent和通知进行关联 -->
<aop:config>
<!-- 每一个config只有一个poingcut,如果有多个,则需要配置多个config -->
<!-- 配置切入点 id自定义,expression表示切入点的函数名-->
<aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.deleteStudent(int)) or execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))" id="pc"/>
<!-- 配置通知 -->
<aop:advisor advice-ref="logBefore" pointcut-ref="pc"/> <aop:advisor advice-ref="logAfter" pointcut-ref="pc"/>
</aop:config>
异常通知
1. 实现接口
异常通知是有异常发生时,才会触发的通知,需要实现【ThrowsAdvice】接口,且此接口没有需要实现的方法,但同时给出了约定:
必须以固定格式实现方法:public void afterThrowing([Method, args, target], ThrowableSubclass);
package com.hex.second; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; /**
* 异常通知
* @author Administrator
*
*/
public class LogException implements ThrowsAdvice { /**
* 异常通知执行
* @param method 切入点
* @param args 参数个数
* @param target 调用目标对象
* @param ex 异常
*/
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
System.out.println("异常通知。。。");
System.out.println("method="+method+",args数量="+args.length+",target="+target+",ex="+ex);
}
}
2. 配置Bean类
<!-- 服务类 -->
<bean id="studentService" class="com.hex.second.StudentServiceImpl"></bean>
<bean id="logException" class="com.hex.second.LogException"></bean>
3. 配置AOP
如下所示:参数只需要写参数类型即可,不需要写参数名称
<!-- 可以配置aop:config -->
<aop:config>
<aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.updateStudent(int))" id="pc1"/>
<!-- 配置通知 -->
<aop:advisor advice-ref="logException" pointcut-ref="pc1"/>
</aop:config>
环绕通知
1. 实现接口
环绕通知,需要实现【MethodInterceptor】接口并实现【invoke】方法,其中obj = invocation.proceed();表示调用目标方法,如果不写此句,则目标方法不会被调用。如下所示:
package com.hex.second; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; /**
* 环绕通知
* 环绕通知的本质上是一个拦截器
* @author Administrator
*
*/
public class LogAround implements MethodInterceptor { /**
*
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = null;
try { // 前置通知
System.out.println("环绕实现前置通知。。。");
System.out.println("环绕通知:target="+invocation.getThis()+",method="+invocation.getMethod().getName()+",args="+invocation.getArguments().length);
// 控制目标方法的执行 obj表示目标方法的返回值,表示执行addStudent(student)方法
//此方法控制目标方法的执行,如果不写此方法,则目标方法不会执行,此方法前的是前置通知,此方法后的是后置通知
obj = invocation.proceed();
// 后置通知
System.out.println("环绕实现后置通知。。。");
} catch (Exception e) {
// 异常通知
System.out.println("环绕实现异常通知。。。");
throw e;
}
// TODO Auto-generated method stub
return obj;
} }
2. 配置Bean
<!-- 服务类 -->
<bean id="studentService" class="com.hex.second.StudentServiceImpl"></bean>
<bean id="logAround" class="com.hex.second.LogAround"</bean>
3. 配置AOP
所有配置切入点通知的方式都是一样的。如下所示:
<aop:config>
<aop:pointcut expression="execution(public void com.hex.second.StudentServiceImpl.addStudent(com.hex.second.Student))" id="pc2"/>
<aop:advisor advice-ref="logAround" pointcut-ref="pc2"/>
</aop:config>
所有的调用方式是一致的,不需要调用通知类,系统会自动调用,如下所示:
package com.hex.second; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestMain { public static void main(String[] args) {
// TODO Auto-generated method stub
//通过Spring进行注入,Spring上下文对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
IStudentService studentService=(IStudentService)context.getBean("studentService");
Student student =new Student();
studentService.addStudent(student);
//studentService.deleteStudent(1);
//studentService.updateStudent(0);
} }
备注
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。
一起学Spring之AOP的更多相关文章
- Spring学习笔记(二)Spring基础AOP、IOC
Spring AOP 1. 代理模式 1.1. 静态代理 程序中经常需要为某些动作或事件作下记录,以便在事后检测或作为排错的依据,先看一个简单的例子: import java.util.logging ...
- 57. Spring 自定义properties升级篇【从零开始学Spring Boot】
之前在两篇文章中都有简单介绍或者提到过 自定义属性的用法: 25.Spring Boot使用自定义的properties[从零开始学Spring Boot] 51. spring boot属性文件之多 ...
- 学习spring1--跟我一起学Spring 3(2)–开发环境配置
http://www.importnew.com/13185.html#spring 首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 我要投稿 更多频道 » - 导航条 - 首页 所 ...
- 17、Spring Boot普通类调用bean【从零开始学Spring Boot】
转载:http://blog.csdn.net/linxingliang/article/details/52013017 我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个 ...
- 21. Spring Boot过滤器、监听器【从零开始学Spring Boot】
转载:http://blog.csdn.net/linxingliang/article/details/52069490 上一篇文章已经对定义Servlet 的方法进行了说明,过滤器(Filter) ...
- 81. Spring Boot集成JSP疑问【从零开始学Spring Boot】
[原创文章,转载请注明出处] 针对文章: ()Spring Boot 添加JSP支持[从零开始学Spring Boot] 有网友提了这么一些疑问: 1.Spring Boot使用jsp时,仍旧可以打成 ...
- 78. Spring Boot完美使用FastJson解析JSON数据【从零开始学Spring Boot】
[原创文章,转载请注明出处] 个人使用比较习惯的json框架是fastjson,所以spring boot默认的json使用起来就很陌生了,所以很自然我就想我能不能使用fastjson进行json解析 ...
- 77. Spring Boot Use Thymeleaf 3【从零开始学Spring Boot】
[原创文章,转载请注明出处] Spring Boot默认选择的Thymeleaf是2.0版本的,那么如果我们就想要使用3.0版本或者说指定版本呢,那么怎么操作呢?在这里要说明下 3.0的配置在spri ...
- 75. Spring Boot 定制URL匹配规则【从零开始学Spring Boot】
在之前有一篇文章说了,博客名称从原来的<从零开始学Spring Boot>更改为<Spring Boot常见异常汇总>,后来写了几篇文章之后发展,有些文章还是一些知识点,所以后 ...
随机推荐
- 学习AI之NLP后对预训练语言模型——心得体会总结
一.学习NLP背景介绍: 从2019年4月份开始跟着华为云ModelArts实战营同学们一起进行了6期关于图像深度学习的学习,初步了解了关于图像标注.图像分类.物体检测,图像都目标物体检测等 ...
- 区块链学习笔记:D04 区块链在各行业领域的应用(二)
这节课主要是政务领域.版权存证领域.能源领域的应用案例介绍 1.房屋租赁联盟链 特点:真实可信.透明补贴.便于追溯.公共监督 节点:房屋运营节点.房管局节点.社保局节点.财政局节点.教育部门节点(多节 ...
- su和sudo的区别与使用
一. 使用 su 命令临时切换用户身份 1.su 的适用条件和威力 su命令就是切换用户的工具,怎么理解呢?比如我们以普通用户beinan登录的,但要添加用户任务,执行useradd ,beina ...
- iOS面试的算法相关
转自:https://www.jianshu.com/p/c4820b159159 面试中遇到的这些算法,在平常工作中,基本不会用到. 不过现实的面试中经常喜欢问关于算法的问题 有些还要求写出代码.一 ...
- iOS开发UI篇—Quartz2D使用(图形上下文栈
转自:http://www.cnblogs.com/wendingding/p/3782489.html 一.qurza2d是怎么将绘图信息和绘图的属性绘制到图形上下文中去的? 说明: 新建一个项目, ...
- MySQL InnoDB 存储引擎原理浅析
注:本文主要基于MySQL 5.6以后版本编写,多数知识来着书籍<MySQL技术内幕++InnoDB存储引擎>,本文章仅记录个人认为比较重要的部分,有兴趣的可以花点时间读原书. 一.MyS ...
- Selenium之编辑框操作
编辑框操作: 网页上随处可见的编辑框,有时候编辑框里有默认的提示文字或者当我们需要输入第二次测试数据时,须先用clear()方法清除该元素里的字符串,再输入文本: 那么如何获取输入框已经输入的文本内容 ...
- 【CuteJavaScript】ES2019 新特性汇总
最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~ 另外之前也整理了 <ES6 ...
- 【Vuejs】301- Vue 3.0前的 TypeScript 最佳入门实践
前言 我个人对更严格类型限制没有积极的看法,毕竟各类转类型的骚写法写习惯了. 然鹅最近的一个项目中,是 TypeScript+ Vue,毛计喇,学之...-真香! 1. 使用官方脚手架构建 npm i ...
- Java中真的只有值传递么?
Java中真的只有值传递么? (本文非引战或diss,只是说出自己的理解,欢迎摆正心态观看或探讨) 回顾值传递和引用传递 关于Java是值传递还是引用传递,网上有不一样的说法. 1.基本类型或基本类型 ...