Spring Framework 之AOP
Spring Framework 之AOP
问题
什么是AOP?
AOP的好处是什么?
AOP的实现方式有哪些?
Spring AOP 与 AspectJ AOP的区别?
AOP概述
AOP(Aspect-Oriented Programming)翻译为中文就是“面向切面编程”。从“面向过程”到“面向对象”编程,编程思想的发展永远是朝更自然、更优雅地描述世界的方向发展。“面向切面“的出现也是这一目的,它是对面向对象的补充,使编程语言能够更好地描述世界。
现实编程中无法将重复出现的代码抽取至父类中。例如鉴权模块、监控模块、日志记录模块等,我们无法将公共代码块纵向抽取。那么如何横向抽取重复代码呢?AOP就能够通过横向抽取机制解决无法纵向抽取的问题,将分散在业务代码中的公共代码块抽取至一个独立的模块中。这也体现了设计模式中的“单一职责“的思想。
AOP知识
1、连接点(Joinpoint)
程序执行的某个特定位置。例如类初始化前后、函数调用前后、函数抛异常后等。类或代码块具有边界性质的特定点就成为“连接点”。
2、切点(PointCut)
每个类或函数都可以认为是连接点,我们如何定位我们关注的“连接点“?我们不需要为每个类或函数添加Advice,PointCut就是通过规则为我们关注的joinpoint添加Advice。
3、增强(Advice)
由aspect添加到特定的Join point的代码块。
4、目标对象(Target)
增强逻辑织入的目标类。
5、引介(Introduction)
引介是一种特殊的增强,他为类添加一些属性和方法。
例子:https://blog.csdn.net/u010599762/article/details/80182178
6、织入(Weaving)
织入是将增强添加至目标类的具体连接点上的过程。
7、代理(Proxy)
类被织入增强后就会产生新的结合了原类与增强的代理类。在Spring AOP中有两种代理,分别是JDK动态代理和CGLib动态代理。
8、切面(Aspect)
切面由切点和增强组成,包括增强的横切逻辑和连接点。Spring AOP负责将切面中的增强逻辑织入指定的连接点中。
代理
静态代理
代理模式
代理模式提供对目标对象进行访问方式,即通过代理对象访问目标对象。可以在目标对象的基础上增强,提供个性功能,达到扩招目标对象功能的作用。
接口
public interface Subject {
void request();
}
具体实现
public class RealSubject implements Subject {
public void request() {
//业务逻辑
}
}
代理类
public class Proxy implements Subject {
//要代理的实现类
private Subject subject = null;
public Proxy() {
this.subject = new Proxy();
}
//通过构造函数传递代理者
public Proxy(Object... objects) {
}
//实现接口定义的方法
public void request() {
this.before();
this.subject.request();
this.after();
}
//预处理
public void before() {
}
//后处理
public void after() {
}
}
动态代理
JDK动态代理
JDK动态代理设计到java.lang.relect包中的两个类:Proxy和InvocationHandler,InvocationHandler可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑与业务逻辑编织到一起。Proxy利用InvocationHandler动态创建某一符合该接口的实例,生成目标类的代理对象。
public class Monitor {
public static void begin(){
System.out.println("before");
}
public static void end(){
System.out.println("after");
}
}
public interface CouponService {
void getCoupon();
}
public class CouponServiceImpl implements CouponService {
public void getCoupon() {
//Monitor.begin();
try {
System.out.println("业务代码");
} catch (Exception e) {
throw new RuntimeException();
}
//Monitor.end();
}
}
public class PerformanceHandler implements InvocationHandler {
//被代理对象
private Object target;
public PerformanceHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Monitor.begin();
Object object = method.invoke(target, args);
Monitor.end();
return object;
}
}
public class Client {
public static void main(String[] args) {
//被代理对象
CouponService target = new CouponServiceImpl();
//让PerformanceHandler将监视横切逻辑编织到CouponService中
PerformanceHandler performanceHandler = new PerformanceHandler(target);
//通过Proxy的newProxyInstace()方法,为编织了业务逻辑与监控逻辑的handler创建一个符合CouponService接口的代理实现
CouponService proxy = (CouponService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),performanceHandler);
proxy.getCoupon();
}
}
CGLIB动态代理
JDK创建代理只能为接口创建代理,实际开发中我们很难保证每个类都有其对应的接口,对于没有通过接口定义业务方法的类,JDK已经没法对其进行代理,这就出现了Cglib,通过字节码技术,为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并织入横切逻辑。
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
//通过代理类调用父类中的方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return result;
}
}
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过冬天生成子类的方式创建代理类
CouponServiceImpl couponService = (CouponServiceImpl) proxy.getProxy(CouponServiceImpl.class);
couponService.getCoupon();
}
}
静态代理与动态代理区别
(1)静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
(2)动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
JDK动态代理 与CGLIB代理区别
Cglib所创建的动态代理,性能要比jdk创建的动态代理高。但对用Cglib创建代理的时间,JDK动态代理显然要快很多。对于无需单例的代理对象或实例池可以使用CGLib来创建代理(无需频繁创建),反之使用JDK动态代理。
@AspectJ
AspectJ是语言级使用Java注解来AOP实现的一种方式,扩招Java语言,定义AOP语法,能够在编译期间提供横切代码织入。
切面定义
Java Configuration方式配置
@Configuration
@EnableAspectJAutoProxy
public class AOPConfig {
}
xml方式配置
<aop:aspectj-autoproxy/>
切面声明
@Component
@Aspect
public class ControllerIntercept {
}
通过@Aspect将ControllerIntercept标识成一个切面,通过@Component将ControllerIntercept标识为一个Bean,Spring框架会收集@Aspect标注的Bean,并将其添加至Spring AOP中。
切点声明
@Pointcut("execution(public * com.ljw.discern_spider.controller.DiscernController.discern(..))")
public void discernWeb() {
}
一个 pointcut 的声明由两部分组成:
- 一个方法签名, 包括方法名和相关参数
- 一个 pointcut 表达式, 用来指定哪些方法执行是我们感兴趣的(即因此可以织入 advice).
切点函数
AspectJ5.0的切点表达式由关键字和操作参数组成,如 "execution( greetTo(..))" 的切点表达式, execution 就是关键字, 而圆括号里的 greetTo(..) 就是操作参数。
函数 | 入参 | 说明 |
---|---|---|
execution() | 方法匹配模式串 | 满足方法匹配模式串所有目标类方法连接点。例如:@Pointcut("execution(public * com.ljw.controller.DiscernController.discern(..))") ,标识目标类中的discern方法 |
@annatation | 方法注解类名 | 表示标注特定注解的目标类方法连接点。例如:@Pointcut("@annotation(com.ljw.ABC)") ,表示标注@ABC注解的目标类连接点 |
within() | 类名匹配串 | 表示特定路径下的所有连接点。例如:within(com.ljw.*) ,表示com.ljw路径下的所有连接点。within(com.ljw.*Service) ,表示com.ljw路径下以Service 结尾的类中的所有连接点 |
args() | 类名 | 匹配参数满足要求的方法。例如@Before(value = "aspectMethod() && args(name)") ,只有一个参数且为name的方法;adgs(com.ljw.ABC),表示所有且仅有一个入参类型与ABC向匹配的方法。 |
target() | 类名 | 目标类按类型匹配指定类。目标类的所有连接点匹配这一切点。例如:target(com.ljw.ABC) |
常见的切点表达式
按方法签名匹配
@Pointcut("execution(public * com.ljw.controller.DiscernController.discern(..))")
按类匹配
@Pointcut("within(com.ljw.*)")
@Pointcut("within(com.ljw..*)")
//匹配实现接口的所有类中的实现的方法
@Pointcut("within(ABCService+)")
按Bean名称匹配
@Pointcut("bean(*Service)")
逻辑运算符运用
可以运用 || 、&& 、!等逻辑运算符
@Pointcut("bean(*Service || *ServiceImpl)")
@Pointcut("bean(*Service) && within(com.ljw.service.*)")
通配符
*:匹配任意字符。只能匹配上下文中的一个元素。
..:匹配任意字符。可以匹配上下文中多个元素。标识类时需要与*结合使用,标识参数时可以单独使用。
+:表示按类型匹配指定类的所有类,必须跟在类名后面。继承或实现指定类的所有类。
增强声明
@Around("discernWeb()")
public Object around(ProceedingJoinPoint joinPoint) {
long start = System.currentTimeMillis();
try {
before(joinPoint);
return joinPoint.proceed(joinPoint.getArgs());
} catch (Throwable t) {
return Result.createFalseRet().withErrMsg("around exception");
}
}
可以从ProceedingJoinPoint中获取参数,对参数进行操作。
@Before:前置增强。
@After:后置增强。
@Around:环绕增强。可以在方法前后进行不同的操作。
参考
[1]《精通Spring4.x企业应用开发实战》
[2]https://segmentfault.com/a/1190000011291179
[3]https://segmentfault.com/a/1190000007469968
Spring Framework 之AOP的更多相关文章
- 框架应用:Spring framework (二) - AOP技术
基础概念 线程中的方法栈 java程序虚拟机启动时会载入程序码,虚拟机会为每一条正在运行的线程生成一个方法调用栈,线程以方法运行为执行单位. AOP概念以及目标 AOP是面向切面编程,其实就是在不修改 ...
- Hello Spring Framework——面向切面编程(AOP)
本文主要参考了Spring官方文档第10章以及第11章和第40章的部分内容.如果要我总结Spring AOP的作用,不妨借鉴文档里的一段话:One of the key components of S ...
- 转-Spring Framework中的AOP之around通知
Spring Framework中的AOP之around通知 http://blog.csdn.net/xiaoliang_xie/article/details/7049183 标签: spring ...
- 【Spring Framework】Spring入门教程(六)Spring AOP使用
Spring的AOP 动态代理模式的缺陷是: 实现类必须要实现接口 -JDK动态代理 无法通过规则制定拦截无需功能增强的方法. Spring-AOP主要弥补了第二个不足,通过规则设置来拦截方法,并对方 ...
- 【Spring Framework】Spring入门教程(五)AOP思想和动态代理
本文主要讲解内容如下: Spring的核心之一 - AOP思想 (1) 代理模式- 动态代理 ① JDK的动态代理 (Java官方) ② CGLIB 第三方代理 AOP概述 什么是AOP(面向切面编程 ...
- 浅谈对Spring Framework的认识
Spring Framework,作为一个应用框架,官方的介绍如下: The Spring Framework provides a comprehensive programming and con ...
- 手动创建Spring项目 Spring framework
之前学习框架一直是看的视频教程,并且在都配套有项目源码,跟着视频敲代码总是很简单,现在想深入了解,自己从官网下载文件手动搭建,就遇到了很多问题记载如下. 首先熟悉一下spring的官方网站:http: ...
- Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->关于spring framework中的beans
Spring framework中的beans 1.概述 bean其实就是各个类实例化后的对象,即objects spring framework的IOC容器所管理的基本单元就是bean spring ...
- Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->使用spring framework的IoC容器功能----->方法一:使用XML文件定义beans之间的依赖注入关系
XML-based configuration metadata(使用XML文件定义beans之间的依赖注入关系) 第一部分 编程思路概述 step1,在XML文件中定义各个bean之间的依赖关系. ...
随机推荐
- javascript - 所有的视图属性和方法(offsetHeight、clientHeight、scrollHeight、innerHeight等)
注意:本文只简单的介绍了各个视图的属性和方法.如果想要知道兼容性或者更多,请至文章底部参考链接处. 本文内容分为五大部分: Window视图属性 innerHeight 和 innerWidth ou ...
- tf.nn.sigmoid_cross_entropy_with_logits 分类
tf.nn.sigmoid_cross_entropy_with_logits(_sentinel=None,,labels=None,logits=None,name=None) logits和la ...
- tf.nn.depthwise_conv2d 卷积
tf.nn.depthwise_conv2d( input, filter, strides, padding, rate=None, name=None, data_format=None ) 参数 ...
- 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
结构体和方法 一.值,指针和引用 我们现在有一段程序: package main import "fmt" func main() { // a,b 是一个值 a := 5 b : ...
- 007-函数-C语言笔记
007-函数-C语言笔记 学习目标 1.[了解]函数的分类 2.[掌握]函数的声明定义和调用 3.[掌握]函数的形参和实参 4.[掌握]带返回值的函数 5.[掌握]全局变量和局部变量 6.[了解]注释 ...
- 植物大战僵尸的代码如何使用python来实现
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:程序IT圈 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...
- 用Python做一个知乎沙雕问题总结
用Python做一个知乎沙雕问题总结 松鼠爱吃饼干2020-04-01 13:40 前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以 ...
- Redis入门使用 -- 个人总结
目录 什么是Redis? Redis 与其他 key - value 数据库的对比 Redis 能干什么 Redis的安装配置 Redis启动和连接 上面开启的是非守护线程下启动(独占模式),下面我们 ...
- MySQL笔记总结-TCL语言
TCL语言 事务 一.含义 事务控制语言 Transaction Control Language 事务:一条或多条sql语句组成一个执行单位,一组sql语句要么都执行要么都不执行 二.特点(ACID ...
- 最新VMware虚拟机安装Linux系统-CentOS(详细教程)
一.前言 最近有网友反应初学Linx不会安装,找了许多教程不是太全面,总会遇到一些要不是启动不了,要不是连不上网,各种问题,为了让大家能够顺利的安装,小乐写了一个非常详细的教程,让大家少入坑. 二.背 ...