概述:

  AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。AOP是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。

  AOP就是将公用功能提取出来,如果以后公用功能的需求发生变化,只需要改动公用的模块的代码即可,多个调用的地方则不需要改动。所谓面向切面,就是只关注通用功能,而不关注业务逻辑。实现方式一般是通过拦截。一般应用在日志记录,事务管理,权限验证,性能监测(统计方法运行时间),异常拦截等。

  1,Aspect(切面):通常是一个类,里面可以定义切入点和通知
  2,JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
  3,Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
  4,Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
  5,AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类,原理我们后面有时间再写一篇博客。AOP动态代理的实现

我们先通过例子来了解一下基于xml配置的AOP:

简单的实现转账功能,刘德华给张学友转账10000元:

首先我们把pom.xml中需要包引进去(我是通过父工程管理的jar包版本,所以没有写版本信息):

<dependencies>
<!--spring需要的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>

我们的dao和service代码:

public class AccountDao {
/**
* 转入方法
*/
public void shiftto(){
System.out.println("张学友的账户转入了 10000...");
} /**
* 转出方法
*/
public void rollout(){
System.out.println("刘德华的账户转出了 10000...");
}
} public class AccountService { private AccountDao accountDao; /**
* 基于xml配置 提供一个set方法
*/
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
} /**
* 转账方法
*/
public String transfer() {
//转出
accountDao.rollout();
//转入
accountDao.shiftto();
return "我是AccountService类的transfer方法的返回值";
}
}

然后配置spring配置文件中对应的bean:

    <!--目标类-->
<bean id="accountDao" class="com.zy.dao.AccountDao"></bean>
<bean id="accountService" class="com.zy.service.AccountService">
<property name="accountDao" ref="accountDao"></property>
</bean>

如果只有上面这些的话(不使用AOP的情况),我们可以通过测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountServiceTest {
@Value("#{accountService}")
private AccountService service; @Test
public void transfer() throws Exception {
service.transfer();
} }

得出的结果如下:

那么如果现在我们要把转账的功能放在事务里的话,正常情况下需要在实现的service中加入事务的代码,但是使用AOP的话我们可以这样做:

新建自己的Aspect类:

public class MyAspect {
/**
* before 前置通知方法
*/
public void before(JoinPoint joinPoint) {
System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");
} /**
* afterReturning 后置通知方法
*/
public void afterReturning(JoinPoint joinPoint, Object returnVal) {
System.out.println("---------------提交事务,返回值为:" + returnVal);
} /**
* after 最终通知方法
*/
public void after(JoinPoint joinPoint) {
System.out.println("---------------释放资源");
} /**
* afterThrowing 后置异常通知方法
*/
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage());
}
}

这个时候 我们需要在spring配置文件applicationContext.xml中这样配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
">
<!--目标类-->
<bean id="accountDao" class="com.zy.dao.AccountDao"></bean>
<bean id="accountService" class="com.zy.service.AccountService">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--切面类:里面可以有多个增强目标类的方法-->
<bean id="myAspect" class="com.zy.aspect.MyAspect"></bean> <!--配置目标类和切面类的关系-->
<aop:config proxy-target-class="false">
<!--配置切面:指定增强方法的位置-->
<aop:aspect ref="myAspect">
<!--配置切入点-->
<aop:pointcut id="myPointcut" expression="bean(*Service)"></aop:pointcut>
<!--配置切入点和增强方法的联系-->
<!--before 前置通知-->
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
<!--afterReturning 后置通知 可能有返回值 returnVal要和方法的形参名称一致-->
<aop:after-returning method="afterReturning" pointcut-ref="myPointcut"
returning="returnVal"></aop:after-returning>
<!--after 最终通知-->
<aop:after method="after" pointcut-ref="myPointcut"></aop:after>
<!--后置异常通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="ex"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>

上面的配置的切入点语法需要解释一下:

Spring 只支持AspectJ的部分切入点语法 

