spring随笔(二) AOP
AOP(Aspect-Oriented Programming 面向切面编程)
aop(Aspect-Oriented Programming)也就是面向切面编程,作为面向对象编程的一种补充。aop从程序运行角度考虑程序的流程,提取业务处理过程的切面。
1,AOP中的概念
Aspect(切面): 是通知(Advice)和切入点(Pointcut)的结合,通知和切入点共同定义了关于切面的全部内容---何时、何地、做什么。
Advice(通知): 所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能,通知定义了切面的”何时”和”做什么”)
Pointcut(切入点): 所谓切入点是指我们要对那些joinpoint进行拦截的定义. 切入点就定义了”何地”.JoinPoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.Target(目标对象): 代理的目标对象Weaving(织入): 是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象Introduction(引入): 在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.AOP代理:springAOP创建的代理对象。也可以说是对目标对象的加强。spring中的代理可以是jdk的动态代理,也可以是CGLIB代理。前者为实现接口的对象进行代理,后者是为了不实现接口的对象的代理。spring会根据具体的类是否有接口来以不同的方式处理代理过程。
2,spring中的两种代理(内部实现)
1,JDK的动态代理
JDK的动态代理主要的核心方法是:java.lang.reflect.Proxy类的 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException该方法。
其中参数 :
loader
- 定义代理类的类加载器 、interfaces - 代理类要实现的接口列表、h
- 指派方法调用的调用处理程序 。其中的InvocationHandler为java.lang.reflect下面的接口
返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
InvocationHandler接口只有一个方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable
代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
如下要在一个CustmerService的保存更新操作上面加上事务处理。
ICustomeService接口:
package com.xiaohui.proxy; public interface ICustomeService {void save(Customer c);void update(Customer c); }
CustomerServiceImplCustomerServiceImpl类
package com.xiaohui.proxy; public class CustomerServiceImpl implements ICustomeService{public void save(Customer customer) {System.out.println("CustomerServiceImpl......save..."+customer.getName());}public void update(Customer c) {System.out.println("CustomerServiceImpl......update..."+c.getName());} }
MyInvocationHandlerFactoryMyInvocationHandlerFactory类
package com.xiaohui.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyInvocationHandlerFactory implements InvocationHandler {//目标对象private Object target;/***被代理的对象* @param target* @return 代理对象*/public Object getProxyInstance(Object target){this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//proxy参数一般不用//前置的处理逻辑程序System.out.println("session.beginTranscation()");//回调目标对象的方法 类似于filter中的chain.doFilter(request,response);Object obj = method.invoke(target, args);//后续的处理逻辑程序System.out.println("session.getTranscation().commit()");return obj;} }
测试类:
@Testpublic void testProxy() throws Exception {ICustomeService service = new CustomerServiceImpl();Customer customer = new Customer();customer.setName("张珊");MyInvocationHandlerFactory factory = new MyInvocationHandlerFactory();ICustomeService serviceProxy =(ICustomeService) factory.getProxyInstance(service);serviceProxy.save(customer);System.out.println("-------------------------------");serviceProxy.update(customer);}
打印结果:
session.beginTranscation()
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
-------------------------------
session.beginTranscation()
CustomerServiceImpl......update...张珊
session.getTranscation().commit()2,使用CGLIB代理
在使用Spring的CGLIB代理过程中主要使用到的类为:net.sf.cglib.proxy.Enhancer。其核心方法为:Object hancer.create();
代码如下:
MyCallbackFactory类,需要注意的是 该类实现的接口和JDK实现的接口名字完全一样,接口结构也一致,单spring对其做了另外的调整。所以开发者们不能实现错接口。
package com.xiaohui.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.InvocationHandler; public class MyCallbackFactory implements InvocationHandler {private Object target;public Object getInstance(Object target){this.target = target;Enhancer hancer = new Enhancer(); hancer.setClassLoader(target.getClass().getClassLoader());hancer.setSuperclass(target.getClass());hancer.setCallback(this);return hancer.create();}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("your before logic progream.....");Object obj = method.invoke(target, args);System.out.println("your after logic progream.....");return obj;} }
测试类:
@Testpublic void testProxy2() throws Exception {CustomerServiceImpl service = new CustomerServiceImpl();Customer customer = new Customer();customer.setName("张珊");MyCallbackFactory factory = new MyCallbackFactory();CustomerServiceImpl proxy = (CustomerServiceImpl) factory.getInstance(service);proxy.update(customer);System.out.println(proxy.getClass().getSuperclass().getName());}
打印结果:
your before logic progream.....
CustomerServiceImpl......update...张珊
your after logic progream.....
com.xiaohui.proxy.CustomerServiceImpl
由此可以看出该代理对象为真实对象的子类。需要代理的对象实不实现接口都无所谓,都可以被CGLIB代理。3,通过实现org.aopalliance.intercept.MethodInterceptor接口,使用spring的org.springframework.aop.framework.ProxyFactoryBean类配置xml获取代理,内部机制就是上面的两种。
TranscationInterceptor类:
package com.xiaohui.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class TranscationInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("your Transcation...logic progream...Before....");Object obj = invocation.proceed();System.out.println("your Transcation...logic progream...After....");return obj;} }
ICustomeService接口:
package com.xiaohui.aop; public interface ICustomeService {void save(Customer c);void update(Customer c); }
CustomerServiceImpl类:
package com.xiaohui.aop; public class CustomerServiceImpl implements ICustomeService{public void save(Customer customer) {System.out.println("CustomerServiceImpl......save..."+customer.getName());}public void update(Customer c) {System.out.println("CustomerServiceImpl......update..."+c.getName());} }
applicationContext.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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><bean id="transcationInteceptor" class="com.xiaohui.aop.TranscationInterceptor" /><bean id="customerService" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces" value="com.xiaohui.aop.ICustomeService" /><property name="target"><bean class="com.xiaohui.aop.CustomerServiceImpl"/></property><property name="interceptorNames"><list><value>transcationInteceptor</value></list></property></bean> </beans>
使用spring的这种配置,被代理的类如果没有实现接口就不用在ProxyFactoryBean中配<property name="proxyInterfaces" value="pacgake.xxxx" />,这样没有接口的代理会通过CGLIB创建代理。有接口的会通过JDK动态代理来创建。
测试代码:@Testpublic void testProxy2() throws Exception {Customer customer = new Customer();customer.setName("张珊");ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");ICustomeService service = ctx.getBean("customerService",ICustomeService.class);service.save(customer);}
打印结果:
your Transcation...logic progream...Before....
CustomerServiceImpl......save...张珊
your Transcation...logic progream...After....
3,spring使用@AspectJ注解添加AOP功能。
AspectJ是一门专门用于Java AOP编程的Java扩展语言。在Spring中,可以通过使用@AspectJ注释,来添加AOP功能。要使用@AspectJ注释编程,首先要在Spring的配置文件中引入aop命名空间:并加入 标签<aop:aspectj-autoproxy/><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><aop:aspectj-autoproxy/> </beans>
使用@AspectJ注解来功能来获取代理首先需要定义一个切面:(定义一个类,使用@Aspect声明)
如下切面类TransCationService:package com.xiaohui.aop;import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;@Aspect public class TransCationService {@Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")public void pointcut() {}@Before("pointcut()")public void beginTranscation(JoinPoint jp) {System.out.println("目标类:" + jp.getTarget().getClass().getName());System.out.println("参数: " + Arrays.toString(jp.getArgs()));System.out.println("方法: " + jp.getSignature().getName());System.out.println("session.brgintranscation()........");}@AfterReturning("pointcut()")public void commit() {System.out.println("session.getTranscation().commit()");}@AfterThrowing(pointcut = ("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"), throwing = "e")public void rollback(Throwable e) {System.out.println("出现异常......" + e.getMessage());System.out.println("session.rollback....");}@After("pointcut()")public void closeSession() {System.out.println("session.close()");} }
上面的
@Pointcut("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")public void pointcut(){}
其定义了一个切入点表达式:其中第一个‘*’ 表示返回值为任何类型,第二个‘*’表示在包com.xiaohui.aop下面的以ServiceImpl结尾的类,第三个‘*’表示所有方法()里的.. 表示任意参数类表。,最终的意思为,在com.xiaohui.aop包下面返回值为任何类型,且类名以ServiceImpl结尾的参数类表不限所有方法。@Before("pointcut()"):前置通知,在目标代理对象执行方法之前调用。直接引用了上面已经定义好的切入点,使用方法名。@AfterReturning("pointcut()"):后置通知,在目标代理对象成功执行方法之后调用。@After("pointcut()"):最终通知,在目标代理对象执行方法之后,无论目标对象方法成功调用与否,都会执行。@AfterThrowing(pointcut=("execution(* com.xiaohui.aop.*ServiceImpl.*(..))"),throwing="e"):异常通知:在一个方法抛出异常后执行。throwing,表示抛出的异常。其切入点为自己重新定义的切入点表达式。applicationContext.xml配置如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><bean id="advice" class="com.xiaohui.aop.TransCationService"/><bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/><aop:aspectj-autoproxy/> </beans>
ICustomerService接口和测试类和2.3中的代码一致,为了测试有异常抛出的情况,我们对CustomerServiceImpl类的save方法进行一点点修改。save方法如下:
public void save(Customer customer) {System.out.println("CustomerServiceImpl......save..."+customer.getName());if(new Random().nextInt(10)>5){throw new RuntimeException("保存失败.....");}}
这样就可以出现抛出异常的情况。没有异常的打印结果如下:
目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@1700391]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
session.getTranscation().commit()
session.close()有异常抛出的打印结果如下:(在测试的过程中发现如果在切面类中将最终通知的方法 定义在 异常通知的方法 上面,则会先打印session.close(),后打印session.rollback....,有点不解)目标类:com.xiaohui.aop.CustomerServiceImpl
参数: [com.xiaohui.aop.Customer@118223d]
方法: save
session.brgintranscation()........
CustomerServiceImpl......save...张珊
出现异常......保存失败.....
session.rollback....
session.close()出过这几个通知外,还有一个环绕通知:@Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")TransCationService切面类:package com.xiaohui.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect;@Aspect public class TransCationService {public void pointcut() {}public void beginTranscation(JoinPoint jp) {System.out.println("目标类:" + jp.getTarget().getClass().getName());System.out.println("参数: " + Arrays.toString(jp.getArgs()));System.out.println("方法: " + jp.getSignature().getName());System.out.println("session.brgintranscation()........");}public void commit() {System.out.println("session.getTranscation().commit()");}public void closeSession() {System.out.println("session.close()");}public void rollback(Throwable e) {System.out.println("出现异常......" + e.getMessage());System.out.println("session.rollback....");}@Around("execution(* com.xiaohui.aop.*ServiceImpl.*(..))")public Object around(ProceedingJoinPoint point) {this.beginTranscation(point);try {Object obj = point.proceed();this.commit();return obj;} catch (Throwable t) {this.rollback(t);} finally {this.closeSession();}return null;} }
测试的结果和上面的测试结果一致。
4,在applicationContext.xml中使用aop命名空间配置代理。
xml中同样需要引入aop的命名空间。但这次不需要打开<aop:aspectj-autoproxy/>
同样,也不需要使用注解来声明切面类和使用advice声明方法。
xml中配置如下
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><aop:config><aop:pointcut expression="execution(* com.xiaohui.aop.*ServiceImpl.*(..))" id="ponit"/><!-- 配置切面 --><aop:aspect ref="advice"><aop:before method="beginTranscation" pointcut-ref="ponit"/><aop:after-throwing method="rollback" pointcut-ref="ponit" throwing="e"/><aop:after-returning method="commit" pointcut-ref="ponit"/><aop:after method="closeSession" pointcut-ref="ponit"/></aop:aspect></aop:config><!-- 定义切面对象 --><bean id="advice" class="com.xiaohui.aop.TransCationService"/><!-- 定义目标代理对象 --><bean id="customerService" class="com.xiaohui.aop.CustomerServiceImpl"/> </beans>
测试代码和ICustomerService接口以及CustomerServiceImpl类都和3中的一样,至于TransCationService,有无注解都没关系,因为在xml中 没有开启<aop:aspectj-autoproxy/>,所以不影响测试。
测试结果也和上面3中的一致。遇到同样的问题是在xml中配置<aop:after-throwing> 在<aop:after>之后时打印结果顺序和配置他的顺序一致,有所不解。 before无论位置在哪都先执行没问题。
spring随笔(二) AOP相关推荐
- Spring 学习二-----AOP的原理与简单实践
一.Spring AOP的原理 AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程.何为切面,就比如说我们系统中的权限管理,日志,事务等我们都可以将其看 ...
- Spring学习之旅(二) AOP(面向切面编程)的使用
辛苦堆砌,转载请注明出处,谢谢! 上一篇说了Spring的依赖注入,今天再看看Spring的AOP,牵扯的AOP的理论知识,大家可以搜索一些文章了解一下,这里不做过多解释,本文主要介绍使用Spring ...
- 一文读懂Spring中的AOP机制
一.前言 这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢? 1.标记日志打印的自定义注解 @Target({ElementType.METHOD}) ...
- Spring系列之AOP实现的两种方式
Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包: aopalliance-1.0.jar 和 ...
- Spring源码-AOP(六)-自动代理与DefaultAdvisorAutoProxyCreator
2019独角兽企业重金招聘Python工程师标准>>> Spring AOP 源码解析系列,建议大家按顺序阅读,欢迎讨论 Spring源码-AOP(一)-代理模式 Spring源码- ...
- Spring 框架之 AOP 原理深度剖析!|CSDN 博文精选
作者 | GitChat 责编 | 郭芮 出品 | CSDN 博客 AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构 ...
- 03.spring framework的AOP
E.作用 AOP是面向对象编程的一个强大补充.通过AOP,我们现在可以把之前分散在应用各处的行为放入可重用的模块中.我们显示地声明在何处如何应用该行为.这有效减少了代码冗余,并让我们的类关注自身的主要 ...
- 【Spring 源码阅读】Spring IoC、AOP 原理小总结
Spring IoC.AOP 原理小总结 前言 版本约定 正文 Spring BeanFactory 容器初始化过程 IoC 的过程 bean 完整的创建流程如下 AOP 的过程 Annotation ...
- Spring(二)IOC容器的初始化流程
文章目录 一.Spring 核心容器类 1.1 BeanFactory 1.2 ApplicationContext 1.3 BeanDefinition 二.IOC容器的初始化 2.1 基于Xml的 ...
- Spring:IOC与AOP
目录 一.Spring简介 1.Spring 2.spring优势 二.IOC 1.概念和原理 2.自定义IOC容器 1.需求 2.实现 3.Spring相关的API 3.1 图解编辑 3.2.Be ...
最新文章
- 算法设计与分析 贪心算法
- 苹果天气不显示_热门天气APP被苹果拿下,安卓用户不能再使用
- pytorch梯度累积
- android获取周围AP信息(上)
- brave+kafka+zipkin+cassandra搭建分布式链路跟踪系统
- css 图片反色,颜色反色,高斯模糊
- 安装Linux系统不分区的问题,浅谈linux系统的分区问题
- MySQL事务的保证机制
- Chrome网页观看百度云视频加速
- unity-编辑器协程
- oracle的驱动是什么文件,oracle数据库驱动在哪个文件夹
- 如何编写无法维护的代码
- 嵌入式软件面试题整理
- 基于GMapping的栅格地图的构建
- 判断输入是否为电话号码
- c语言简单的字符串处理
- carsim中质心加速度_carsim输入、输出常用变量
- 白塑投影幕布为何深受青睐?
- mysql sql 优化 博客园_Mysql的SQL优化指北
- 腾达路由器电信宽带服务器无响应,分析腾达路由器无法分配IP地址
热门文章
- MSDN Visual系列:在MOSS中创建一个BDC实体
- Kafka源码解析 - 副本迁移任务提交流程
- php处理ubb代码,过滤UBB代码的php类
- linux 减小根分区大小_减小linux下根分区
- mysql数据与Hadoop之间导入导出之Sqoop实例
- Java基础—集合2Set接口和Map接口
- php json_decode NULL
- 像QQ一样输入表情图像
- [HttpException (0x80004005): Failed to Execute URL.]之画蛇添足之痛
- xcache安装配置