一、AOP

AOP 产生的背景

“存在即合理”,任何一种理论或技术的产生,必然有它的原因。了解它产生的背景、为了解决的问题有助于我们更好地把握AOP的概念。
软件开发一直在寻求一种高效开发、护展、维护的方式。从面向过程的开发实践中,前人将关注点抽象出来,对行为和属性进行聚合,形成了面向对象的开发思想,其在一定程度上影响了软件开发的过程。鉴于此,我们在开发的过程中会对软件开发进行抽象、分割成各个模块或对象。例如,我们会对API进行抽象成四个模块:Controller,Service,Gateway,Command.这很好地解决了业务级别的开发,但对于系统级别的开发我们很难聚焦。比如、对于每一个模块需要进行打日志、代码监控、异常处理。以打日志为例,我只能将日志代码嵌套在各个对象上,而无法关注日志本身,而这种现象又偏离了OOP思想。

为了能够更好地将系统级别的代码抽离出来,去掉与对象的耦合,就产生了面向AOP(面向切面)。如上图所示,OOP属于一种横向扩展,AOP是一种纵向扩展。AOP依托于OOP,进一步将系统级别的代码抽象出来,进行纵向排列,实现低耦合。
至于AOP的确切的概念,我不希望给出抽象复杂的表述,只需要了解其作用即可。

二、AOP的技术实现Proxy

AOP仅仅是一种思想,那为了让这种思想发光,必然脱离语言本身的技术支持,Java在实现该技术时就是采用的代理Proxy,那我们就去了解一下,如何通过代理实现面向切面

1.静态代理

 

就像我们去买二手房要经过中介一样,房主将房源委托给中介,中介将房源推荐给买方。中间的任何手续的承办都由中介来处理,不需要我们和房主直接打交道。无论对买方还是卖房都都省了很多事情,但同时也要付出代价,对于买房当然是中介费,对于代码的话就是性能。下面我们来介绍实现AOP的三种代理方式。
下面我就以买房的过程中需要打日志为例介绍三种代理方式
静态和动态是由代理产生的时间段来决定的。静态代理产生于代码编译阶段,即一旦代码运行就不可变了。下面我们来看一个例子

public interface IPerson {
public void doSomething();
}
public class Person implements IPerson {
public void doSomething(){
System.out.println("I want wo sell this house");
}
}

public class PersonProxy {
private IPerson iPerson;
private final static Logger logger = LoggerFactory.getLogger(PersonProxy.class); public PersonProxy(IPerson iPerson) {
this.iPerson = iPerson;
}
public void doSomething() {
logger.info("Before Proxy");
iPerson.doSomething();
logger.info("After Proxy");
} public static void main(String[] args) {
PersonProxy personProxy = new PersonProxy(new Person());
personProxy.doSomething();
}
}

通过代理类我们实现了将日志代码集成到了目标类,但从上面我们可以看出它具有很大的局限性:需要固定的类编写接口(或许还可以接受,毕竟有提倡面向接口编程),需要实现接口的每一个函数(不可接受),同样会造成代码的大量重复,将会使代码更加混乱。

2.动态代理

那能否通过实现一次代码即可将logger织入到所有函数中呢,答案当然是可以的,此时就要用到java中的反射机制


public class PersonProxy implements InvocationHandler{
private Object delegate;
private final Logger logger = LoggerFactory.getLogger(this.getClass(); public Object bind(Object delegate) {
this.delegate = delegate;
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
logger.info("Before Proxy");
result = method.invoke(delegate, args);
logger.info("After Proxy");
} catch (Exception e) {
throw e;
}
return result;
} public static void main(String[] args) {
PersonProxy personProxy = new PersonProxy();
IPerson iperson = (IPerson) personProxy.bind(new Person());
iperson.doSomething();
}
}

它的好处理时可以为我们生成任何一个接口的代理类,并将需要增强的方法织入到任意目标函数。但它仍然具有一个局限性,就是只有实现了接口的类,才能为其实现代理。

3.CGLIB

CGLIB解决了动态代理的难题,它通过生成目标类子类的方式来实现来实现代理,而不是接口,规避了接口的局限性。
CGLIB是一个强大的高性能代码生成包(生成原理还没研究过),其在运行时期(非编译时期)生成被 代理对象的子类,并重写了被代理对象的所有方法,从而作为代理对象。

public class PersonProxy implements MethodInterceptor {
private Object delegate;
private final Logger logger = LoggerFactory.getLogger(this.getClass()); public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
logger.info("Before Proxy");
Object result = methodProxy.invokeSuper(method, args);
logger.info("After Proxy");
return result;
} public static Person getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class); enhancer.setCallback(new PersonProxy());
return (Person) enhancer.create();
}
}

当然CGLIB也具有局限性,对于无法生成子类的类(final类),肯定是没有办法生成代理子类的。

