一起学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常见异常汇总>,后来写了几篇文章之后发展,有些文章还是一些知识点,所以后 ...
随机推荐
- 第三章 学习Shader所需的数学基础(1)
1. 笛卡尔坐标系 在游戏中,我们使用的数学大部分都是为了计算位置.距离和角度等变量.而这些就算大部分是在笛卡尔坐标系下进行的. 1.1 二维笛卡尔坐标系 一个二维笛卡尔坐标系包含了两个部分的信息 1 ...
- 【HUAWEI Mate30】抽奖啦!华为IoT新福利上线!
华为云OceanConnect IoT云服务包括应用管理.设备管理.系统管理等能力,实现统一安全的网络接入.各种终端的灵活适配.海量数据的采集分析,从而实现新价值的创造. 华为云OceanConnec ...
- 移动开发在路上-- IOS移动开发系列 网络交互四(1)
最近一段时间上班忙的没日没夜的,不是披星戴月,就是头天早上出门,第二天早上回家...一直没出处时间来更新博客,码农之苦,说了都是泪,废话不多说,直接本主题,经过之前三篇的讲述,ios开发的东西大家或多 ...
- Codeforces Round #605 (Div. 3) E - Nearest Opposite Parity
题目链接:http://codeforces.com/contest/1272/problem/E 题意:给定n,给定n个数a[i],对每个数输出d[i]. 对于每个i,可以移动到i+a[i]和i-a ...
- 将项目部署到github的方法
GitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名GitHub. GitHub于2008年4月10日正式上线,除了Git代码仓库托管及基本的 We ...
- 【Taro】363- 玩转 Taro 跨端之 flex 布局篇
Taro 是一套遵循 React 语法规范的跨平台开发解决方案,但是目前当我们使用 Taro 的时候,在不同平台上的开发体验还有不一致的地方,所以我们也都期待有一套跨平台统一的解决方案,能够以最小差异 ...
- Spring-security-oAuth2分享
Spring-security-oAuth2分享 oAuth2简介 OAuth 2.0是用于授权的行业标准协议.OAuth 2.0致力于简化客户端开发人员的工作,同时为Web应用程序,桌面应用程序,移 ...
- Orleans 初接触
简介 这篇随笔主要记录了自己学习Orleans的经过和理解,在学习过程中会一直更新,思路和理解可能有些偏颇,如果有幸有大佬看到这篇文章,希望能给予批评指正. 导航 (一) 入门例子 (二) 测试用例 ...
- 使用@babel/preset-typescript取代awesome-typescript-loader和ts-loader
前言 之前写过一篇旧React项目升级使用TypeScript的文章:在React旧项目中安装并使用TypeScript的实践. 博客里使用awesome-typescript-loader对Type ...
- CSS预处理器之less和sass
CSS预处理器 1. 基于CSS的另一种语言 2. 通过工具编译成CSS 3. 添加了很多CSS不具备的特性 4. 能提升CSS文件的组织方式 ...