AOP概念

AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。AOP是OOP的一个强有力的补充。

AOP术语

AOP的术语不太直观,Spring文档中也没有给一个确切的定义,所以重在理解。

  • Join Point: Spring AOP中,join point就是一个方法。(通俗来讲就是起作用的那个方法)。

  • Pointcut: 用来指定join point(通俗来讲就是描述的一组符合某个条件的join point)。通常使用pointcut表达式来限定joint point,Spring默认使用AspectJ pointcut expression language。

  • Advice: 在join point上特定的时刻执行的操作,Advice有几种不同类型,下文将会讨论(通俗地来讲就是起作用的内容和时间点)。

  • Introduction:给对象增加方法或者属性。

  • Target object: Advice起作用的那个对象。

  • AOP proxy: 为实现AOP所生成的代理。在Spring中有两种方式生成代理:JDK代理和CGLIB代理。

  • Aspect: 组合了Pointcut与Advice,在Spring中有时候也称为Advisor。某些资料说Advisor是一种特殊的Aspect,其区别是Advisor只能包含一对pointcut和advice,但是aspect可以包含多对。AOP中的aspect可以类比于OOP中的class。

  • Weaving:将Advice织入join point的这个过程。

Advice的类型

  • Before advice:  执行在join point之前的advice,但是它不能阻止joint point的执行流程,除非抛出了一个异常(exception)。

  • After returning advice: 执行在join point这个方法返回之后的advice。

  • After throwing advice: 执行在join point抛出异常之后的advice。

  • After(finally) advice: 执行在join point返回之后或者抛出异常之后的advice,通常用来释放所使用的资源。

  • Around advice: 执行在join point这个方法执行之前与之后的advice。

实现机制

Spring AOP是基于代理机制的,通过JDK Proxy和CGLIB Proxy两种方法实现代理。

如果target object没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

如果target object实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。

Pointcut expression

Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合.

args()
@args()
execution()
this()
target()
@target()
within()
@within()
@annotation

其中execution 是用的最多的,其格式为:

ret-type-pattern,name pattern, 和 parameters pattern是必须的.

  • ret-type-pattern:可以为*表示任何返回值,全路径的类名等.
  • name-pattern:指定方法名,*代表所以,set*,代表以set开头的所有方法.
  • parameters pattern:指定方法参数(声明的类型),(..)代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个为String类型.

举例说明:

1) 任意公共方法的执行:

execution(public * *(..))

2) 任何一个以“set”开始的方法的执行:

execution(* set*(..))

3) AccountService 接口的任意方法的执行:

execution(* com.xyz.service.AccountService.*(..))

4) 定义在service包里的任意方法的执行:

execution(* com.xyz.service.*.*(..))

5) 定义在service包和所有子包里的任意类的任意方法的执行:

execution(* com.xyz.service..*.*(..))

6) 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:

execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))

7) pointcutexp包里的任意类.

within(com.test.spring.aop.pointcutexp.*)

8) pointcutexp包和所有子包里的任意类.

within(com.test.spring.aop.pointcutexp..*)

9) 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.

this(com.test.spring.aop.pointcutexp.Intf)

10) 带有@Transactional标注的所有类的任意方法.

@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)

11) 带有@Transactional标注的任意方法.

@annotation(org.springframework.transaction.annotation.Transactional)

12) 参数带有@Transactional标注的方法.

@args(org.springframework.transaction.annotation.Transactional)

13) 参数为String类型(运行是决定)的方法.

args(String)

完整示例:

首先新建一个maven项目,在项目的pom.xml中添加spring aop相关的依赖项:

如下是完整的pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.outofmemory</groupId>
<artifactId>spring-aop-aspect</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>spring-aop-aspect</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>3.1.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>

在maven中我们引入了spring aop相关的依赖,aspectj相关的包有两个,cglib是必须的。

下面我们在项目中新建一个Service类PersonService,它是业务代码,是我们AOP注入的目标类:

package cn.outofmemory.spring_aop_aspect;

import org.springframework.stereotype.Service;

