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相关推荐

  1. Spring 学习二-----AOP的原理与简单实践

    一.Spring  AOP的原理 AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程.何为切面,就比如说我们系统中的权限管理,日志,事务等我们都可以将其看 ...

  2. Spring学习之旅(二) AOP(面向切面编程)的使用

    辛苦堆砌,转载请注明出处,谢谢! 上一篇说了Spring的依赖注入,今天再看看Spring的AOP,牵扯的AOP的理论知识,大家可以搜索一些文章了解一下,这里不做过多解释,本文主要介绍使用Spring ...

  3. 一文读懂Spring中的AOP机制

    一.前言 这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢? 1.标记日志打印的自定义注解 @Target({ElementType.METHOD}) ...

  4. Spring系列之AOP实现的两种方式

    Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包: aopalliance-1.0.jar 和 ...

  5. Spring源码-AOP(六)-自动代理与DefaultAdvisorAutoProxyCreator

    2019独角兽企业重金招聘Python工程师标准>>> Spring AOP 源码解析系列,建议大家按顺序阅读,欢迎讨论 Spring源码-AOP(一)-代理模式 Spring源码- ...

  6. Spring 框架之 AOP 原理深度剖析!|CSDN 博文精选

    作者 | GitChat 责编 | 郭芮 出品 | CSDN 博客 AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构 ...

  7. 03.spring framework的AOP

    E.作用 AOP是面向对象编程的一个强大补充.通过AOP,我们现在可以把之前分散在应用各处的行为放入可重用的模块中.我们显示地声明在何处如何应用该行为.这有效减少了代码冗余,并让我们的类关注自身的主要 ...

  8. 【Spring 源码阅读】Spring IoC、AOP 原理小总结

    Spring IoC.AOP 原理小总结 前言 版本约定 正文 Spring BeanFactory 容器初始化过程 IoC 的过程 bean 完整的创建流程如下 AOP 的过程 Annotation ...

  9. Spring(二)IOC容器的初始化流程

    文章目录 一.Spring 核心容器类 1.1 BeanFactory 1.2 ApplicationContext 1.3 BeanDefinition 二.IOC容器的初始化 2.1 基于Xml的 ...

  10. Spring:IOC与AOP

    目录 一.Spring简介 1.Spring 2.spring优势 二.IOC 1.概念和原理 2.自定义IOC容器 1.需求 2.实现 3.Spring相关的API 3.1 图解​编辑 3.2.Be ...

最新文章

  1. 算法设计与分析 贪心算法
  2. 苹果天气不显示_热门天气APP被苹果拿下,安卓用户不能再使用
  3. pytorch梯度累积
  4. android获取周围AP信息(上)
  5. brave+kafka+zipkin+cassandra搭建分布式链路跟踪系统
  6. css 图片反色,颜色反色,高斯模糊
  7. 安装Linux系统不分区的问题,浅谈linux系统的分区问题
  8. MySQL事务的保证机制
  9. Chrome网页观看百度云视频加速
  10. unity-编辑器协程
  11. oracle的驱动是什么文件,oracle数据库驱动在哪个文件夹
  12. 如何编写无法维护的代码
  13. 嵌入式软件面试题整理
  14. 基于GMapping的栅格地图的构建
  15. 判断输入是否为电话号码
  16. c语言简单的字符串处理
  17. carsim中质心加速度_carsim输入、输出常用变量
  18. 白塑投影幕布为何深受青睐?
  19. mysql sql 优化 博客园_Mysql的SQL优化指北
  20. 腾达路由器电信宽带服务器无响应,分析腾达路由器无法分配IP地址

热门文章

  1. MSDN Visual系列:在MOSS中创建一个BDC实体
  2. Kafka源码解析 - 副本迁移任务提交流程
  3. php处理ubb代码,过滤UBB代码的php类
  4. linux 减小根分区大小_减小linux下根分区
  5. mysql数据与Hadoop之间导入导出之Sqoop实例
  6. Java基础—集合2Set接口和Map接口
  7. php json_decode NULL
  8. 像QQ一样输入表情图像
  9. [HttpException (0x80004005): Failed to Execute URL.]之画蛇添足之痛
  10. xcache安装配置