4.3.1 语法一: execution(修饰符? 返回值 方法名(参数) 异常?)
execution(* *(..)) 匹配所有spring管理对象所有方法, 第一个*任意返回值 ,第二个*任意方法, .. 任意参数
execution(* com.zy.service.*..*(..)) 匹配com.zy.service包中所有对象所有方法
execution(* com.zy.service.AccountService.s*(..)) 匹配com.zy.service.AccountService中s开头方法 4.3.2 语法二:bean(beanName) 匹配目标Bean所有方法
bean(*Service) 匹配所有以Service结尾BeanName 的对象 4.3.3 语法三:within(包.*) 匹配包下所有类的所有方法
within(com.zy.service..*) 匹配spring包及其子包中类所有方法 注意: 一个.代表子目录; 两个点.. 表示后代目录

最后再运行我们的测试代码会得出以下结果:

这样我们就把事务加进去了,这样做最大的好处就是可以随时把事务去掉或者是修改其实现代码。

但是如果感觉这样在xml文件中配置比较麻烦的话,这里讲述一个稍微简单的方式,就是我们文章开始标红的通知类型 around(环绕通知):

修改MyAspect,加入around方法:

    /**
* around 环绕通知
*/
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
Object returnVal = null;
try {
//执行before
System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");
//执行具体业务
returnVal = joinPoint.proceed();
//执行afterReturning
System.out.println("---------------提交事务,返回值为:" + returnVal);
} catch (Exception ex) {
//执行afterThrowing
System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage()); } finally {
//执行after
System.out.println("---------------释放资源");
}
}

修改xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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
">
<!--目标类-->
<bean id="accountDao" class="com.zy.dao.AccountDao"></bean>
<bean id="accountService" class="com.zy.service.AccountService">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--切面类:里面可以有多个增强目标类的方法-->
<bean id="myAspect" class="com.zy.aspect.MyAspect"></bean> <!--配置目标类和切面类的关系-->
<aop:config proxy-target-class="false">
<!--配置切面:指定增强方法的位置-->
<aop:aspect ref="myAspect">
<!--配置切入点-->
<aop:pointcut id="myPointcut" expression="bean(*Service)"></aop:pointcut>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="myPointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>

可以看出 一行around通知的配置代替了之前的 before afterReturning afterThrowing after,但是效果是一样的。

下面我们来说一下基于注解的方式:

首先我们的spring配置文件改写成如下这样:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--context 开启注解扫描-->
<context:component-scan base-package="com.zy"></context:component-scan>
<!--aop 开启自动代理-->
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
</beans>

给我们的dao和service添加注解:

@Repository("accountDao")
public class AccountDao {
/**
* 转入方法
*/
public void shiftto(){
System.out.println("张学友的账户转入了 10000...");
} /**
* 转出方法
*/
public void rollout(){
System.out.println("刘德华的账户转出了 10000...");
}
} @Service("accountService")
public class AccountService {
@Value("#{accountDao}")
private AccountDao accountDao; /**
* 转账方法
*/
public String transfer() {
//转出
accountDao.rollout();
//转入
accountDao.shiftto();
return "我是AccountService类的transfer方法的返回值";
}
}

给我们的MyAspect类添加注解:

@Component("myAspect")
@Aspect //通知这个类是个切面
public class MyAspect { /**
* 提出来公用的切入点
*/
@Pointcut("bean(*Service)")
private void myPointcut() {
} /**
* before 前置通知方法
*/
@Before("myPointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("---------------由" + joinPoint.getTarget().getClass().getSimpleName() + "类开启事务");
} /**
* afterReturning 后置通知方法
*/
@AfterReturning(pointcut = "myPointcut()", returning = "returnVal")
public void afterReturning(JoinPoint joinPoint, Object returnVal) {
System.out.println("---------------提交事务,返回值为:" + returnVal);
} /**
* after 最终通知方法
*/
@After("myPointcut()")
public void after(JoinPoint joinPoint) {
System.out.println("---------------释放资源");
} /**
* afterThrowing 后置异常通知方法
*/
@AfterThrowing(pointcut = "myPointcut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("---------------事务回滚,程序异常信息:" + ex.getMessage());
}
}

运行测试代码得到如下结果:

到此 完全ojbk。

