Spring基础知识之基于注解的AOP
背景概念:
1)横切关注点:散布在应用中多处的功能称为横切关注点
2)通知(Advice):切面完成的工作。通知定了了切面是什么及何时调用。
5中可以应用的通知:
前置通知(Before):在目标方法被调用前调用通知功能。
后置通知(After):在目标方法完成后调用通知,此时不会关系方法输出什么。
返回通知(After-returning):在目标方法成功执行后调用通知。
异常通知(After-throwing):在目标方法抛出异常后调用通知。
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的方法。
3)连接点(Join Point):调用通知的时机称为连接点。
4)切点(Pointcut):通知要织入的连接点的范围。
5)切面(Aspect):切面:通知和切点的结合。
6)引入(Introduction):允许我们向现有的类添加新方法或属性。
7)织入(weaving):把切面应用到目标对象并创建新的代理对象的过程。
切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:
编译期:切面在目标类编译时织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
类加载期:切面在目标类被加载到JVM时织入。这种方式需要特殊的类加载器,他可以在目标类被引入应用之前增加该目标类的字节码。AspectJ5的加载时织入,就支持以这种方式织入。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在切面被织入时,AOP容器会为目标对象动态创建代理对象,SpringAOP就是以这种方式进行织入的。
注:注解的解释:注解本身是没有功能的,就和XML一样,注解和XML都是一种元数据,元数据就是解释数据的数据,这里就是所谓的配置。
注解的功能来自用注解的这个地方
Spring对AOP的支持
SpringAOP存在的目的:解耦。
AOP可以让一组类共享相同的行为。避免通过继承等高耦合方式实现为类添加功能。
Spring支持4中类型的AOP支持:
1)基于代理的经典SpringAOP;
2)纯POJO切面;
3)@AspectJ注解驱动的切面;
4)注入式AspectJ切面(适用于各个Spring版本)。
注:前三种都是SpringAOP实现的变体,SpringAOP构建在动态代理的基础之上,因此,Spring对AOP的支持局限于方法拦截。
如果AOP需求超过了简单的方法调用(如构造器或属性拦截),那么需要使用第四种方式。
Spring通知是java编写的
Spring的通知是POJO实现的,可以基于注解和XML实现,相对简单便捷。
AspectJ以java扩展的方式实现的,优点:特有的AOP语言可以获得更强大的细粒度的控制以及更丰富的AOP工作集,但学习成本大。
Spring在运行时通知对象
Spring运行时才会创建代理对象,所以我们不需要特殊的编译器来织入SpringAOP的切面。
Spring只支持方法级别的连接点
如果需要使用除方法拦截之外的连接点拦截功能,那么我们可以利用Aspect来补充Spring AOP的功能。
通过切点来选择连接点
注:只有execution指示器是实际执行匹配的,其他指示器都是限定匹配的,我们在编写切点定义时最主要使用的指示器应当是:execution指示器,在此基础上使用其他指示器来限制所匹配的切点。
编写切点:
定义一个performance接口:
package com.spring.learn.index;
public interface Performance {
public void perform();
}
则我们想在表演接口中的表演方法触发我们的通知时,需要的切点表达式:
execution(* com.spring.learn.index.Performance.perform(..))
若我们仅仅需要的是com包下的类,可以使用within()来限制匹配:
execution(* com.spring.learn.index.Performance.perform(..)) && within(com.*)
使用 && 来表示与关系;同理 || 表示或关系;!表示非。
注在XML中使用and or not 来替换(因为符号在XML中有特殊含义)。
在切点中选择bean
除了上述指示器外Spring还引入了bean()指示器,bean()使用bean ID或者bean名称来作为参数限制切点只匹配特定的bean。
使用注解创建切面
已经定义了Performance接口,它是切面中目标对象。使用AspecJ注解来定义切面。
定义切面:
使用Audience类:观看演出的切面:
@Aspect
public class Audience { //这是表演之前
@Before("execution(* com.spring.learn.index.Performance.perform(..))")
public void silenceCellPhones(){
System.out.println("观众的手机静音");
} //表演之前
@Before("execution(* com.spring.learn.index.Performance.perform(..))")
public void takeSeats() {
System.out.println("观众落座");
} //表演之后
@AfterReturning("execution(* com.spring.learn.index.Performance.perform(..))")
public void applause() {
System.out.println("掌声雷动");
} //表演失败
@AfterThrowing("execution(* com.spring.learn.index.Performance.perform(..))")
public void demandRefund() {
System.out.println("要求退款");
}
}
@Aspect注解,表明了Audience不仅仅是一个POJO,还是一个切面。
Audience中的方法使用注解的方式定义了通知何时调用。AspectJ中提供了五个注解来定义通知:
每一个注解都使用了切点表达式来作为他的值。但是我们的切点表达式重复使用了四次,其实我们可以只写一次,然后使用引用的方式实现同样的操作。
@PointCut注解能够在一个@Aspect定义的切面内定义可重复使用的切点:
@Aspect
public class Audience { //定义命名的切点
@Pointcut("execution(* com.spring.learn.index.Performance.perform(..))")
public void myPointCut(){} @Before("myPointCut()")
public void silenceCellPhones(){
System.out.println("观众的手机静音");
} @Before("myPointCut()")
public void takeSeats() {
System.out.println("观众落座");
} @AfterReturning("myPointCut()")
public void applause() {
System.out.println("掌声雷动");
} @AfterThrowing("myPointCut()")
public void demandRefund() {
System.out.println("要求再来一场新的表演");
}
}
到此为止Audience仍只是一个普通的POJO类,即使使用了@Aspect注解也不会被视为切面,这些注解不会解析,也不会创建将其转化为切面的代理。
需要额外的配置类,并在配置类上使用EnableAspectJ-AutoProxy 注解启动自动代理功能:
//启用AspectJ自动代理
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class AspectConfig { //声明Audience bean
@Bean
public Audience audience() {
return new Audience();
}
}
如果使用的是XML来装配bean的话,需要使用Spring AOP命名空间的<aop:aspectj-autoproxy> 元素:
不论哪种方式,AspectJ自动代理都会使用@Aspect注解的bean创建一个代理,这个代理会围绕所有该切面的切点所匹配的bean。将会为bean创建一个代理,使通知的方法在切点前后被调用。
创建环绕通知
@Aspect
public class Audience { //定义命名的切点
@Pointcut("execution(* com.spring.learn.index.Performance.perform(..))")
public void myPointCut(){} @Around("myPointCut()")
public void watchPerformance(ProceedingJoinPoint jp){
try {
System.out.println("观众的手机静音");
System.out.println("观众落座");
jp.proceed();
System.out.println("掌声雷动");
}catch (Throwable e) {
System.out.println("退票退票");
}
}
}
可以看到环绕方法可以实现之前的几个注解的所有功能。
注:ProceedingJoinPoint参数,这个参数必须有,因为要在通知中通过它来调用被通知的方法。通知方法可以做任何事,当要将控制权交给被通知的方法时,需要调用ProceedingJoinPoint的proceed()方法。
注: 一定要调用proceed()方法,否则通知将会阻塞被通知方法的调用。
实际上你也可以对被通知方法进行多次调用。这一般是为了实现重试逻辑。
处理通知中的参数
场景:磁带中不同的磁道有多种歌曲,调用playTrack()方法可以实现播放。要记录每个磁道被播放的次数,使用切面来完成:
通过上述的方式可以将切面上的参数同步到通知上。
通过注解引入新功能
通过引入的AOP概念,切面可以为Spring bean添加新的方法。
我们为Performance接口引入下面的接口:
public interface Encoreable {
void perforEncore();
}
则定义的切面如下:
@Aspect
public class EncoreableIntroducer {
@DeclareParents(value = "com.spring.learn.index.Performance+",
defaultImpl = DefaultEncoreable.class)
public static Encoreable encoreable;
}
注:和其他切面一样,我们需要将在Spring应用中将Encoreableintroducer声明为一个bean:
//启用AspectJ自动代理
@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class AspectConfig { //声明Audience bean
@Bean
public Audience audience() {
return new Audience();
} //声明EncoreableIntroducer bean
@Bean
public EncoreableIntroducer encoreableIntroducer() {
return new EncoreableIntroducer();
}
}
或:
由于目前已经不接触XML配置了。
所以XML配置与AspectJ的更强大面向切面实现,等过一阵再来补充。
AspectJ的全面学习博客地址。
AspectJ切入点语法详解地址。
本文内容是书中内容兼具自己的个人看法所成。可能在个人看法上会有诸多问题(毕竟知识量有限,导致认知也有限),如果读者觉得有问题请大胆提出,我们可以相互交流、相互学习,欢迎你们的到来,心成意足,等待您的评价。
Spring基础知识之基于注解的AOP的更多相关文章
- Spring基础知识
Spring基础知识 利用spring完成松耦合 接口 public interface IOutputGenerator { public void generateOutput(); } 实现类 ...
- Spring_Spring与AOP_AspectJ基于注解的AOP实现
一.AspectJ.Spring与AOP的关系 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Cl ...
- spring中基于注解使用AOP
本文内容:spring中如何使用注解实现面向切面编程,以及如何使用自定义注解. 一个场景 比如用户登录,每个请求发起之前都会判断用户是否登录,如果每个请求都去判断一次,那就重复地做了很多事情,只要是有 ...
- Spring 基础知识(一)基本概念 DI、IOC、AOP
DI(依赖注入) 和IOC(控制反转)都是一种设计思想,要理解他们,让我们从coding中的一些痛点入手. 依赖注入 Dependency Injection : 如果A类要使用B类的一个方法,首先必 ...
- 阶段3 2.Spring_08.面向切面编程 AOP_9 spring基于注解的AOP配置
复制依赖和改jar包方式 src下的都复制过来. 复制到新项目里了 bean.xml里面复制上面一行代码到下面.把aop改成context. 配置spring容器创建时要扫描的包 Service的配置 ...
- Spring 基础知识
Spring架构简单描述 原文:https://www.shiyanlou.com/courses/document/212 Spring 概述 1. Spring 是什么 Spring是一个开源的轻 ...
- Spring基础知识详解
Spring 概述 1. 什么是spring? Spring 是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring ...
- spring 基础知识复习
spring是一个分层架构,由 7 个定义良好的模块组成.Spring 模块构建在核心容器之上,核心容器定义了创建.配置和管理 bean 的方式. 组成spring框架的每个模块(或组件)都可单独存在 ...
- Spring系列9:基于注解的Spring容器配置
写在前面 前面几篇中我们说过,Spring容器支持3种方式进行bean定义信息的配置,现在具体说明下: XML:bean的定义和依赖都在xml文件中配置,比较繁杂. Annotation-based ...
随机推荐
- 简单聊聊java中如何判定一个对象可回收
背景 说到java的特性,其中一个最重要的特性便是java通过new在堆中分配给对象的内存,不需要程序员主动去释放,而是由java虚拟机自动的回收.这也是java和C++的主要区别之一:那么虚拟机是如 ...
- [转载] RED-BLACK(红黑)树的实现TreeMap源码阅读
转载自http://lxy2330.iteye.com/blog/1664786 由于平衡二叉树与红黑树都是二叉排序树,又红黑树是对平衡二叉树的一种改进实现,所以它的很多思想算法都来源于排序二叉或平衡 ...
- MySQL子查询优化实例
优化:子查询改写成关联查询 线上遇到问题,查询较慢,如为对应SQL的查询执行计划: localhost.\G . row *************************** id: select_ ...
- 【机器学习】人工神经网络ANN
神经网络是从生物领域自然的鬼斧神工中学习智慧的一种应用.人工神经网络(ANN)的发展经历的了几次高潮低谷,如今,随着数据爆发.硬件计算能力暴增.深度学习算法的优化,我们迎来了又一次的ANN雄起时代,以 ...
- php 的开发工具
通过上篇我们已经配置好了php的开发环境,我们就可以在这个模拟的环境下运行我们编写的php代码了. 在编写代码前,先安装一个自己喜欢的代码编辑器. 1.sublime text Sublime Tex ...
- 为什么大家觉得自学HTML5难?
互联网发展到今天,越来越多的技术岗位人才出现了稀缺的状态,就拿当前的HTML5来讲,基本成为了每家互联网公司不可缺少的人才.如果抓住这个机会,把HTML5搞好,那么前途不可限量,而且这门行业是越老越吃 ...
- Linux下安装Redis php-redis扩展 redis重启shell脚本 超详细!
前言 前面刚写过nosql其中三款热门产品的对比,这次主要写关于Redis的一些事情,Redis的介绍.安装以及扩展(php-redis,因为我是phper)安装等等.同时是写给我的朋友(cccjjj ...
- hive中一些常用的sql语句
1.建表 CREATE TABLE IF NOT EXISTS student( time varchar(64) , num int , age int )PARTITIONED BY ( scor ...
- OpenStack搭建遇到的问题2(组件配置错误了,别重装全部,就把模块卸载就行了)
apt-get remove -y mysql-server python-mysqldb 在装OpenStack的时候,出错的可能就是就是一个模块,比如keysstone或者是glance出错了,我 ...
- [WPF]本地化入门
1. 前言 WPF的本地化是个很常见的功能,我做过的WPF程序大部分都实现了本地化(不管最终有没有用到).通常本地化有以下几点需求: 在程序启动时根据CultureInfo.CurrentUICult ...