第3章--AOP技术

Spring框架 - AOP概述 笔记https://my.oschina.net/hava/blog/758873
Spring框架 - AOP使用 笔记https://my.oschina.net/hava/blog/758881

AOP概述

在第1章中讲到AOP的一个例子:calculator中使用logger时将业务逻辑分离

public class Calculator {
public int add(int a, int b) {
return a + b;
}
} public class Calculator {
private static final Logger logger = Logger.getLogger(Calculator.class); public int add(int a, int b) {
logger.info("Enter into Calculator.add(a,b) method"); int result = 0;
try {
result = a + b;
} catch (Exception ex {
logger.info("Exception in Calculator.add(a,b) method");
}
logger.info("Leaving Calculator.add(a,b) method"); return result;
}
}

缺点:

代码重复

耦合业务逻辑(计算)与非业务逻辑(Logger)

AOP-->

public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int sub(int a, int b) {
return a - b;
}
} public class MyLogger {
private static final Logger logger = Logger.getLogger(MyLogger.class);
public void methodEnter (Method method) {
logger.info("Enter into " + method.getMethodInfo() + " method");
}
public void methodException (Method method) {
logger.info("Exception in " + method.getMethodInfo() + " method");
}
public void methodLeave (Method method) {
logger.info("Leaving " + method.getMethodInfo() + " method");
}
}

优点:

代码重用

解耦业务逻辑与非业务逻辑

AOP (面向切面)

Aspect: 切面(如MyLogger等日志、安全横切功能)

Join point: 函数执行/属性访问的过程(Spring中没有提供AOP的属性访问操作)

Advice: 定义了在某个特定函数执行点的切面功能 (如MyLogger打印日志的行为)

Advice类型:

Before: 函数执行之前

After returning: 函数正常返回之后

After throwing: 函数抛出异常之后

After finally: 函数返回之后

Around: 函数执行前后(入口和出口)

Pointcut: 匹配横切目标函数的表达式(哪些业务逻辑需要进行该AOP)

Spring AOP:

非完整AOP实现(如果想要更完整的AOP实现,可以使用AspectJ)

整合了AOP和IoC

通过XML schema-based AOP供使用(Spring本身提供的,通过XML声明的方式)

通过@AspectJ annotation-based AOP供使用(整合了AspectJ中的某些功能)

AOP使用

@AspectJ AOP

添加aspectjweaver.jar包

pom.xml中:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.1.RELEASE</version>
</dependency> <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.1.RELEASE</version>
</dependency>

application-context.xml中

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <aop:aspectj-autoproxy /> </beans>

xmlns的作用跟package name的作用类似,用于区分两个相同类名(不同位置)的类

aop的属性tag比如aop:aspectj-autoproxy, 会通过spring-aop-2.0.xsd文件来验证属性配置是否正确

定义Aspect

application-context.xml中

<bean id="loggingAspect" class="com.netease.nanodegree.LoggingAspect"></bean>

新建类LoggingAspect.java中

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect { }

定义Pointcut

@Pointcut("execution(* com.netease.nanodegree.Calculator.*(..))")
private void arithmetic() { }

将该AOP匹配到Calculator类中的所有函数

表达式名称为arithmetic,后续使用可以直接通过该名称使用

Pointcut表达式:designator(modifiers? return-type declaring-type? name(para,) throws?)

designator: 如execution表示函数执行, within表示在某个包/类下运行的函数

modifiers: public/private(可选)

return-type: 返回类型,*表示匹配所有返回类型

declaring-type: 包名/类名(可选)

name: 函数名

param: 参数列表:()无参;(..)任意参数

throws: 抛出的异常类型(可选)

示例:

匹配所有public的函数--@Pointcut("execution(public * *(..))")  private void publicMethod()

匹配所有DAO模块中的public函数--@Pointcut("execution(public * com.netease.dao.*.*(..))")  private void publicDaoMethod()