Spring总结六:AOP(面向切面编程)的更多相关文章

  1. Spring Boot2(六):使用Spring Boot整合AOP面向切面编程

    一.前言 众所周知,spring最核心的两个功能是aop和ioc,即面向切面和控制反转.本文会讲一讲SpringBoot如何使用AOP实现面向切面的过程原理. 二.何为aop ​ aop全称Aspec ...

  2. Spring(3):AOP面向切面编程

    一,AOP介绍 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开 ...

  3. Spring Boot之AOP面向切面编程-实战篇

    目录 前言 编程范式主要有以下几类 引入pom依赖 aop注解 实现日志分割功能 前言 AOP是一种与语言无关的程序思想.编程范式.项目业务逻辑中,将通用的模块以水平切割的方式进行分离统一处理,常用于 ...

  4. 【Spring 核心】AOP 面向切面编程

    一.什么是面向切面编程? 二.通过切点来选择连接点 三.使用注解创建切面 四.在XML中声明切面 五.注入AspectJ切面

  5. Spring注解式AOP面向切面编程.

    1.AOP指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式.aop底层是动态代理. package com.bie.config; import org.aspectj.lan ...

  6. Spring:AOP面向切面编程

    AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...

  7. 详细解读 Spring AOP 面向切面编程(二)

    本文是<详细解读 Spring AOP 面向切面编程(一)>的续集. 在上篇中,我们从写死代码,到使用代理:从编程式 Spring AOP 到声明式 Spring AOP.一切都朝着简单实 ...

  8. 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

    简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...

  9. Spring 08: AOP面向切面编程 + 手写AOP框架

    核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...

  10. Javascript aop(面向切面编程)之around(环绕)

    Aop又叫面向切面编程,其中“通知”是切面的具体实现,分为before(前置通知).after(后置通知).around(环绕通知),用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被 ...

随机推荐

  1. 8.var目录下的文件和目录详解

    1./var目录下的文件和目录详解. /var (该目录存放的是不断扩充且经常修改的目录,包括各种日志文件或者pid文件,存放linux的启动日志和正在运行的程序目录(变化的目录:一般是日志文件,ca ...

  2. python学习之aop装饰模式

    实际开发过程当中可能要对某些方法或者流程做出改进,添加监控,添加日志记录等所以我们要去改动已有的代码,自己的或者别人的,但改动后测试不周会引发不可控的异常,aop 模式解决了这类问题引发重复代码大量积 ...

  3. C++之内存管理

    内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存泄 ...

  4. 剑指offer-第六章面试中的各项能力(和为S的两个数字VS和为S的连续正序列)

    题目1:输入一个排序数组和一个值S,输出数组中两个数的和为S的任意一对数. 思路:分别用两个指针指向数组的头start和尾end.如果两个数字之和等于S输出.如果打于,则end--,再次相加.因此循环 ...

  5. SqlServer 数据表数据移动

    描述:将Test1表中的数据放到Test2表中 1.Test2表不存在 select A,B,C insert into Test2 from Test1 select * into Test2 fr ...

  6. LeetCode Maximum Swap

    原题链接在这里:https://leetcode.com/problems/maximum-swap/description/ 题目: Given a non-negative integer, yo ...

  7. Hbase 参数配置及优化

    From:http://www.open-open.com/lib/view/open1346684547787.html 接触hbase已有半年的时间,查了很多资料,也参考了很多别人心得,也希望把自 ...

  8. form表单中name和id区别

    HTML文本是由HTML命令组成的描述性文本,HTML命令可以说明文字.图形.动画.声音.表格.链接等.HTML的结构包括头部(Head).主体(Body)两大部分,其中头部描述浏览器所需的信息,而主 ...

  9. B/S与C/S的区别

    参考:http://www.cnblogs.com/groler/articles/2116905.html 一.概念 C/S结构:即Client/Server(客户机/服务器)结构,是大家熟知的软件 ...

  10. Dobbo问题及解决方案:forbid-consumer

    本地运行Dubbo经常出现以下情况: com.alibaba.dubbo.rpc.RpcException: Forbid consumer 10.0.53.69 access service com ...