循序渐进之Spring AOP(1) - 原理
AOP全称是Aspect Oriented Programing,通常译为面向切面编程。利用AOP可以对面向对象编程做很好的补充。
用生活中的改装车比喻,工厂用面向对象的方法制造好汽车后,车主往往有些个性化的想法,但是又不想对车进行大规模的拆卸、替换零件,这时可以买一些可替换的零件、装饰安装到汽车上,并且这些改装应该很容易拆卸,以避免验车时无法通过。
先看一个实际例子:有一个用户登录的方法,某一段时间内我们希望能够临时监控执行时间,但是又不想直接在方法上修改,用AOP方案实现如下。
UserService类
用sleep随机时间来模拟用户登录消耗的时间
- import java.util.Random;
- public class UserService {
- public void login(String userName, String password) {
- try {
- Thread.sleep(new Random(47).nextInt(100));
- } catch (InterruptedException e) {}
- System.out.println("UserService: 用户" + userName + "登录成功");
- }
- }
PerformanceMonitorUserService类,spring将把它当作UserService的替身(代理,Proxy)
- import java.util.concurrent.TimeUnit;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- @Aspect
- public class PerformanceMonitorUserService {
- @Around("execution(* login(..))")
- public void aroundLogin(ProceedingJoinPoint pjp) {
- String userName = pjp.getArgs()[0].toString();
- long begin = System.nanoTime();
- try {
- pjp.proceed();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- long end = System.nanoTime();
- System.out.println("PerformanceMonitorUserService: 用户" + userName + "登录耗时" + TimeUnit.MILLISECONDS.convert((end - begin), TimeUnit.NANOSECONDS) + "毫秒");
- }
- }
applicationContext.xml,放在src根目录
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
- <aop:aspectj-autoproxy />
- <bean id="userService" class="demo.aop.UserService" />
- <bean class="demo.aop.PerformanceMonitorUserService" />
- </beans>
需要添加的jar包
测试代码
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class Client {
- public static void main(String[] args) {
- ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserService userService = (UserService)ctx.getBean("userService");
- userService.login("Tom", "123456");
- }
- }
- UserService: 用户Tom登录成功
- PerformanceMonitorUserService: 用户Tom登录耗时71毫秒
这样就保持了UserService业务的纯粹性,避免非业务代码和业务代码混合在一起。如果希望取消时间监控,只需要删除applicationContext里的<bean class="demo.aop.PerformanceMonitorUserService" />即可。
Spring是如何做到的呢?底层的两大功臣是JDK的动态代理和CGLib动态代理技术。我们以JDK的动态代理技术来重现上面的过程
UserService接口(JDK动态代理只能为接口创建代理,所以先抽象了一个接口)
- public interface UserService {
- void login(String userName, String password);
- }
实现类
- import java.util.Random;
- public class UserServiceImpl implements UserService {
- public void login(String userName, String password) {
- try {
- Thread.sleep(new Random(47).nextInt(100));
- } catch (InterruptedException e) {}
- System.out.println("UserService: 用户" + userName + "登录成功");
- }
- }
代理类
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.util.concurrent.TimeUnit;
- public class PerformanceMonitorUserService implements InvocationHandler {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- long begin = System.currentTimeMillis();
- Object obj = method.invoke(target, args);
- long end = System.currentTimeMillis();
- System.out.println("PerformanceMonitorUserService: 用户" + args[0] + "登录耗时" + TimeUnit.MILLISECONDS.convert((end - begin), TimeUnit.NANOSECONDS) + "毫秒");
- return obj;
- }
- private Object target;
- public PerformanceMonitorUserService(Object target) {
- this.target = target;
- }
- }
测试代码
- import java.lang.reflect.Proxy;
- public class Client {
- public static void main(String[] args) {
- UserService target = new UserServiceImpl();
- PerformanceMonitorUserService handler = new PerformanceMonitorUserService(target);
- UserService proxy = (UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), handler);
- proxy.login("Tom", "123456");
- }
- }
从上面的代码可以看出,AOP的原理就是创建代理,在运行时我们开发的业务逻辑类已经被替换成添加了增强代码的代理类,而Spring帮我们省略了这些繁琐和重复的步骤。
循序渐进之Spring AOP(1) - 原理的更多相关文章
- 【Spring】Spring AOP实现原理
Spring AOP实现原理 在之前的一文中介绍过Spring AOP的功能使用,但是没有深究AOP的实现原理,今天正好看到几篇好文,于是就自己整理了一下AOP实现的几种方式,同时把代理模式相关知识也 ...
- Spring AOP 实现原理
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Spring Aop底层原理详解
Spring Aop底层原理详解(来源于csdn:https://blog.csdn.net/baomw)
- spring AOP底层原理实现——jdk动态代理
spring AOP底层原理实现——jdk动态代理
- Spring AOP底层原理
------------------siwuxie095 Spring AOP 底层原理 AOP 即 Aspect Or ...
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现
前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...
- Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建
上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...
随机推荐
- oracle 主键自增 设置----杜恩德
<div id="topicList"> <div class="forFlow"> <div class = "pos ...
- SoapUI模拟soap接口返回不同响应(通过groovy脚本)
一.创建soap项目,输入wsdl文件,然后生成SOAP Mock Service,再生成测试用例,然后新建新的响应 WSDL文件:MathUtil.wsdl <?xml version=&qu ...
- 【python】lambda创建匿名函数
- 手动安装cloudera manager 5.x(tar包方式)详解
官方共给出了3中安装方式:第一种方法必须要求所有机器都能连网,由于最近各种国外的网站被墙的厉害,我尝试了几次各种超时错误,巨耽误时间不说,一旦失败,重装非常痛苦.第二种方法下载很多包.第三种方法对系统 ...
- TXDragon的大火题合集
还有三个题没写,弃疗了 Solution
- Linux第七节随笔 diff /uniq /stat
linux第七讲(上)1.diff link 作用:diff命令能比较单个文件或者目录内容.如果指定比较的是文件,则只有当输入为文本文件时才有效.以逐行的方式,比较文本文件的异同处. 如果指定比较的是 ...
- [经验分享]WebAPI中返回类型JsonMessage的应用
这是一个绝无仅有的好类型,一个你爱不释手的好类型,好了,不扯了,直接上干货. 相信大家都知道,在调用接口的时候返回Json数据已经成为一种不成文的标准,因为它的解析快,易读等优秀的特性,所以被绝大多数 ...
- C/C++调用Golang 一
C/C++调用Golang 一 (开发环境: 操作系统: windows 7 32位操作系统 C++: visual studio 2010 Golang:go version go1.9 windo ...
- java基础->循环
while循环 格式: while(条件表达式) { // 条件表达式其实就是一个结果为boolean类型的代码 循环体; } 执行流程: 先判断条件表达式的值, 如果为true就执行循环体,执行 ...
- Struts2-整理笔记(五)拦截器、拦截器配置
拦截器(Interceptor) 拦截器是Struts2最强大的特性之一,它是一种可以让用户在Action执行之前和Result执行之后进行一些功能处理的机制. 拦截器的优点 简化了Action的实现 ...