匹配所有以save开头的函数--@Pointcut("execution(* save*(..))")  private void saveMethod()

匹配所有以save开头的public函数--@Pointcut("execution(publicMethod() && saveMethod())")  private void publicSaveMethod()

定义Advice

实例:在函数操作前执行打印日志的功能

// 在函数操作前执行打印日志的功能
@Before("com.netease.nanodegree.LoggingAspect.arithmetic()")
// 可以直接写pointcut表达式
// @Before("execution(* com.netease.nanodegree.Calculator.*(..))")
public void doLog() { }

同样,还有@AfterReturning/@AfterThrowing/@After等等

但是,doLog()函数如何得到函数的上下文信息呢?

public void doLog(JoinPoint jp) {
System.out.println(jp.getSignature() + ", " + jp.getArgs());
}

getSignature()返回的为函数签名

getArgs()返回的为无类型的Object对象

但是,@Around和其他类型有所不同,需要注入的为ProceedingJoinPoint(JoinPoint的子类)

@Around("com.netease.nanodegree.LoggingAspect.arithmetic()")
public void doLog(ProceedingJoinPoint pjp) {
System.out.println("start method: " + pjp.toString());
Object retVal = pjp.proceed();
System.out.println("stop method: " + pjp.toString());
return retVal;
}

要想得到调用函数的返回值

@AfterReturning (
pointcut="com.netease.nanodegree.LoggingAspect.arithmetic()",
returning="retVal")
public void doLog(Object retVal) {
// do something with retVal
}

要想得到调用函数抛出的异常

@AfterThrowing (
pointcut="com.netease.nanodegree.LoggingAspect.arithmetic()",
throwing="ex")
public void doLog(IllegalArgumentException ex) {
// do something with ex
}

要想得到目标函数的参数(有类型的对象)

@Before("com.netease.nanodegree.LoggingAspect.arithmetic() && args(a, ..)")
public void doLog(JoinPoint jp, int a) {
// do something with parameters
}

匹配第一个参数并命名为a,不管后面其他的参数

实例:

1. 新建Maven项目

2. 在pom.xml中添加依赖

org.springframework: spring-aop

org.aspectj: aspectjweaver

3. application-context.xml初始化如上

4. 新建Calculator.java类

public class Calculator {
public int add(int a, int b) {
return a + b;
} public int sub(int a, int b) {
return a - b;
}
}

5. 新建LoggingAspect.java

@Aspect

public class LoggingAspect { }

6. 在application-context.xml中定义关于LoggingAspect的bean

<bean id="loggingAspect" class="com.netease.nanodegree.LoggingAspect"></bean>

<bean id="calculator" class="com.netease.nanodegree.Calculator"></bean>

7. 在LoggingAspect.java中

@Before("execution(* com.netease.nanodegree.Calculator.*(..)")
private void arithmeticDoLog(Joinpoint jp) {
System.out.println(jp.toString());
}

8. 新建TestApp.java

public class TestApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); Calculator calculator = context.getBean("calculator", Calculator.class);
System.out.println(calculator.add(1, 1)); ((ConfigurableApplicationContext) context).close();
}

9. 运行

10. 功能扩展

得到函数参数列表等等

@Before("execution(* com.netease.nanodegree.Calculator.*(..)) && args(a, ..)")
private void arithmeticDoLog(JoinPoint jp, int a) {
System.out.println(a + ": " + jp.toString());
}

输出:

1: execution(int com.netease.nanodegree.Calculator.add(int,int))

5: execution(int com.netease.nanodegree.Calculator.sub(int,int))

Schema-based AOP:

application-context.xml的文件相同,但是不需要 <aop:aspectj-autoproxy />

Aspect的定义

<aop:config>
<aop:aspect id="loggingAspect" ref="loggingBean">
...
</aop:aspect>
</aop:config>

Pointcut的定义

