Sping-AOP
2015-08-29 11:06
330 查看
Spring AOP
在Spring Framework简介中介绍了spring的整体框架,在SpringIOC中介绍了spring的核心功能,那么spring framework中,另外一个比较重要的模块就是Spring AOP。那么,什么是AOP呢?AOP是Aspect-Oriented Programming的缩写,是一种不同与面向对象(Object-Oriented
Programming (OOP))的编程方式。其主要的设计目的是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码,也就是无入侵式的编程。
AOP概念
要理解AOP,就必须要对一些关键词非常了解,下面列表是AOP中常用的一些关键词:Aspect,切面,可以包含多个类,多个方法组成,由Advice,Pointcut等元素组成。在常见的事务管理中,我们通常会使用如下的配置:
<aop:config> <aop:pointcut id="service" expression="execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))" /> <aop:advisor pointcut-ref="service" advice-ref="advice" /> </aop:config>
Join Point,切入点,通常指的是一个方法,也就是在调用时,匹配Pointcut表达式指定的方法。
Advice,增强点,通常是在执行某个方法时,根据需要来执行某种操作。比如在发送邮件前,先要记录下日志,这种就是属于前置增强。发送邮件后发送通知给管理员,这种就属于后置增强点。
Pointcut,切入点集合,通常是一个表达式,比如:execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))。
Introduction:为目标对象声明一些字段或者方法,通常是声明接口。
Target
Object:目标对象,需要使用AOP增强的目标类
AOP
proxy:代理对象,由spring framework动态创建
Wearving:根据配置信息,在编译、加载类、运行时,动态的代码组织起来。
Advice的类型
在AOP的规定中,Advice的类型主要有:Before Advice,After Advice,Around Advice,After returning Advice,After throwing advice。如下图所示:
通过上面的图片,我们可以看出,这些定义都是围绕着代码的执行时间点来命名的,比如Before advice就是在执行Join
point前插入一段执行代码,After advice就是在执行成功后插入一段代码,After throwing advice就是在抛出异常后执行一段代码,Around advice就是把Before advice、After advice同时都执行。
AOP的实现原理
介绍完AOP的一些基本概念后,下面我们来探讨下AOP的实现。在设计模式中,有一种设计模式叫做代理设计模式。那么什么是代理设计模式呢?在生活中,该设计模式也随处可见。比如我们经常在淘宝上海淘,通常会去某家海淘店铺购买物品,然后店主帮我们去购买,发货给我们。如下图:在这里面,我们就是购买者,换成java角度而言,就是调用者;海淘店主就是代理对象(Proxy);实际货品提供者就是目标对象(Target
object)。通过代理模式,即可为Target object提供一些而外的功能,也就是上文所述的Advice。在这里面,可能大家都很疑惑,Advice是在代码实现层是如何动态加入进去的呢?这个其实也不难,在JDK中,对于动态代理模式提供的相应的API,可以动态的生成代理对象。如下代码所示:
<span style="font-size:18px;">/** * 接口PersonService,Target Object的接口定义,因为jdk只能生成接口的动态代理对象 * */ public interface PersonService{ public void save(); public void add(); public void update(); } /** * 要被代理的对象,也就是Target object * */ public class PersonServiceImpl implements PersonService{ private String user; public PersonServiceImpl(String user){ this.user = user; } //给外部提供接口 public String getUser(){ return user; } public void add(){ System.out.println("I am the PersonServiceImpl add() method"); } public void update(){ } public void save(){ } } </span>
<span style="font-size:18px;">import java.lang.reflect.*; /*在这里,直接让工厂实现了InvocationHandler,所以在调用createProxy()的时候,给Proxy.netProxyInstance()的第三个参数传进去了一个this。如果要把工厂和InvocationHandler解耦,可以重新顶一个实现InvocationHandler的类*/ public class JDKProxyFactory implements InvocationHandler{ //要代理的目标对象 private Object target; public JDKProxyFactory(Object obj){ target = obj; } //生产一个代理对象 public Object createProxy(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //当客户端调用目标对象进行工作的时候,就会被这个代理对象拦截到 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ //将目标对象向下转型 PersonServiceImpl bean = (PersonServiceImpl)target; //这里进行拦截 if(bean.getUser() != null && !"".equals(bean.getUser())){ //调用目标对象的方法 Object result = method.invoke(target, args); return result; }else{ System.out.println("sorry, you have no limitation to access this resource!!!"); return null; } } }</span>
在代码中,我们发现创建一个代理对象并不难,主要步骤如下:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
但是凡事都有弊端,java提供生成代理对象的API其实是不支持为类生成代理对象,只能给接口生成代理对象。所以在这里面,Spring AOP借助于Aspectj代理框架来完成该任务,实现类的动态代理对象的生成。有了代理模式,动态代理对象的生成,那么面向切面大功告成了。
AOP的应用
在spring framework中,AOP通常和IOC模块一同使用,借助AOP来实现声明式的事务。下面我们通过配置一个声明式的事务,通过观察日志来了解AOP的工作原理。首先我们在IOC中声明如下几个bean:<span style="font-size:18px;"> <!-- 对数据源进行事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" /> <!-- 配置事务传播特性 --> <tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="del*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="find*" propagation="REQUIRED" /> <tx:method name="get*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="apply*" propagation="REQUIRED" /> <tx:method name="list*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!--配置一个AOP切面,同时指定advice、pointcut,那么一个最简单的AOP就配置完毕。--> <aop:config> <aop:pointcut id="service" expression="execution(* com.ckg.luohong.biz.*.*.service.impl.*.*(..))" /> <aop:advisor pointcut-ref="service" advice-ref="advice" /> </aop:config></span>
单元测试类,这里面使用spring + junit的方式来运行。
<span style="font-size:18px;">package com.skg.luohong.biz.ou.system; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import com.skg.luohong.biz.ou.system.service.IDaysUserService; /** * * @author 骆宏 * @date 2015-08-29 10:27:09 * @author 846705189@qq.com * @author 15013336884 * @blog http://blog.csdn.net/u010469003 * */ @ContextConfiguration({"classpath:conf/spring-mybatis.xml", "classpath:conf/spring.xml"}) @RunWith(SpringJUnit4ClassRunner.class) @Transactional public class DaysUserTest{ @Autowired private IDaysUserService service; @Test public void testCrud(){ System.out.println(service.countAll()); System.out.println(service.findAll()); } } </span>
日志输出,下面为IOC容器初始化bean的过程,下面的日志为IOC创建Pointcut, Advice,TransactionManager bean的过程:
<span style="font-size:18px;">2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean 'sqlSessionFactory' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.mybatis.spring.mapper.MapperScannerConfigurer#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating shared instance of singleton bean 'transactionManager' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean 'transactionManager' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Eagerly caching bean 'transactionManager' to allow for resolving potential circular references 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'dataSource' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Invoking afterPropertiesSet() on bean with name 'transactionManager' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean 'transactionManager' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating shared instance of singleton bean 'advice' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean 'advice' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Eagerly caching bean 'advice' to allow for resolving potential circular references 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'transactionManager' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Creating instance of bean '(inner bean)' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [save*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [del*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [update*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [add*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [countAll*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [find*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [get*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [insert*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [apply*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource]-[DEBUG] Adding transactional method [list*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Finished creating instance of bean '(inner bean)' 2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Invoking afterPropertiesSet() on bean with name 'advice'</span>
下面的日志展示了AOP对事物的拦截:
<span style="font-size:18px;">2015-08-29 10:39:01 [org.springframework.beans.factory.support.DefaultListableBeanFactory]-[DEBUG] Returning cached instance of singleton bean 'transactionManager' 2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Creating new transaction with name [testCrud]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' 2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Acquired Connection [com.mysql.jdbc.JDBC4Connection@3ff1b8db] for JDBC transaction 2015-08-29 10:39:01 [org.springframework.jdbc.datasource.DataSourceTransactionManager]-[DEBUG] Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@3ff1b8db] to manual commit 2015-08-29 10:39:01 [org.springframework.test.context.transaction.TransactionalTestExecutionListener]-[DEBUG] No method-level @Rollback override: using default rollback [true] for test context [TestContext@7475b962 testClass = DaysUserTest, testInstance = com.skg.luohong.biz.ou.system.DaysUserTest@5cde0ca9, testMethod = testCrud@DaysUserTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@76115ae0 testClass = DaysUserTest, locations = '{classpath:conf/spring-mybatis.xml, classpath:conf/spring.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]] 2015-08-29 10:39:01 [org.springframework.test.context.transaction.TransactionalTestExecutionListener]-[INFO] Began transaction (1): transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@19dde4d9]; rollback [true]</span>
总结
AOP是一种不同于OOP的编程风格,在事务管理,日志管理等需要横跨多个类的地方,有着非常广泛的用途。AOP本身的概念其实并不难,如果对代理设计模式掌握的较好,那么AOP就更加不在话下。最近这段时间,工作较为充实,抽空把Spring AOP的知识整理了一番,受益匪浅。相关文章推荐
- Centos6.6下SVN配合Apache
- linux 学习笔记之常用命令二
- 维护通讯录的方法及群组通讯录 管理平台
- 关于tomcat出现闪退问题
- Linux平台上apache服务器的搭建和应用
- linux学习笔记之常用命令一
- poj 2186 Popular Cows 【强连通分量Tarjan算法 + 树问题】
- Nginx防爬虫或限制浏览器访问
- linxu shell iconv 转码问题
- Node.js HelloWord Linux(Ubuntu)
- umask--文件的默认权限
- property 和 synthesize
- 记一次Linux服务器上查杀木马经历
- 将linux默认python升级到2.7.4版本
- linux学习笔记----进程管理----杀死进程and 把进程放入后台
- Linux字符设备驱动程序编译进入内核的方法
- linux命令(2):gzip命令
- linux命令(1):wget命令
- opencv-模板匹配的例子
- 玩转linux第四天之用户及组管理类相关命令(二)