1.AOP前奏: 使用动态代理解决日志需求

ArithmeticCalculator.java

package com.aff.spring.aop.helloworld;
public interface ArithmeticCalculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}

ArithmeticCalculatorImpl.java

package com.aff.spring.aop.helloworld;
public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override
public int add(int i, int j) {
int result = i + j;
return result;
} @Override
public int sub(int i, int j) {
int result = i - j;
return result;
} @Override
public int mul(int i, int j) {
int result = i * j;
return result;
} @Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}

ArithmeticCalculatorLoggingProxy.java

package com.aff.spring.aop.helloworld;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays; public class ArithmeticCalculatorLoggingProxy {
//要代理的对象
private ArithmeticCalculator target; public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
super();
this.target = target;
} //返回代理对象
public ArithmeticCalculator getLoggingProxy(){
ArithmeticCalculator proxy = null; ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = new Class[]{ArithmeticCalculator.class};
InvocationHandler h = new InvocationHandler() {
/**
* proxy: 代理对象。 一般不使用该对象
* method: 正在被调用的方法
* args: 调用方法传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
//打印日志
System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args)); //调用目标方法
Object result = null; try {
//前置通知
result = method.invoke(target, args);
//返回通知, 可以访问到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
} //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值 //打印日志
System.out.println("[after] The method ends with " + result); return result;
}
}; /**
* loader: 代理对象使用的类加载器。
* interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法.
* h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
*/
proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h); return proxy;
}
}

Main.java

package com.aff.spring.aop.helloworld;

public class Main {
public static void main(String[] args) { ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl(); arithmeticCalculator = new ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy(); int result = arithmeticCalculator.add(11, 12);
System.out.println("result:" + result); result = arithmeticCalculator.div(21, 3);
System.out.println("result:" + result); }
}

结果如下

[before] The method add begins with [11, 12]
[after] The method ends with 23
result:23
[before] The method div begins with [21, 3]
[after] The method ends with 7
result:7

2.AOP简介

①AOP 的好处:
    每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
    业务模块更简洁, 只包含核心业务代码

②.AOP术语

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice): 切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。

连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice): 切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

③用 AspectJ 注解声明切面

要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
通知是标注有某种注解的简单的 Java 方法.
AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

LoggingAspect.java

package com.aff.spring.aop.imlp;

import java.util.Arrays;
import java.util.List; 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.springframework.stereotype.Component; //把这个类声明为切面,需要把这个类放到IOC容器中, 再声明为一个切面
@Aspect
@Component
public class LoggingAspect {
//声明该方法是一个前置通知, 在目标方法开始之前执行
@Before("execution(* com.aff.spring.aop.imlp.*.*(int , int ))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " +methodName+"begins with"+args); }
//后置通知:在目标方法执行后(无论 是否发生异常) ,执行的通知
//在后置通知中还不能访问目标方法执行的结果
//第一个*:任意返回值类型; 第二个*:包下的任意类; 第三个*:任意方法; .. : 任意参数
@After("execution(* com.aff.spring.aop.imlp.*.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " +methodName+"ends ");
} /**
* 返回通知

* 在方法正常结束后执行的代码
* 返回通知是可以访问到方法的返回值
* @param joinPoint
*/
@AfterReturning(value="execution(* com.aff.spring.aop.imlp.*.*(..))",returning="result")
public void afterReturning(JoinPoint joinPoint,Object result ){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " +methodName+"ends with "+result);
} /**
* 异常通知

* 在目标方法出现异常时 会执行的代码
* 可以访问到异常对象,且可以指定出现特定异常时在执行通知代码
* @param joinPoint
* @param ex
*/
@AfterThrowing(value="execution(* com.aff.spring.aop.imlp.*.*(..))",throwing="e")
public void afterThrowing(JoinPoint joinPoint,Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " +methodName+"ocurs exception :"+e);
} /**
* 转绕通知需要携带 ProceedingJoinPoint 类型的参数

* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法
* 且环绕通知必须有返回值
* @param pjd
*/
@Around("execution(* com.aff.spring.aop.imlp.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result =null;
String methodName= pjd.getSignature().getName();
//执行目标方法
try {
//前置通知
System.out.println("The method " +methodName+"begins with"+Arrays.asList(pjd.getArgs())); //执行目标方法
result = pjd.proceed(); //返回通知
System.out.println("The method "+methodName +"end with"+result);
} catch (Throwable e) {
//异常通知
System.out.println("The method"+methodName+" ocurs exception :"+e);
throw new RuntimeException(e);
}
System.out.println("The method "+methodName +"ends");
return result;
} }

Main

package com.aff.spring.aop.imlp;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class); int result = arithmeticCalculator.add(2, 3);
System.out.println("result:"+result); int result2 = arithmeticCalculator.div(2, 0);
System.out.println("result2:"+result2); }
}

效果如下

The method addbegins  with[2, 3]
The method addends
The method addends with 5
result:5
The method divbegins with[2, 1]
The method divends
The method divends with 2
result2:2

 目录

3.@Order()注解指定切面的优先级,值越小优先级越高

   如:@Order(1)高于@Order(2)

4.重用切点表达式