<!-- 3种方法定义pointcut -->
<aop:config>
<aop:pointcut id="arithmetic" expression="execution(* com.netease.nanodegree.Calculator.*(..))"/>
</aop:config>
<aop:config>
<aop:pointcut id="arithmetic" expression="com.netease.nanodegree.LoggingAspect.arithmetic()"/>
</aop:config>
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:pointcut id="arithmetic" expression="execution(* com.netease.nanodegree.Calculator.*(..))"/>
</aop:aspect>
</aop:config>

Advice的定义

<!-- 定义各种Advice -->
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:before pointcut-ref="arithmetic" method="doLog"/>
</aop:aspect>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:before pointcut="execution(* com.netease.nanodegree.Calculator.*(..))" method="doLog"/>
</aop:aspect>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:after-returning pointcut-ref="arithmetic" returning="retVal" method="doLog"/>
</aop:aspect>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:after-throwing pointcut-ref="arithmetic" throwing="ex" method="doLog"/>
</aop:aspect>
<aop:aspect id="loggingAspect" ref="loggingBean">
<aop:around pointcut-ref="arithmetic" method="doLog"/>
</aop:aspect>
</aop:config>

Schema or @AspectJ

Schema: 配置集中

@AspectJ: 配置分散,但兼容AspectJ

AOP技术单元测试

本次得分为:7.00/7.00, 本次测试的提交时间为:2017-09-11
1单选(2分)

如下关于AOP的描述中错误的是:

  • A.AOP可以对代码进行解耦;
  • B.AOP可以作用于函数执行;
  • C.AOP可以简化代码;
  • D.AOP只能作用于public函数;2.00/2.00
2单选(2分)

下面哪个不属于AOP的Advice类型:

  • A.Before returning;2.00/2.00
  • B.After finally;
  • C.After returning;
  • D.Before;
3多选(3分)

关于Pointcut表达式,说法错误的是:

  • A.必须得声明函数的参数列表匹配模式;
  • B.必须得声明函数的modifiers;1.00/3.00
  • C.必须得声明函数抛出异常的匹配模式;1.00/3.00
  • D.必须得声明函数所在类的包的匹配模式;1.00/3.00

AOP技术作业

http://zhanjingbo.site/14759932535473.html

1(12分)

完成一个基本的应用,提供若干个Service(>=2),每个Service提供基本的增删查改的接口(实现随意,比如输出一行信息),通过AOP保证所有所有的Service接口在正常调用返回后以及抛出异常时(Service接口模拟),打出如下信息:函数名称,函数参数,并说明发生的事件:正常返回或者抛出异常。

(注:需要查找资料,了解JoinPoint的API)

基本要求:必须附加一个项目说明文档,说明每个功能点对应程序的运行结果(截图),项目的接口说明或者关键代码(不要把全部代码贴出来)等可以反映项目结果的内容。提交作业的时候必须有这个项目说明文档,否则会影响最终评分。