@Service
public class PersonService { public void addPerson(String personName) {
System.out.println("add person " + personName);
} public boolean deletePerson(String personName) {
System.out.println("delete person " + personName) ;
return true;
} public void editPerson(String personName) {
System.out.println("edit person " + personName);
throw new RuntimeException("edit person throw exception");
} }

PersonService类中定义了三个方法,addPerson方法无返回值,而deletePerson方法有返回值,而editPerson方法抛出运行时的异常。

下面我们看Aspect类:

package cn.outofmemory.spring_aop_aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; @Component
@Aspect
public class SimpleAspect {
@Pointcut("execution(* cn.outofmemory.spring_aop_aspect.*Service*.*(..))")
public void pointCut() {
} @After("pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("after aspect executed");
} @Before("pointCut()")
public void before(JoinPoint joinPoint) {
//如果需要这里可以取出参数进行处理
//Object[] args = joinPoint.getArgs();
System.out.println("before aspect executing");
} @AfterReturning(pointcut = "pointCut()", returning = "returnVal")
public void afterReturning(JoinPoint joinPoint, Object returnVal) {
System.out.println("afterReturning executed, return result is "
+ returnVal);
} @Around("pointCut()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around start..");
try {
pjp.proceed();
} catch (Throwable ex) {
System.out.println("error in around");
throw ex;
}
System.out.println("around end");
} @AfterThrowing(pointcut = "pointCut()", throwing = "error")
public void afterThrowing(JoinPoint jp, Throwable error) {
System.out.println("error:" + error);
}
}

SimpleAspect类中的第一个方法是pointCut()方法,此方法无任何执行内容,只有一个@Pointcut的注解,其他方法都会引用这个注解中指定的pointcut表达式。

其他几个方法是各种Advice类型的方法实现,在这些方法中都可以通过JoinPoint实例来获得方法执行的上下文信息,参数信息。需要注意AfterReturning和AfterThrowing,After三种不同Advice的执行顺序。

有了spring框架和aspectj以及cglib的支持,我们只需要实现上面两个类就可以使用spring aop的功能了,下面我们看下如何在spring的配置文件中配置spring 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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.1.xsd">
    <aop:aspectj-autoproxy />
    <context:component-scan base-package="cn.outofmemory" /> 
</beans>

注意在beans节点中需要指定aop命名空间,一家chemaLocation地址,启用aop只需要添加<aop:aspectj-autoproxy/>就可以了。<context:component-scan/>节点用来指定自动扫描bean的命名空间

最后我们要测试下aop的效果,需要新建一个App类作为程序的入口类。

package cn.outofmemory.spring_aop_aspect;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    ApplicationContext appContext = new ClassPathXmlApplicationContext("/appContext.xml");
    PersonService personService = appContext.getBean(PersonService.class);
    String personName = "Jim";
    personService.addPerson(personName);
    personService.deletePerson(personName);
    personService.editPerson(personName);
    ((ClassPathXmlApplicationContext)appContext).close();
    }
}

这个类中我们初始化了ApplicationContext,然后从中得到PersonService的实例,并执行其addPerson,deletePerson,editPerson方法,最后关闭ApplicationContext。

其执行结果如下:

before aspect executing
around start..
add person Jim
after aspect executed
around end
afterReturning executed, return result is null
-------------------------------------
before aspect executing
around start..
delete person Jim
after aspect executed
around end
afterReturning executed, return result is null
----------------------------------------
before aspect executing
around start..
edit person Jim
after aspect executed
error in around
error:java.lang.RuntimeException: edit person throw exception
Exception in thread ...

从上面执行结果可以看到我们要执行的Before,around,after以及around,afterReturning,afterThrowing都正常的执行了。

AOP 配置详解

http://blog.csdn.net/lipei1220/article/details/52152430

Spring AOP 完成日志记录

http://hotstrong.iteye.com/blog/1330046

http://blog.csdn.net/baidu_25958185/article/details/43764467

http://www.oschina.net/code/snippet_1159320_49213

Spring Aop自定义注解拦截Controller实现日志管理

http://blog.csdn.net/itnik/article/details/38542373