以上就是三种代理的实现方式,但千成别被迷惑了,在Spring AOP中这些东西已经被封装了,不需要我们自己实现。要不然得累死,但了解AOP的实现原理(即基于代理)还是很有必要的。

五、最佳实践

那什么时候来使用AOP呢?我就私自定义一下:当一个功能和对象本身没有必段关系,重复出现在多个模块时,就应该用AOP来解耦。
有人总结了一下使用情景(参考, 是否认同自己决定):
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

5.1 日志

看了很多资料都是用日志做AOP的例子,其实这个并不太好,因为AOP很难完全实现log的行为,但对于某一类型的日志处理还是有用的。
例如:对Command的异常进行统一处理,对Controller层的请求进行打日志
请参考日志:https://github.com/ameizi/DevArticles/issues/152

5.2 代码性能测试

利用PerformanceMonitorInterceptor来协助应用性能优化, spring自带的

http://www.cnblogs.com/f1194361820/p/6626077.html
当然还有这个JamonPerformanceMonitorInterceptor

作者:oneWeekOneTopic
链接:https://www.jianshu.com/p/7fa3e6ebf0c1
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Spring AOP 理论的更多相关文章

  1. [转]彻底征服 Spring AOP 之 理论篇

    基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹的是, 这些概念经过了中文翻译后, 变得面目全非, 相同的一个术语, 在不同的翻译下, ...

  2. 彻底征服 Spring AOP 之 理论篇

    基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹的是, 这些概念经过了中文翻译后, 变得面目全非, 相同的一个术语, 在不同的翻译下, ...

  3. Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  4. [转]彻底征服 Spring AOP 之 实战篇

    Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个具体 ...

  5. 161110、彻底征服 Spring AOP 之 实战篇

    Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个具体 ...

  6. 彻底征服 Spring AOP 之 实战篇

      Spring AOP 实战 看了上面这么多的理论知识, 不知道大家有没有觉得枯燥哈. 不过不要急, 俗话说理论是实践的基础, 对 Spring AOP 有了基本的理论认识后, 我们来看一下下面几个 ...

  7. 关于 Spring AOP (AspectJ) 该知晓的一切

    关联文章: 关于Spring IOC (DI-依赖注入)你需要知道的一切 关于 Spring AOP (AspectJ) 你该知晓的一切 本篇是年后第一篇博文,由于博主用了不少时间在构思这篇博文,加上 ...

  8. springmvc 运行原理 Spring ioc的实现原理 Mybatis工作流程 spring AOP实现原理

    SpringMVC的工作原理图: SpringMVC流程 . 用户发送请求至前端控制器DispatcherServlet. . DispatcherServlet收到请求调用HandlerMappin ...

  9. 【转】Java Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

随机推荐

  1. Merge git repo into branch of another repo

    git 两个repo merge You can't merge a repository into a branch. You can merge a branch from another rep ...

  2. docker 实践

    https://doc.yonyoucloud.com/doc/docker_practice/etcd/etcdctl.html 启动http restful API docker批量映射端口 怎么 ...

  3. python简说(十)json模块

    常用模块: 一个python文件就是一个模块 1.标准模块,python自带的 2.第三方模块,需要安装 3.自己写的python文件 json,就是一个字符串 1.json转为字典 json_str ...

  4. 关于scrapy 使用代理相关问题

    在scrapy中使用代理时,我们不能保证每个代理都可用,难免出现代理ip错误的情况,如果代理ip出现错误设置一个请求超时和重新发送这个链接 在yield scrapy.Request时候加上一个参数: ...

  5. vba编程基础2

    安装office2010的时候, 最好是 完全安装/完整安装 , 这样可以查阅 excel的 "帮助文档" 帮助文档中包含了更多的/更详细的 参考信息. 普通模块无事件, 只有 子 ...

  6. 再谈 apache设置virtualhost + apache的一些相关设值

    首先 要弄懂究竟什么是, vitrual host: 虚拟主机, 他有什么用处? 就是说: 一台计算机(server)中, 通常只是放一个网站/站点 但是 如果机器紧张, 服务器性能比较好,或访问的站 ...

  7. facebook api之Ads Insights API

    The Ads Insights API provides API access for reporting and analytics purposes. When exclusively usin ...

  8. JS定时器时间日期钟表

    window.onload=function(){ setTime(); setInterval('setTime()',1000); } function checkTime(n){ if(n< ...

  9. layui upload 后台获取不到值

    后台获取不到值方法一: <script> layui.use('upload', function () { var upload = layui.upload; //执行实例 var u ...

  10. React生命周期执行顺序详解

    文章内容转载于https://www.cnblogs.com/faith3/p/9216165.html 一.组件生命周期的执行次数是什么样子的??? 只执行一次: constructor.compo ...