Java开发工程师(Web方向) - 04.Spring框架 - 第3章.AOP技术的更多相关文章

  1. Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器

    第2章.IoC容器 IoC容器概述 abstract: 介绍IoC和bean的用处和使用 IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, ...

  2. Java开发工程师(Web方向) - 04.Spring框架 - 第1章.Spring概述

    第1章.Spring概述 Spring概述 The Spring Framework is a lightweight solution and a potential one-stop-shop f ...

  3. Java开发工程师(Web方向) - 04.Spring框架 - 第5章.Web框架

    第5章--Web框架 Web框架概述 Web框架单元测验 本次得分为:13.50/15.00, 本次测试的提交时间为:2017-09-25 1单选(2分) 关于Spring MVC中Dispatche ...

  4. Java开发工程师(Web方向) - 04.Spring框架 - 第4章.数据访问

    第4章--数据访问 Spring JDBC DAO (Data Access Object) 实现数据访问相关接口(接口和实现分离) ORM (Object Relation Mapping) 对象关 ...

  5. Java开发工程师(Web方向) - 04.Spring框架 - 期末测试

    Spring框架客观题 Spring框架编程题 http://blog.csdn.net/sinoacc/article/details/51702458 1 (25分) 假设有如下数据表: crea ...

  6. Java开发工程师(Web方向) - 01.Java Web开发入门 - 第1章.Web应用开发概述

    第1章--Web应用开发概述 Web应用开发概述 浏览器-服务器架构(BS-architecture) browser/ App    ---- request ---->    server ...

  7. Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis

    第5章--MyBatis MyBatis入门 Abstract: 数据库框架的工作原理和使用方法(以MyBatis为例) 面向对象的世界与关系型数据库的鸿沟: 面向对象世界中的数据是对象: 关系型数据 ...

  8. Java开发工程师(Web方向) - 03.数据库开发 - 第1章.JDBC

    第1章--JDBC JDBC基础 通过Java Database Connectivity可以实现Java程序对后端数据库的访问 一个完整的数据库部署架构,通常是由客户端和服务器端两部分组成 客户端封 ...

  9. Java开发工程师(Web方向) - 02.Servlet技术 - 第3章.Servlet应用

    第3章.Servlet应用 转发与重定向 转发:浏览器发送资源请求到ServletA后,ServletA传递请求给ServletB,ServletB生成响应后返回给浏览器. 请求转发:forward: ...

随机推荐

  1. 检查BUG插件 代码规范(Findbugs)插件 安装以及使用(idea)

    使用findbugs进行检查代码规范 Findbugs很多人都并不陌生,Eclipse中有插件可以帮助查找代码中隐藏的bug,IDEA中也有这款插件.这个插件可以帮助我们查找隐藏的bug,比较重要的功 ...

  2. PAT——1005. 继续(3n+1)猜想 (25)

    卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数.例如对n=3进行验证的时候, ...

  3. DU1525 Euclid's Game 博弈

    HDU1525 Euclid's Game 博弈 题意 给定两个数字 a, b. 每次只能用 较大的值 减去 较小的值的倍数, 两个人轮流进行操作, 第一个得到 0 的胜利. 分析 对于 a == b ...

  4. win7环境中使用notepad++配置python IDE

    1, 下载notepad++,并安装 http://notepad-plus-plus.org/download/v6.5.5.html 2, 下载python的win版本,并安装 https://w ...

  5. 去掉CodeIgniter URL中的index.php

    CI默认的rewrite url中是类似这样的,例如你的CI根目录是在/CodeIgniter/下,你的下面的二级url就类似这样http://localhost /CodeIgniter/index ...

  6. ios之coredata(一)

    下面开始学习一下CoreData. Core Data不是一个关系型数据库,也不是关系型数据库管理系统(RDBMS).Core Data 为数据变更管理.对象存储.对象读取恢复的功能提供了支持. 它可 ...

  7. Oracle->oracle单实例Shell脚本[20180122]

    脚本主要用于redhat平台安装11g和12c软件     依赖包检查与安装     用户.组检查与安装     系统内核.用户限制     防火墙.selinux关闭     注意,linux组脚本 ...

  8. jQuery 常用核心方法

    jQuery 常用核心方法 .each() 遍历一个jQuery对象,为每个匹配元素执行一个函数 $('p').each(function(idx,node){ $(node).text(idx + ...

  9. vue vue-router 完美实现前进刷新,后退不刷新。附scrollBehavior源码解析

    需求:在一个vue的项目中,我们需要从一个列表页面点击列表中的某一个详情页面,从详情页面返回不刷新列表,而从列表的上一个页面重新进入列表页面则需要刷新列表. 而浏览器的机制则是每一次的页面打开都会重新 ...

  10. 详解PreparedStatement

    详解PreparedStatement /** * PrepareStatement 测试插入数据库 */ /** * 如果使用Statement,那么就必须在SQL语句中,实际地去嵌入值,比如之前的 ...