Spring 3 AOP 概念及完整示例的更多相关文章

  1. spring之aop概念和配置

    面向切面的一些概念: 简单说: 连接点就一些方法,在这些方法基础上需要额外的一些业务需求处理. 切入点就是方法所代表的功能点组合起来的功能需求. 通知就是那些额外的操作. 织入就是使用代理实现整个切入 ...

  2. Spring框架学习-Spring的AOP概念详解

    一.SpringAOP的概述. AOP(Aspect Oriented Programming),面向切面编程,通过预编译方式和运行期间动态代理实现程序的功能的统一维护的技术.AOP是OOP(面向对象 ...

  3. Spring AspectJ AOP 完整示例

    http://outofmemory.cn/java/spring/AOP/aop-aspectj-example-before-after-AfterReturning-afterThrowing- ...

  4. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  5. Spring第二天——IOC注解操作与AOP概念

    大致内容 spring的bean管理(注解实现) AOP原理 log4j介绍 spring整合web项目的演示 一.spring注解实现bean管理 注解: 代码中一些特殊的标记,使用注解也可以完成一 ...

  6. spring Aop概念

    面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足.在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面.切面能对关注点进行模块化 ...

  7. Spring系列22:Spring AOP 概念与快速入门篇

    本文内容 Spring AOP含义和目标 AOP相关概念 声明式AOP快速入门 编程式创建代理对象 Spring AOP含义和目标 OOP: Object-oriented Programming 面 ...

  8. Spring使用 --- 基本概念(二):AOP,面向方面编程

    Table of Contents 什么是面向方面编程 怎样使用 什么时候使用 好处 本文讲述sprint的第二个基本概念: AOP,即面向方面编程 什么是面向方面编程 软件项目中,日志系统等服务系统 ...

  9. [Spring学习笔记 4 ] AOP 概念原理以及java动态代理

    一.Spring IoC容器补充(1) Spring IoC容器,DI(依赖注入): 注入的方式:设值方法注入setter(属性注入)/构造子注入(构造函数传入依赖的对象)/字段注入Field(注解) ...

随机推荐

  1. ubuntu下chromium浏览器flash插件安装

    ubuntu下chromium浏览器默认是不支持flash的,在新立德软件包中搜索flash得到的“Adobe Flash Player plugin installer”也没有什么卵用,因为装完以后 ...

  2. 201521123077 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 线程的同步(加锁防止多个线程同时访问) synchronized关键字修饰 可以使用于方法前或者方法内做同步 ...

  3. 201521123069 《Java程序设计》 第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. (1)我们可以利用堆栈追踪来获得异常发生的根源(堆栈追踪信息最顶层). (2)自定义异常:不是由Java系统 ...

  4. 快速搭建一个Fabric 1.0的环境

    之前笔者写了一篇Fabric1.0 Beta的部署和Fabric 1.0的多机部署.但是很多人在部署Fabric的时候还是很容易出问题,所以我就再把Fabric 1.0的单机环境搭建讲一下.其实很多内 ...

  5. IDEA 2 的注册码

    43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTczWVlKIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiI ...

  6. 关于XCode9的一些问题

    最近几天一直在做一项工作,为新项目在做搭建框架的前期准备,然后恰逢更新iOS11和XCode9,笔者心急的尝了个先,发现了一些问题,记录一下,如果有相同问题的程序猿,也可以参考一下. 目前问题遇到两点 ...

  7. BigDecimal的加减乘除及比较大小

    import java.math.BigDecimal; import static java.lang.System.out; public class BaseClass { public sta ...

  8. Java钉钉开发_01_开发前的准备

    源码已上传GitHub:传送门 一.准备事项 1.1  一个能在公网上访问的项目: 参见:Java微信开发_02_本地服务器映射外网 1.2  一个钉钉账号 去注册 1.3 创建一个应用 登录钉钉后台 ...

  9. 实例讲解webpack的基本使用第二篇

    这一篇来讲解一下如何设置webpack的配置文件webpack.config.js 我们新建一个webpack-demo的项目文件夹,然后安装webpack 执行如下命令 在项目文件夹下,建一个dis ...

  10. S2_SQL_第四章

    1.使用EXISTS语句判断该数据库对象是否存在的语法: DROP TABLE IF EXISTS temp; 2. EXISTS作为WHERE语句的子查询: SELECT <字段>FRO ...