跟着刚哥学习Spring框架--AOP(五)

时间:2023-02-17 22:25:23

AOP

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

基于Spring的AOP简单实现

1、通过配置文件实现AOP

首先需要额外上网下载两个jar包:

  ① aspectjrt.jar
  ② aspectjweaver.jar

先定义一个接口IHelloWorld:

 public interface IHelloWorld {
void printHelloWorld(String name);
}

定义一个实现类HelloWorld

 public class HelloWorld implements IHelloWorld {

     @Override
public void printHelloWorld(String name) {
System.out.println("Print HelloWorld :" + name);
}
}

实现了两个切面(一个日志切面和一个时间切面)

 public class LogAspect {
public void beforeLog(JoinPoint joinPoint){
System.out.println("log before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
public void afterLog(JoinPoint joinPoint){
System.out.println("log after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
}
 public class TimeAspect {
public void beforeTime(JoinPoint joinPoint){
System.out.println("time before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
public void afterTime(JoinPoint joinPoint){
System.out.println("time after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
}

定义一个配置文件

 <?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:context="http://www.springframework.org/schema/context"
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/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"> <!-- 核心业务Bean -->
<bean id="helloworld" class="com.hzg.HelloWorld"></bean> <!-- 切面Bean(日志切面) -->
<bean id="logAspect" class="com.hzg.LogAspect"></bean> <!-- 切面Bean(时间切面) -->
<bean id="timeAspect" class="com.hzg.TimeAspect"></bean> <!-- AOP配置 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut id="pointcut" expression="execution(* com.hzg.IHelloWorld.*(..))"></aop:pointcut>
<!-- 配置切面(日志切面)切面的优先级需要通过order定义 -->
<aop:aspect id="log" ref="logAspect" order="1">
<!-- 配置通知(前置通知、后置通知、返回通知、异常通知、环绕通知) -->
<aop:before method="beforeLog" pointcut-ref="pointcut"></aop:before>
<aop:after method="afterLog" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
<!-- 配置切面(时间切面)切面的优先级需要通过order定义 -->
<aop:aspect id="time" ref="timeAspect" order="2">
<!-- 配置通知(前置通知、后置通知、后置返回通知、异常通知、环绕通知) -->
<aop:before method="beforeTime" pointcut-ref="pointcut"></aop:before>
<aop:after method="afterTime" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config> </beans>

定义Main主方法

 public static void main(String[] args) {
//由于ApplicationContext没有close方法,所以要使用它下面接口ConfigurableApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext("configaop.xml");
IHelloWorld helloworld = (IHelloWorld) ctx.getBean("helloworld");
helloworld.printHelloWorld("gangge");
}

输出结果:

log before method printHelloWorld参数 :[gangge]
time before method printHelloWorld参数 :[gangge]
Print HelloWorld :gangge
time after method printHelloWorld参数 :[gangge]
log after method printHelloWorld参数 :[gangge]

2、通过注解实现AOP

实现配置文件方式同样的功能

先定义一个接口文件(没区别)

 public interface IHelloWorld {
void printHelloWorld(String name);
}

定义实现类(区别:加了一个注解@Component)

 @Component
public class HelloWorld implements IHelloWorld { @Override
public void printHelloWorld(String name) {
System.out.println("Print HelloWorld :" + name);
}
}

定义两个切面(一个日志切面和一个时间切面 区别:增加了@Component、@Aspect和@Order注解)

 @Component
@Aspect
@Order(1)
public class LogAspect {
public void beforeLog(JoinPoint joinPoint){
System.out.println("log before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
public void afterLog(JoinPoint joinPoint){
System.out.println("log after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
}
 @Component
@Aspect
@Order(2)
public class TimeAspect {
@Before("execution(* com.hzg.HelloWorld.*(..))")
public void beforeTime(JoinPoint joinPoint){
System.out.println("time before method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
@After("execution(* com.hzg.HelloWorld.*(..))")
public void afterTime(JoinPoint joinPoint){
System.out.println("time after method " + joinPoint.getSignature().getName() + "参数 :" + Arrays.asList(joinPoint.getArgs()));
}
}

定义配置文件(区别: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:context="http://www.springframework.org/schema/context"
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/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:component-scan base-package="com.hzg"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>

定义Main主方法(无区别)

 public static void main(String[] args) {
//由于ApplicationContext没有close方法,所以要使用它下面接口ConfigurableApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext("configaop.xml");
IHelloWorld helloworld = (IHelloWorld) ctx.getBean("helloworld");
helloworld.printHelloWorld("gangge");
}

输出结果和配置文件一样

五种通知

1、前置通知

  上面已经有了,不再说明

2、后置通知

  上面已经有了,不再说明

3、返回通知

 /**
* 返回通知: 方法正常执行完后调用,如果有异常,则不调用
* 可以访问到方法的返回值
* @param joinPoint
* @param result 方法的返回值
*/
@AfterReturning(value="execution(* com.hzg.*(..))",returning="result")
public void afterReturning(JoinPoint joinPoint,Object result){
//方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("method "+methodName+" end:"+result);
}

4、异常通知

 /**
* 异常通知: 当方法出现异常时
* 可以指定出现哪些异常时才执行: 如何指定?通过入参指定,如:
* 如果入参为NullPointException ex,那么只有在发生空指针异常时才执行
* @param joinPoint
* @param ex
*/
@AfterThrowing(value="execution(* com.hzg.*(..))",throwing="ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
//方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("method "+methodName+" occurs:"+ex);
}

5、环绕通知

  环绕通知类似于动态代理的全过程,可以使用环绕通知实现前置通知,后置通知,返回通知,异常通知的功能,十分强大,但并不常用

 /**
* 环绕通知需要携带ProceedingJoinPoint类型的参数
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint这个类型的参数可以决定是否执行目标方法
* 且环绕通知必须有返回值,返回值即为目标方法的返回值
* @param pjd
*/
@Around("execution(* com.hzg.*(..))")
public Object around(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName(); try {
//前置通知
System.out.println("method:"+methodName+" begins with "+Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("method:"+methodName+" end with "+result);
} catch (Throwable e) {
// 异常通知
System.out.println("method:"+methodName+" occurs exception "+e);
}
//后置通知
System.out.println("method ends");
return result;
}

-------------------------------------------------------------------------------------------------------------------------

跟着刚哥学习Spring框架系列:

跟着刚哥学习Spring框架--创建HelloWorld项目(一)

跟着刚哥学习Spring框架--Spring容器(二)

跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

跟着刚哥学习Spring框架--通过注解方式配置Bean(四)

跟着刚哥学习Spring框架--AOP(五)

跟着刚哥学习Spring框架--JDBC(六)

跟着刚哥学习Spring框架--事务配置(七)

跟着刚哥学习Spring框架--AOP(五)的更多相关文章

  1. 跟着刚哥学习Spring框架--创建HelloWorld项目(一)

    1.Spring框架简介 Spring是一个开源框架,Spring是在2003年兴起的一个轻量级的开源框架,由Rod johnson创建.主要对JavaBean的生命周期进行管理的轻量级框架,Spri ...

  2. 跟着刚哥学习Spring框架--通过注解方式配置Bean(四)

    组件扫描:Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件. 特定组件包括: 1.@Component:基本注解,识别一个受Spring管理的组件 2.@Resposit ...

  3. 跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

    Spring配置Bean有两种形式(XML和注解) 今天我们学习通过XML方式配置Bean 1. Bean的配置方式 通过全类名(反射)的方式   √ id:标识容器中的bean.id唯一. √ cl ...

  4. 跟着刚哥学习Spring框架--Spring容器(二)

    Spring容器 启动Spring容器(实例化容器) -- IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化(加载启动),这样才可以从容器中获取Bean的实例并使用.  Bean是S ...

  5. 跟着刚哥学习Spring框架--事务配置(七)

    事务 事务用来保证数据的完整性和一致性. 事务应该具有4个属性:原子性.一致性.隔离性.持久性.这四个属性通常称为ACID特性.1.原子性(atomicity).一个事务是一个不可分割的工作单位,事务 ...

  6. 跟着刚哥学习Spring框架--JDBC(六)

    Spring的JDBC框架 Spring JDBC提供了一套JDBC抽象框架,用于简化JDBC开发. Spring主要提供JDBC模板方式.关系数据库对象化方式.SimpleJdbc方式.事务管理来简 ...

  7. 深入浅出学习Spring框架(四):IoC和AOP的应用——事务配置

    在前文 深入浅出学习Spring框架(一):通过Demo阐述IoC和DI的优势所在. 深入浅出学习Spring框架(三):AOP 详解 分别介绍了Spring的核心功能——IoC和AOP,光讲知识远远 ...

  8. spring框架 AOP核心详解

    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Asp ...

  9. spring框架aop用注解形式注入Aspect切面无效的问题解决

    由于到最后我的项目还是有个邪门的错没解决,所以先把文章大概内容告知: 1.spring框架aop注解扫描默认是关闭的,得手动开启. 2.关于Con't call commit when autocom ...

随机推荐

  1. Python中三目计算符的正确用法及短路逻辑

    今天在看别人代码时看到这样一种写法, 感觉是个挺容易踩到的坑, 搞清楚后写出来备忘. 短路逻辑 Python中进行逻辑运算的时候, 默认采用的是一种叫做短路逻辑的运算规则. 名字是很形象的, 下面直接 ...

  2. java collection&period;sort&lpar;&rpar;根据时间排序list

    首先:定义bean 然后:定义比较器 最后:测试使用 一.userBean package com.butterfly.Class; public class user { private Strin ...

  3. Objective-C 利用OC的消息机制,使用Method Swizzling进行方法修改

    功能:修改父类不可修改函数方法,函数方法交换 应用场景:假如我们使用的他人提供一个的framework,.m已被打包成二进制.a无法修改源码,只留下.h头文件,那假如代码中某个函数出现了问题可以通过这 ...

  4. Django基础-过滤器

    1.可以通过过滤器来修改变量的显示,过滤器的形式是:{{ variable | filter }},管道符号'|'代表使用过滤器 2.过滤器能够采用链式的方式使用,例如:{{ text | escap ...

  5. GNU的ar,ranlib和nm

    转:http://blog.csdn.net/yuntongsf/article/details/6284517 RANLIB 的作用: CC = CC=/usr/local/ndk/toolchai ...

  6. MySQL存储写入性能严重抖动分析

    案例描述: 通过iostat发现存储的写性能长期维持在10MB左右,而且因为写性能差已经导致数据库性能变差: 两个小时以后,iostat发现系统的写性能已经能够到100MB以上,数据库性能也恢复正常. ...

  7. Android - 文字向上翻滚效果的实现

    本文转载https://xwc2013.iteye.com/blog/1976051 今天看到了一种文字翻滚的效果,感觉非常实用.所以就自己试着做出了这种效果,现在把它分享给大家! 首先在res目录下 ...

  8. MySql 8&period;0 版本使用navicat连不上解决

    先通过命令行进入mysql的root账户: 更改加密方式 ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE ...

  9. 浏览器调用接口发现Provisional headers are shown

    一次请求时候报错 无论如何也找不到错误,后台接口和前端请求都是正确的.后来发现是 自己浏览器上装了广告拦截的插件 把我这个请求给拦截 果断卸载插件立马就好了.

  10. leetcode第27题:移除指定元素

    给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...