梳理一下我理解的aop
在看了很多网上的资料和记录之后,我大概捋了下SpringAOP的各种阶段:
- 基本的advice编程,利用ProxyFactory拿代理类
- 利用spring把ProxyFactory,advice等bean化
- 切面编程,在advisor中编写advice和pointcut
- SpringAOP实现自动代理,也就是说不用闲式地配置ProxyFactory,和从ProxyFactory中拿代理类了,SpringAOP帮你解决了这些东西。
- AspectJ风格的注解式的Aspect编程
先记录一下AOP的几个概念:
AOP:
首先,AOP是Aspect-Originted Programming,即面向切面编程。我觉得可以这样地简单理解,aop就是为了你更清楚的逻辑,让你的业务逻辑代码更清晰,不用去想其他事,像日志啊,权限啊这些和业务逻辑无关的东西。那要怎么用这些又很重要的功能呢?我们不用改变原来的代码,只要在另一个个地方,把这些要附加的功能打包好,然后运行的时候切进你要用这些功能的地方,aop其实大概就是这样。好的那就来看看下面的几个名词:
advice:
翻译成增强、通知,其实就是你要做的东西,你要切到你的代码中的功能。aop框架会把advice模拟成拦截器interceptor。
Join Point:
连接点,就是你可以加入功能,加入advice的地方。Spring只支持在方法或者是抛出异常的地方建立使用通知,也就是说这些地方都可以是joinpoint,像其他aop像aspectJ还可以让你在构造器或属性注入时加入advice。
Pointcut:
刚刚JoinPoint我们说了哪些地方可以用advice,但不是所有地方都要用advice啊,所以就有了这个pointcut,就是加上条件的joincut。在写代码的时候,会用类似正则表达式的方式去选择那些joinpoint来作为pointcut。
Aspect:
切面,这个可是个很重要的概念哦。其实就是advice+pointcut。Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:
- 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
- 如何在 advice 中编写切面代码.
Introduction:
翻译成引入,就是引入允许我们向现有的类添加新方法或属性,从而无需修改这些现有类的情况下,让他们具有新的行为和状态。Spring AOP 允许我们为 目标对象 引入新的接口(和对应的实现). 例如我们可以使用 introduction 来为一个 bean 实现 IsModified 接口, 并以此来简化 caching 的实现.
Target:
目标,要加入新功能,新advice的目标类。就是不知道什么情况然后等等要被我们切入新功能的那个类。
AOP proxy:
一个类被 AOP 织入 advice, 就会产生一个结果类, 它是融合了原类和增强逻辑的代理类. 在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象. 可以说,spring aop就是用动态代理来实现的,我理解的,就是我们要在一个target类中加入新功能嘛,就通过一个代理类来实现,代理类是原来目标类+advice的结合,可以想象成变强了的target类的替身。那么要怎样才能成为这个替身呢?要么就实现一样的接口(jdk动态代理的原理),要么就继承target类称为它的子类(CGLIB的原理)。
Weaving:
织入,将aspect和其他对象连接起来,并创建adviced object的过程。(可以把weaving理解成动词,introduction理解成名词)。根据不同的技术,weaving的方式有三种:编译器织入,要求有特制的编译器;类装载期织入,这需要特殊的类装载期器;动态代理织入,在运行期间为target添加advice的方式。
一、先是非常原始的advice实现
这里我们写一个非常原始的,连spring bean都不用的例子来帮助理解aop。写的是advice增强,用的是jdk代理的方式,也就是接口代理。但要先引入Spring aop的相关jar包
思路就是,要加强的类实现一个接口,然后advice作为加强类,要实现SpringAOP提供的Advice相关接口。然后通过ProxyFactory来拿代理类,往代理类中addAdvice来达到加强的效果。
这里我们有个Greeting接口。
package com.stuPayment.aopTest; public interface Greeting {
public void sayHello(String name);
public int saySth();
}
然后它的实现:
package com.stuPayment.aopTest; public class GreetingImpl implements Greeting {
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub
System.out.println("hello, " + name);
} @Override
public int saySth() {
// TODO Auto-generated method stub
System.out.println("this is say sth");
return 213;
} public int sayMorning(String name) {
System.out.println("good morning, " + name);
return 123;
} public void sayAfternoon(String name) {
System.out.println("good afternoon, " + name);
}
}
然后是我们的advice增强类,要这里我们用的是MethodBeforeAdvice接口和AfterReturningMethod,表示着advice是加在方法调用前还是方法返回之后。
package com.stuPayment.aopTest.advice; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice; public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice{
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("this is after");
} @Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
// TODO Auto-generated method stub
System.out.println("this is before");
} }
然后测试类:
package com.stuPayment.aopTest; import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory; import com.stuPayment.aopTest.advice.GreetingAroundAdvice;
import com.stuPayment.aopTest.advice.GreetingBeforeAndAfterAdvice; public class Test1 {
@Test
public void demo1() {
ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
proxyFactory.setTarget(new GreetingImpl());//注入目标类对象
proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice());//添加前置加强和后置加强
//proxyFactory.addAdvice(new GreetingAroundAdvice());//添加前置加强和后置加强
Greeting greeting = (Greeting)proxyFactory.getProxy();//从代理工厂中获取代理
greeting.sayHello("Ben");//调用代理的方法
greeting.saySth();
}
}
结果:
所以大概思路就是,编写一个增强类去实现Spring aop提供的几个advice的接口(其实这些接口就是决定在接入点的哪个位置加入新advice),然用aop给的ProxyFactory,先设定一个target,然后addAdvice来加入你要切入的功能,然后你就可以通过这个代理工厂来获得一个代理类(对应接口的),一个加强后的adviced object,调用它里面方法就会看到advice的效果。
然后SpringAop给了好几种Advice的增强接口:
二、一般,到了后面,我们会把这个Proxy的配置加入Spring的配置文件中
这个greetingAroundAdvice就像我们刚刚那个GreetingBeforeAndAfterAdvice一样是个实现了一个advice接口的增强类。
这里再介绍一下一个Introduction Advice,来看看jdk动态代理和CGLIB类代理的区别。
上面说到,这个引介增强Introduction Advice是一种特殊的增强,之前的连接点都是方法级别的,而这个是类级别的,也就是对类的加强。
引入增强Introduction Advice的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。
先定义一个新接口Love:
然后定义授权引入增强类:
这个DelegatingIntroductionInterceptor就是一个引入的advice类,继承它就有这种advice的能力。
然后是Proxy的配置:
proxyTargetClass属性表示是否代理目标类,默认是false,也就是代理接口,上面一个例子的配置就是没有这一项属性所以用JDK动态代理,现在是true即使用CGLib动态代理。
然后看测试方法里面:
首先看到,从代理里面拿这个GreetingImpl的时候,不是像以前一样:Greeting greeting = (Greeting).......
而是直接用实现类GreetingImpl来拿。因为现在是代理目标类而不是接口类。
然后是这里的Love love = (Love)greetingImpl是将目标类强制向上转换成了Love接口,但注意,我们并没有把这个greetingImpl实现这个Love接口哦,这就是引用增强(DelegatingIntroductionInterceptor)的特性——“接口动态实现的”功能。所以display()方法可以由GreetingImpl的对象来调用,只需要强行转换接口就行了。
三、好了,在advice的层次下的编程之后,就到了后面aspect切面的编程了:
所谓切面,其实就是advice+pointcut,我们在切面中一般要做的就是定义要加的功能还有正则表达式确定要拦截的方法。
这里开始内容就很多了,也有点乱,这里稍微捋一下,迟点会通过阅读相关的书籍来理清楚关系。
切面编程一开始,我们可以通过springAOP提供的切面类RegexpMethodPointcutAdvisor来配置切面。一样还是刚刚的实现了Greeting的GreetingImpl作为target,配置文件如下:
这上面的proxy中的配置中的InterceptorNames,不再是之前的advice加强了,而是一个定义好的切面。我们看这个greetingAdvisor的bean的配置,可以看到一个属性是advice,而另一个是pattern,其实就相当于要加强的功能和pointcut。
再往后发展,proxy也不用怎么写配置了,有了个叫aop自动代理的东西,大概就是spring框架自动生成代理:
配置文件:(属性optimize为true,表示,如果target有接口,就用jdk动态代理,若谷target没有接口,就用CGLib动态代理)
还有测试代码:
此时因为是自动代理,getBean()的值不再是原来的代理id(greetingProxy),而是目标类GreetingImpl的bean的id,这同样也是个代理类,只是自动代理,隐藏了代理的工作和代码。
再往后,就到了AspectJ风格的切面编程,用注解就可以完成切面的编程,大大节省了配置的时间。
先是利用<aop:config>元素声明切面的方法,配置文件类似于下图的xml:
也可以直接使用@Aspect注解,只需要配置文件中简单得配置一下自动代理:
<context:component-scan base-package="demo.spring"/> <!-- 用@Component自动发布bean,需要配置这个元素。 --> <aop:aspectj-autoproxy /> <!-- 使用@AspectJ及其它AOP注解需要配置,否则无法使用注解;@AspectJ注解,将@Component自动发布出来的"interceptor" bean转换为一个aspectj切面,而@Pointcut、@Before、@After、@Around等注解,功能与在xml文件中配置是一样的;@Pointcut注解下面的方法内容无意义,只是要求一个相应方法提供注解依附。 -->
注解只能在使用能获得源码的场景,如果不能获取源码,则只能通过xml配置的形式,将指定的对象配置成拦截器,对指定的目标进行拦截;因此,通过xml文件配置,而不是注解,是更加通用的方式。
然后再下一篇博客,会记录如何在Springboot用AspectJ风格的注解来用SpringAOP实现一个日志记录的功能。
参考过的博客:
https://blog.csdn.net/h525483481/article/details/79625718
https://blog.csdn.net/icarus_wang/article/details/51737474 讲增强类型的
https://www.cnblogs.com/jacksonshi/p/5863313.html
梳理一下我理解的aop的更多相关文章
- 深入理解Spring AOP之二代理对象生成
深入理解Spring AOP之二代理对象生成 spring代理对象 上一篇博客中讲到了Spring的一些基本概念和初步讲了实现方法,当中提到了动态代理技术,包含JDK动态代理技术和Cglib动态代理 ...
- ctr预估论文梳理和个人理解
问题描述 ctr的全称是click through rate,就是预估用户的点击率,可以用于推荐系统的ranking阶段.ctr预估可以理解为给用户的特征.item的特征以及context的特征(比如 ...
- spring 理解Spring AOP 一个简单的约定游戏
应该说AOP原理是Spring技术中最难理解的一个部分,而这个约定游戏也许会给你很多的帮助,通过这个约定游戏,就可以理解Spring AOP的含义和实现方法,也能帮助读者更好地运用Spring AOP ...
- 正确理解Spring AOP中的Around advice
Spring AOP中,有Before advice和After advice,这两个advice从字面上就可以很容易理解,但是Around advice就有点麻烦了. 乍一看好像是Before ad ...
- Spring 梳理 - filter、interceptor、aop实现与区别 -第二篇
spring mvc中的Interceptor可以理解为是Spring MVC框架对AOP的一种实现方式.一般简单的功能又是通用的,每个请求都要去处理的,比如判断token是否失效可以使用spring ...
- 曹工说Spring Boot源码(21)-- 为了让大家理解Spring Aop利器ProxyFactory,我已经拼了
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- 如何简单理解spring aop和事务
用比喻的方法理解吧: 初学者的理解,仅仅为了个人好记 aop:由三部分组成:工具箱,工人,为工人分配工具 tx事务:由四部分组成:管理者,制度,工人,向工人通知管理制度 为什么这样理解呢?个人觉得好 ...
- 理解Spring AOP的实现方式与思想
Spring AOP简介 如果说IOC是Spring的核心,那么面向切面编程就是Spring最核心的功能之一了,在数据库事务中,面向切面编程被广泛应用. AOP能够将那些与业务无关,却为业务模块所共同 ...
- 轻松理解 Spring AOP
目录 Spring AOP 简介 Spring AOP 的基本概念 面向切面编程 AOP 的目的 AOP 术语和流程 术语 流程 五大通知执行顺序 例子 图例 实际的代码 使用 Spring AOP ...
随机推荐
- CentOS系统文件和目录管理相关的一些重要命令
我们都知道,在Linux系统中,基本上任何我们需要做的事都可以通过输入命令来完成,所以在Linux系统中命令非常的多,我们不可能也没必要记住所有的这些命令,但是对于一些常用的命令我们还是必须要对其了如 ...
- C++打印变量地址
%p专门用来打印变量的以十六进制表示的地址: #include<iostream> using namespace std; int main() { ; printf("a的地 ...
- CI核心文件分析之基准测试类 (Benchmark.php)
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * CodeIgniter * * ...
- Django_model进阶
Django-model进阶 QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Ent ...
- codevs 2456栅栏
传送门 2456 栅栏 2006年省队选拔赛四川 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Maste 题目描述 Description 农夫约翰打算建立一个栅 ...
- C++之函数适配器--绑定器bind原理图解
转自:http://www.cnblogs.com/xusd-null/p/3698969.html#3081606 本文解释了bind 是如何工作的.为了清晰,图中的语法作了一些简化(例如,省略函数 ...
- DataWindow.NET 控件 实现点击列头排序
1.定义字段 Boolean ib_SetSort = true; string is_SortType = " ...
- WebBrowser内嵌页面的跨域调用问题
很早之前我写过一篇Blog:网页通过External接口与WebBrowser交互,文中的交互其实只介绍了JS调用C++的部分,而C++调用JS由于微软自己的例子太多,那篇文章就没介绍,不过我最近遇到 ...
- chrome提示您的连接不是私密连接
在实验室登陆qq邮箱总是提示连接不是私密,感觉是上级路由器配置的问题,或者是被监控了.反正邮箱也没啥东西,干脆直接删除这个提示.解决方法如下,测试可用. 解决:我想打不开百度网页是因为chrome强制 ...
- Codeforces - 65D - Harry Potter and the Sorting Hat - 简单搜索
https://codeforces.com/problemset/problem/65/D 哈利波特!一种新思路的状压记忆化dfs,记得每次dfs用完要减回去.而且一定是要在dfs外部进行加减!防止 ...