AOP全称是Aspect Oriented Programing,通常译为面向切面编程。利用AOP可以对面向对象编程做很好的补充。

用生活中的改装车比喻,工厂用面向对象的方法制造好汽车后,车主往往有些个性化的想法,但是又不想对车进行大规模的拆卸、替换零件,这时可以买一些可替换的零件、装饰安装到汽车上,并且这些改装应该很容易拆卸,以避免验车时无法通过。

先看一个实际例子:有一个用户登录的方法,某一段时间内我们希望能够临时监控执行时间,但是又不想直接在方法上修改,用AOP方案实现如下。

UserService类

用sleep随机时间来模拟用户登录消耗的时间

  1. import java.util.Random;
  2. public class UserService {
  3. public void login(String userName, String password) {
  4. try {
  5. Thread.sleep(new Random(47).nextInt(100));
  6. } catch (InterruptedException e) {}
  7. System.out.println("UserService: 用户" + userName + "登录成功");
  8. }
  9. }

PerformanceMonitorUserService类,spring将把它当作UserService的替身(代理,Proxy)

  1. import java.util.concurrent.TimeUnit;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. @Aspect
  6. public class PerformanceMonitorUserService {
  7. @Around("execution(* login(..))")
  8. public void aroundLogin(ProceedingJoinPoint pjp) {
  9. String userName = pjp.getArgs()[0].toString();
  10. long begin = System.nanoTime();
  11. try {
  12. pjp.proceed();
  13. } catch (Throwable e) {
  14. e.printStackTrace();
  15. }
  16. long end = System.nanoTime();
  17. System.out.println("PerformanceMonitorUserService: 用户" + userName + "登录耗时" + TimeUnit.MILLISECONDS.convert((end - begin), TimeUnit.NANOSECONDS) + "毫秒");
  18. }
  19. }

applicationContext.xml,放在src根目录

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
  9. <aop:aspectj-autoproxy />
  10. <bean id="userService" class="demo.aop.UserService" />
  11. <bean class="demo.aop.PerformanceMonitorUserService" />
  12. </beans>

需要添加的jar包

测试代码

  1. import org.springframework.context.ApplicationContext;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. public class Client {
  4. public static void main(String[] args) {
  5. ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  6. UserService userService = (UserService)ctx.getBean("userService");
  7. userService.login("Tom", "123456");
  8. }
  9. }
  1. UserService: 用户Tom登录成功
  2. PerformanceMonitorUserService: 用户Tom登录耗时71毫秒

这样就保持了UserService业务的纯粹性,避免非业务代码和业务代码混合在一起。如果希望取消时间监控,只需要删除applicationContext里的<bean class="demo.aop.PerformanceMonitorUserService" />即可。

Spring是如何做到的呢?底层的两大功臣是JDK的动态代理和CGLib动态代理技术。我们以JDK的动态代理技术来重现上面的过程

UserService接口(JDK动态代理只能为接口创建代理,所以先抽象了一个接口)

  1. public interface UserService {
  2. void login(String userName, String password);
  3. }

实现类

  1. import java.util.Random;
  2. public class UserServiceImpl implements UserService {
  3. public void login(String userName, String password) {
  4. try {
  5. Thread.sleep(new Random(47).nextInt(100));
  6. } catch (InterruptedException e) {}
  7. System.out.println("UserService: 用户" + userName + "登录成功");
  8. }
  9. }

代理类

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.util.concurrent.TimeUnit;
  4. public class PerformanceMonitorUserService implements InvocationHandler {
  5. @Override
  6. public Object invoke(Object proxy, Method method, Object[] args)
  7. throws Throwable {
  8. long begin = System.currentTimeMillis();
  9. Object obj = method.invoke(target, args);
  10. long end = System.currentTimeMillis();
  11. System.out.println("PerformanceMonitorUserService: 用户" + args[0] + "登录耗时" + TimeUnit.MILLISECONDS.convert((end - begin), TimeUnit.NANOSECONDS) + "毫秒");
  12. return obj;
  13. }
  14. private Object target;
  15. public PerformanceMonitorUserService(Object target) {
  16. this.target = target;
  17. }
  18. }