//把这个类声明为切面,需要把这个类放到IOC容器中, 再声明为一个切面
@Aspect
@Component
public class LoggingAspect { /**
* 定义一个方法, 用于声明切入点表达式,一般,该方法中不再需要添加其他代码
*
* @Pointut 声明切点表达式
* 后面的其他通知 直接使用方法名来引用当前的切点表达式
*/
@Pointcut("execution(* com.aff.spring.aop.imlp.*.*(int , int ))")
public void declareJointPointExpression(){ } //声明该方法是一个前置通知, 在目标方法开始之前执行
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " +methodName+"begins with"+args);
}
}

Spring_AOP_AspectJ支持的通知注解的更多相关文章

  1. JAXB学习(二): 对JAXB支持的主要注解的说明

    我们在上一篇中对JAXB有了一个大致的认识,现在我们来了解JAXB的一些主要注解. 顶层元素:XmlRootElement 表示整个XML文档的类应该使用XmlRootElement修饰,其实就像之前 ...

  2. 支持异步通知的globalfifo平台设备驱动程序及其测试代码

    驱动: #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #incl ...

  3. AspectJ 切面注解中五种通知注解:@Before、@After、@AfterRunning、@AfterThrowing、@Around

    https://blog.csdn.net/u010502101/article/details/78823056

  4. Spring 通过XML配置文件以及通过注解形式来AOP 来实现前置,环绕,异常通知,返回后通知,后通知

    本节主要内容: 一.Spring 通过XML配置文件形式来AOP 来实现前置,环绕,异常通知     1. Spring AOP  前置通知 XML配置使用案例     2. Spring AOP   ...

  5. 学习 Spring (十一) 注解之 Spring 对 JSR 支持

    Spring入门篇 学习笔记 @Resource Spring 还支持使用 JSR-250 中的 @Resource 注解的变量或 setter 方法 @Resource 有一个 name 属性,并且 ...

  6. spring通知的注解

    1.代理类接口Person.java package com.xiaostudy; /** * @desc 被代理类接口 * * @author xiaostudy * */ public inter ...

  7. Spring学习记录(十二)---AOP理解和基于注解配置

    Spring核心之二:AOP(Aspect Oriented Programming) --- 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软 ...

  8. Spring AOP 5种通知与java动态代理

    接口,要求为每个方法前后添加日志  @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl ...

  9. 面向切面编程AOP:基于注解的配置

    Aop编程就是面向编程的羝是切面,而切面是模块化横切关注点. -切面:横切关注点,被模块化的特殊对象. -通知:切面必须要完成的工作 -目标:被通知的对象 -代理:向目标对象应用通知之后创建的对象. ...

随机推荐

  1. 配置中心Apollo搭建全过程

    总体架构 用户在Portal操作配置发布 Portal调用Admin Service的接口操作发布 Admin Service发布配置后,发送ReleaseMessage给各个Config Servi ...

  2. M - Little Pony and Harmony Chest 状压dp

    M - Little Pony and Harmony Chest 怎么感觉自己越来越傻了,都知道状态的定义了还没有推出转移方程. 首先这个a的范围是0~30   这里可以推出 b数组的范围 0~60 ...

  3. Oracle的pl/sql变量类型

    pl/sql定义 sql是结构化查询语言.sql是不是一个编程语言?编程语言一般都能够声明变量,写条件判断,循环.sql不具备这些特征,所有sql不是一门编程语言.我们在实际的开发中,有这种需要,把s ...

  4. 自动化运维工具Ansible之Roles测验详解

    Ansible Roles 详解与实战案例 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务数据乱放: 3. 该用 ...

  5. 王颖奇 201771010129《面向对象程序设计(java)》第六周学习总结

    实验六 继承定义与使用 实验时间 2018-9-28 1.目的与要求 理论部分: 继承(inheritance): 继承的特点:具有结构层次:子类继承了父类的域和方法. 主要内容: (1)类.子类.超 ...

  6. 【Hadoop离线基础总结】oozie的安装部署与使用

    目录 简单介绍 概述 架构 安装部署 1.修改core-site.xml 2.上传oozie的安装包并解压 3.解压hadooplibs到与oozie平行的目录 4.创建libext目录,并拷贝依赖包 ...

  7. 【Hadoop离线基础总结】oozie任务串联

    目录 需求 1.准备工作目录 2.准备调度文件 3.开发调度的配置文件 4.上传资源文件夹到hdfs对应路径 5.执行调度任务 需求 执行shell脚本 → 执行MR程序 → 执行hive程序 1.准 ...

  8. [hdu5358]分类统计,利用单调性优化

    题意:直接来链接吧http://acm.hdu.edu.cn/showproblem.php?pid=5358 思路:注意S(i,j)具有区间连续性且单调,而⌊log2x⌋具有区间不变性,于是考虑枚举 ...

  9. python 基础应用3

    1.使用while .for循环分别打印字符串s = 'fsufhjshh3hf'中每一个元素. #使用while .for循环分别打印字符串s = 'fsufhjshh3hf'中每一个元素. s = ...

  10. 关于layui数据表格的各种事件

    table.on('tool(demo)', function(obj){}):监听工具条事件,tool 是工具条事件名,demo 是 table 原始容器的属性 lay-filter="对 ...