Spring中的AOP
什么是AOP?
(以下内容来自百度百科)
面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP与OOP的关系
很多人在初次接触 AOP 的时候可能会说,AOP 能做到的,一个定义良好的 OOP 的接口也一样能够做到,我想这个观点是值得商榷的。AOP和定义良好的 OOP 的接口可以说都是用来解决并且实现需求中的横切问题的方法。但是对于 OOP 中的接口来说,它仍然需要我们在相应的模块中去调用该接口中相关的方法,这是 OOP 所无法避免的,并且一旦接口不得不进行修改的时候,所有事情会变得一团糟;AOP 则不会这样,你只需要修改相应的 Aspect,再重新编织(weave)即可。 当然,AOP 也绝对不会代替 OOP。核心的需求仍然会由 OOP 来加以实现,而 AOP 将会和 OOP 整合起来,以此之长,补彼之短。
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改
变这些行为的时候不影响业务逻辑的代码
AOP具体实现
AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点(如Java),目前AOP具体实现有以下几个项目:
AspectJ (TM): 创建于Xerox PARC. 有近十年历史,成熟
缺点:过于复杂;破坏封装;需要专门的Java编译器。动态AOP:使用JDK的动态代理API或字节码Bytecode处理技术
Spring中的AOP
Spring对AOP编程提供了丰富的支持,是spring的两大核心(IOC和AOP)之一。Spring中的AOP是用java来实现的,在spring3.2.5官方文档对于AOP Proxies的描述如下:
1、Spring中默认使用标准的J2EE动态代理来生成AOP代理,这使得任何实现接口(或者一组接口)的类可以被代理。
2、Spring中的AOP也使用CGLIB代理,被代理的类必须是一个类而且是没有实现任何接口的。如果一个类没有实现接口,那么这样的类就可以用CGLIB来实现代理。
注:在Spring3.2.5中Spring-core中默认加入了CGLIB这个jar包,不需要自己再添加jar包了。如果要改变生成类的方式可以用如下的配置:proxy-target-class=‘true’
<aop:aspectj-autoproxy proxy-target-class="true" />
关于Spring的一些术语
- Aspect(切面):业务流程运行的某个特定的步骤,也就是应用运行过程的关注点。关注点可以横切多个对象,所以常常称为横切关注点。
- JoinPoint(连接点):程序运行过程中的一个点,例如一个方法的执行或者是异常的处理。在Spring AOP中,一个连接点总是代表者方法的执行。
- Advice(增强处理):AOP在特定的切入点中执行的增强处理。有"around," "before" 和 "after" 增强处理。
- PointCut(切入点):可以插入增强处理的连接点,简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就 变成 了切入点。Spring默认使用AspectJ的切入点语法。
- Introduction(引入):将方法或字段添加到被处理的类中。Spring允许你引用新的接口给任何的处理类。例如,你可以使用一个bean实现IsModified接口,以些来简化缓存。
- Target Object(目标对象):对象可以被一个或多个切面处理,因此也被称为增强对象,因为Spring的AOP是用运行时代理来实现的,所以目标对象总是一个代理对象。
- AOP proxy(AOP 代理):AOP框架创建的对象,简单的说代理对象就是对目标对象的增强。在Spring AOP框架中,即包括JDK动态代理,也包括CGLIB代理。
- Weaving(织入):连接目标对象和切面并生成一个增强的对象就是叫做织入,织入有两个种实现方式:编译时增强(如AspectJ)和运行时增强(如:CGLIB)。Spring AOP中使用的是运行时增强。
好了说了这么多的概念性的东西,来看代码吧
请看下面的类图 :
这是一个很简单的model2框架,里出全是业务实现代码并没有其它功能(里的类的注入用的是SpringIOC)。如果你想给某天对这个项目进行修改、重构的时候想在saveUser()的前后添加日志记录怎么办?
可以新手最先想到的是:直接添加不就行了。但是这是一个类,如果在以后的项目中10或者20个这样的类应该怎么做?你还要一个一个类的打开进行复制、修改。想想这都是一个噩梦!
如果我们用AOP的思想去思考呢?
我们可以把所有对数据库保存的方法就是以save*开头的方法当成一个切面对方法前后添加记录日志的功能。
解决方案:利用SpringAOP来实现 (Annotation方式)
以下代码在Myeclipse2013和JDK1.7 Spring3.2.5、JUnit4中测试通过
第一步:在Spring配置文件中启用Spring的AOP
- <?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:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
- <!-- 启用Annotation配置 -->
- <context:annotation-config />
- <!-- 告诉spring要扫描那个包下的类 -->
- <context:component-scan base-package="com.zxd"></context:component-scan>
- <!-- 启用AspectJ对Annotation的支持 -->
- <!-- 从Spring3.2开始在Spring-core中默认加入了CGLIB这个jar包,不需要自己再添加jar包了 proxy-target-class=‘true’
- 改变生成代理的方式 -->
- <aop:aspectj-autoproxy proxy-target-class="true" />
- <!-- Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。 -->
- <!-- 启用AspectJ支持 如果不使用XML Schema配置方式,可以使用在配置文件中添加以下的内容 -->
- <!-- <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"
- /> -->
- </beans>
注:请注意要先引用命名空间。
第二步:建立一个专用添加日志的拦截器
LogInterceptor .java
- package com.zxd.aop;
- import java.util.Arrays;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.annotation.Pointcut;
- import org.springframework.core.annotation.Order;
- import org.springframework.stereotype.Component;
- /**
- * 这个类是用来进行测试Spring的AOP切面类
- *
- * @author zhang
- *
- */
- @Aspect
- @Component
- public class LogInterceptor {
- // 声明一个切入点
- @Pointcut("execution(* com.zxd.service..*.add(..))")
- public void actionMethod() {
- };
- /*
- * 使用Before增强处理,通常要指定一个value值,用来指定切入点 这个增强处理发生在目标方法执行之前织入执行 注:无法访问目标方法的返回值
- */
- @Before("actionMethod()") public void addLog() {
- System.out.println("添加日志");
- }
- @After("actionMethod()")
- public void overLog2() {
- System.out.println("添加日志结束");
- }
- }
DAO层对应的实现类
UserDAOImpl.java
- package com.zxd.dao.impl;
- import org.springframework.stereotype.Component;
- import com.zxd.dao.UserDAO;
- import com.zxd.model.User;
- //默认使用类名首字母小写
- @Component("userDAO")
- public class UserDAOImpl implements UserDAO {
- public void save(User user) {
- //Hibernate
- //JDBC
- //XML
- //NetWork
- System.out.println("user saved!");
- }
- }
JUnit测试类 test.java
- package com.zxd.test;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import com.zxd.model.User;
- import com.zxd.service.UserService;
- //Dependency Injection
- //Inverse of Control
- public class UserServiceTest {
- @Test
- public void testAdd() throws Exception {
- ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- UserService us = context.getBean("userService",UserService.class);
- us.add(new User());
- }
- }
打印结果如下:
在Spring中增强处理有以下几种:
- Before增强处理:使用Before增强处理,通常要指定一个value值,用来指定切入点 这个增强处理发生在目标方法执行之前织入执行 注:无法访问目标方法的返回值
- AfterReturning增强处理:这个增强处理会在目标方法执行完成后被织入执行。returning:指定一个返回值的形参名,增强处理定义的方法可以通过该形参名来访问目标方法的返回值
注:AfterReturning还可以限定切入点的只匹配具有对应返回值类型的方法可以过滤一些其它返回值类型的方法,虽然AfterReturning可以访问目标参数的方法,但是不能改变目标方法的返回值。例如:
- /*
- * 对于AfterReturning增强处理的说明 这个增强处理会在目标方法执行完成后被织入执行
- * returning:指定一个返回值的形参名,增强处理定义的方法可以通过该形参名来访问目标方法的返回值
- * 注:AfterReturning还可以限定切入点的只匹配具有对应返回值类型的方法可以过滤一些其它返回值类型的方法
- * 虽然AfterReturning可以访问目标参数的方法,但是不能改变目标方法的返回值
- */
- /*
- @AfterReturning(pointcut = "actionMethod()", returning = "rvt") public
- void log(Double rvt) { System.out.println("获得目标方法的返回值:" + rvt);
- System.out.println("AferReturning方法执行》》》》》》》");
- }
- AfterThrowing 增强处理:主要用于处理程序中未处理的异常,throwing:指定一个形参,通过这个方法可以得到该形参名来访问目标方法中所抛出的异常对象
注:如果形参的类型是Throwable类型的话,是可以匹配任何的异常 。AfterThrowing与catch的区别:catch意味着完全处理该异常,如果catch块中没有重新抛出该异常,则该方法可以正常结束;而AfterThrowing处理虽然处理该异常,但它并不能完全处理该异常,该异常依然传播到上一级,最终导致程序的结束 - After增强处理:After增强处理与AfterReturning的区别:
- AfterReturning是在目标方法正确执行的情况下,才能被织入的
- After是无论目标方法是否正常的结束,都会被织入,相当于finnally中的方法
- 综上,所以After增强处理必须准备处理两种情况,正常返回和异常返回,(通常用于资源的释放)。
- Around增强处理:即可以在目标方法执行前织入动作,也可以在目标方法之后织入动作。与Before和AfterReturning的增强处理不同的是,Around增强处理甚至可以决定目标方法什么时候执行,如何执行,甚至完全可以阻止目标方法的执行 如果需要目标方法执行之前和之后共享某种状态数据,则应该考虑使用Around增强处理。关于Around使用的注意
- 定义一个Around方法,该方法的第一个参数必须为ProceedingJoinPoint类型(至少包含一个形参),在方法体内部调用ProceedingJoinPoint方法的 proceed方法才会执行目标方法。
- 当调用proceed方法传入的Object[ * ]对象方法,作为目标方法的参数时,如果传入的Object[]参数长度与目标方法所需要的参数不相等,或者类型不同都会出现异常
- 在Around方法中获取目标参数的方法 Object[] getArgs(): 返回执行目标方法时的参数 Signature , getSignature:返回被增强方法的相关信息 Object getTarget():返回被织入增强处理的目标对象 Object ,getThis:返回AOP框架为目标对象生成的代理对象
关于Spring中拦截器的先后顺序:
如果在一个目标对象的方法里有两个增强处理对这个方法进行了拦截那么Spring是如何对这个顺序进行排序的呢?
Spring采用和AspectJ一样的优先顺序来织入增强处理在“进入时”(Before)优先级高的先被织入,在“退出”(After)时, * 优先级高的会被后织入 Spring提供了如下两种方案来解决处理程序的优先级问题:
1、让切面类来实现org.springframework.core.Ordered接口,实现该接口只需要实现一个 int getOrder()方法, 该方法的返回值越小,则优先级越高
2、直接使用@Order Annotation来修饰一个切面类,里面指定一个int类型的值,值越小优先级越高,比如:@Order(2)
注:在before时优先级越高先进行织入,如果在After之后优先级越高越在最后进行织入。
关于XML的配置方式
XML代码如下:
- <?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:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
- <!-- 启用Annotation配置 -->
- <context:annotation-config />
- <!-- 告诉spring要扫描那个包下的类 -->
- <context:component-scan base-package="com.zxd"></context:component-scan>
- <bean id="log" class="com.zxd.aop.LogInterceptor"></bean>
- <!-- XML中对于AOP的配置 -->
- <aop:config>
- <!-- 声明一个切点 -->
- <aop:pointcut expression="execution(* com.zxd.service..*.test(..))"
- id="logPoint" />
- <!-- 定义切面类中的具体操作 -->
- <aop:aspect ref="log" id="logAspect">
- <!-- 定义一个before处理 -->
- <aop:before method="before" pointcut-ref="logPoint" />
- </aop:aspect>
- </aop:config>
- </beans>
Spring中的AOP的更多相关文章
- Spring中关于AOP的实践之概念
一.什么是AOP AOP:也称作面向切面编程 在分享几个概念执行我想先举个栗子(可能例子举得并不是特别恰当): 1.假如路人A走在大街上,被一群坏人绑架了: 2.警察叔叔接到报警迅速展开行动:收集情报 ...
- Spring中的AOP 专题
Caused by: java.lang.IllegalArgumentException: ProceedingJoinPoint is only supported for around advi ...
- spring中的AOP 以及各种通知 配置
理解了前面动态代理对象的原理之后,其实还是有很多不足之处,因为如果在项目中有20多个类,每个类有100多个方法都需要判断是不是要开事务,那么方法调用那里会相当麻烦. spring中的AOP很好地解决了 ...
- Spring学习笔记(四)—— Spring中的AOP
一.AOP概述 AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.O ...
- 2018.12.24 Spring中的aop演示(也就是运用aop技术实现代理模式)
Aop的最大意义是:在不改变原来代码的前提下,也不对源代码做任何协议接口要求.而实现了类似插件的方式,来修改源代码,给源代码插入新的执行代码. 1.spring中的aop演示 aop:面向方面编程.不 ...
- JavaWeb_(Spring框架)认识Spring中的aop
1.aop思想介绍(面向切面编程):将纵向重复代码,横向抽取解决,简称:横切 2.Spring中的aop:无需我们自己写动态代理的代码,spring可以将容器中管理对象生成动态代理对象,前提是我们对他 ...
- (五)Spring 中的 aop
目录 文章目录 AOP概念 AOP原理 AOP术语 **`Spring`** 中的 **`aop`** 的操作 使用 `AspectJ` 实现 `aop` 的两种方式 AOP概念 浅理解 aop :面 ...
- Spring 中基于 AOP 的 @AspectJ
Spring 中基于 AOP 的 @AspectJ @AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格. 通过在你的基于架构的 XML ...
- Spring 中基于 AOP 的 XML架构
Spring 中基于 AOP 的 XML架构 为了使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述: <?xml version="1.0" e ...
随机推荐
- Ubuntu phpmyadmin 缺少mcrypt扩展解决方法
之前在登陆phpmyadmin的时候,会出现警告说缺少mcrypt扩展的错误,一直没去解决这个问题,觉得没什么影响就算了. 今天谷歌了一下,原来是php5没有启用mcrypt模块. sudo ph ...
- Python核心编程2第一章课后练习
1-1 在windows下的安装方法在网上下载python2.7直接安装到C盘1)在系统变量中找到path. 2)编辑path值,添加你安装的python路径,C:\Python27. 3)检验pyt ...
- C++实现红黑树,仿STL封装
//RB_Tree.hpp //The code of red black trees //2011/12/31 by Adoo // The foundation :http://www.roadi ...
- 运用MyEclipse插件(link方式注意点)
Windows7 中 MyEclipse 安装位置下,有以下两个目录: MyEclipse 10 Common 注意点一 Common 下的子目录是 plugins 和 features : 而在 M ...
- 除了创建时指定窗口位置之外,还有3种移动窗口位置的办法(移动的同时往往可以改变窗口大小)(SetWindowPos最有用,它有许多标志位)
首先,在创立窗口对象的时候,CreateWindowEx就可以指定窗口的位置.除此之外,还有三种方法可以改变窗口的位置: procedure TWinControl.CreateWindowHandl ...
- Linux 配置多IP
这里以红帽Linux为例.假定原系统已配置一个IP,地址为:192.168.20.140,配置文件路径/etc/sysconfig/network-script/ifcfg-eth0.现在需要配置一个 ...
- 【Linux】鸟哥的Linux私房菜基础学习篇整理(三)
1. gzip [-cdtv#] filename:压缩.参数:-c:将压缩的数据输出到屏幕上,可通过数据重定向进行处理:-d:解压缩的参数:-t:可以用来检验一个压缩文件的一致性,查看文件有无错误: ...
- C# Hashtable中存入数组、List
哈希表中存入数组示例代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; ...
- phpMyAdmin <= 4.0.4.1 import.php GLOBALS变量注入漏洞
漏洞版本: phpMyAdmin <= 4.0.4.1 漏洞描述: CVE(CAN) ID: CVE-2013-4729 phpmyadmin是MySQL数据库的在线管理工具,主要功能包括在线创 ...
- uboot从SD卡烧写内核和文件系统
环境:ubuntu 13.04一.首先制作sd启动盘: 插入SD卡 sudo dd iflag=dsync oflag=dsync if=tiny210v2-uboot.binof=/dev/m ...