测试代码

  1. import java.lang.reflect.Proxy;
  2. public class Client {
  3. public static void main(String[] args) {
  4. UserService target = new UserServiceImpl();
  5. PerformanceMonitorUserService handler = new PerformanceMonitorUserService(target);
  6. UserService proxy = (UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
  7. target.getClass().getInterfaces(), handler);
  8. proxy.login("Tom", "123456");
  9. }
  10. }

从上面的代码可以看出,AOP的原理就是创建代理,在运行时我们开发的业务逻辑类已经被替换成添加了增强代码的代理类,而Spring帮我们省略了这些繁琐和重复的步骤。

循序渐进之Spring AOP(1) - 原理的更多相关文章

  1. 【Spring】Spring AOP实现原理

    Spring AOP实现原理 在之前的一文中介绍过Spring AOP的功能使用,但是没有深究AOP的实现原理,今天正好看到几篇好文,于是就自己整理了一下AOP实现的几种方式,同时把代理模式相关知识也 ...

  2. Spring AOP 实现原理

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...

  3. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  4. Spring Aop底层原理详解

    Spring Aop底层原理详解(来源于csdn:https://blog.csdn.net/baomw)

  5. spring AOP底层原理实现——jdk动态代理

    spring AOP底层原理实现——jdk动态代理

  6. Spring AOP底层原理

    ------------------siwuxie095                                 Spring AOP 底层原理         AOP 即 Aspect Or ...

  7. jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  8. Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现

    前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...

  9. Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建

    上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...

随机推荐

  1. 腾讯WeTest《2017中国移动游戏质量白皮书》开放预约,再为国内手游把把脉

    产品为王,质量先行.如果说2016年是爆款手游相继崛起的一年,那么2017年则更像是打磨精品.建立生态的高手切磋之年.守住一个游戏的质量生命线,方能建立健康生态,方能在如火如荼的行业竞争中角逐到最后. ...

  2. nginx日常维护常用命令

    http://www.jb51.net/article/47750.htm 一.简明nginx常用命令 1. 启动 Nginx poechant@ubuntu:sudo ./sbin/nginx 2. ...

  3. 点击button1弹出form2,并在form2中点击button2来调用form1的方法

    链接地址:http://www.sufeinet.com/thread-1273-1-1.html   1.     private void button1_Click(object sender, ...

  4. Java I/O---字符与字节转换流---FileReader&FileWriter:

      public class SubTransStreamDemo { /** * @param args * @throws IOException */ public static void ma ...

  5. ArcGIS 网络分析[8] ArcObjects二次开发之底层网络分析开发

    基于现有的线要素类.转弯要素类(在地理数据库的要素数据集中),要用AO做两件事: 1. 创建网络数据集(使用Geodatabase类库) 2. 执行网络分析(使用NetworkAnalyst类库) 在 ...

  6. C#又能出来装个B了。一步一步微信跳一跳自动外挂

    PS:语言只是载体.思维逻辑才是王道 前天看见了个python的脚本.于是装python.配置环境变量.装pip.折腾了一上午,最终装逼失败. 于是进入博客园,顶部有篇文章吸引了我 .NET开发一个微 ...

  7. bzoj 4539: [Hnoi2016]树

    Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结 点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过 ...

  8. JS画几何图形之六【过直线外一点作垂线】

    样例:http://www.zhaojz.com.cn/demo/draw10.html 依赖:[点].[直线] //过直线外一点画垂线 function drawVerticalLine(point ...

  9. RabbitMQ教程(一) ——win7下安装RabbitMQ

    RabbitMQ依赖erlang,所以先安装erlang,然后再安装RabbitMQ; 下载RabbitMQ,下载地址: rabbitmq-server-3.5.6.exe和erlang,下载地址:o ...

  10. java获取properties配置文件值

    package me.ilt.Blog.util; import java.io.File; import java.io.FileInputStream; import java.io.IOExce ...