16 Spring AOP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hxdeng/article/details/82904347

1. AOP相关概念

AOP 介绍

AOP(Aspect Oriented Programming)的缩写,意为:面向切面编程通过预编译方式和运行期动态代理实现程序功能的同意维护的一种技术。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等

切面介绍

AOP 实现方式

  1. 预编译
    AspectJ
  2. 运行期动态代理
         运行时动态代理又分JDK动态代理,CGLib动态代理
         Spring AOP
         JbossAOP

AOP 术语介绍

编号 名称 说明
1 切面(Aspect) 一个关注点的模块化,这个关注点可能会横切多个对象
2 连接点(Joinpoint) 程序执行过程中的某个特定点
3 通知(Advice) 在切面的某个特定的连接点上执行的动作
4 切入点(Pointcut) 匹配连接点的断言,在AOP中通知和切入点表达式关联
5 引入(Introduction) 在不修改类代码的前提下,为类添加新的方法和和属性
6 目标对象(Target Object) 被一个或多个切面所通知的对象
7 AOP代理(AOP Proxy) AOP框架创建的对象,用来实现切面契约(aspect contract)
(包括通知方法执行等功能)
8 织入(weaving) 把切面链接到其他应用程序类型或者对象上,并创建一个被通知的对象,
分为:编译时织入、类加载时织入、执行时织入

Advice 通知类型

编号 名称 说明
1 前置通知(Befor advice) 在某连接点(join point)之前执行的通知,
但不能阻止连接点钱的执行(除非它跑出异常)
2 返回后通知(After returning advice) 在某连接点(join point)正常完成执行后的通知
3 跑出异常后通知(After throwing advice) 在方法抛出异常退出是执行的通知
4 后置通知(After(finally) advice) 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
5 环绕通知(Around Advice) 包围一个连接点(join point)的通知

Spring 框架中AOP的用途

  1. 提供了声明式的企业服务,特别是EJB的替代服务的声明
  2. 允许用户定制自己的方面,已完成OOP与AOP的互补使用

Spring 的AOP实现

  1. 纯Java实现,无需特殊的编译过程,不需要控制类的加载层次
  2. 目前只支持方法执行连接点(通知Spring Bean的方法执行)
  3. 不是为了提供完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IOC 容器之间的整合,用于帮助解决企业应用中的常见问题
  4. Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案

有接口和无接口的Spring AOP的实现区别

  1. Spring AOP 默认使用标准的JavaSE 动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
  2. Spring AOP 中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)

2. AOP 开发

开发步骤

  1. 引入命名空间
<?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:aop="http://www.springframework.org/schema/aop"  
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/context
   	http://www.springframework.org/schema/context/spring-context.xsd">
 
</beans>
  1. 添加JAR 包
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.9.1</version>
</dependency>

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.1</version>
</dependency>
  1. 声明一个切面( Aspect)
<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
	
	</aop:aspect>
</aop:config>
  1. 声明一个切入点(Pointcut)
<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
  1. 通知配置(advice)
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="aspectPoint" />
<!-- 后置通知 -->
<aop:after method="after" pointcut-ref="aspectPoint" />
<!-- 返回后通知 -->
<aop:after-returning method="returning" pointcut-ref="aspectPoint" />
<!-- 抛出异常通知 -->
<aop:after-throwing method="throwing" pointcut-ref="aspectPoint" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="aspectPoint" />

切入点详细介绍

No 切入点 切入点说明
1 execution(public * *(…)) 切入点为执行所有的public方法
2 execution(* set*(…)) 切入点为执行所有set开始的方法
3 execution(* com.xyz.service.AccountService.*(…)) 切入点为执行AccountService 类中所有方法时
4 execution(* com.xyz.service…(…)) 切入点为执行com.xyz.service包下所有方法时
5 execution(* com.xyz.service…(…)) 切入点为执行com.xyz.service包及其子包下所有方法时
6 within(com.xyz.service.*) within 用于匹配指定类型内的方法执行
7 this(com.xyz.service.AccountService) this 用于匹配当前AOP 代理对象类型的执行方法
8 target(com.xyz.service.AccountService) target 用于匹配当前目标对象类型的执行方法

注意 1 - 5 的切入方式所有AOP 都支持。从6以后只有Spring AOP 支持

1. 前置通知(Befor advice)

业务类

package com.javaee.spring.biz;

public class AspectBiz {
	public void save() {
		System.out.println("AspectBiz ------ save");
	}
}

切面类

public class AspectBean {

	public void before() {
		System.out.println("------前置通知------");
	}
}

配置文件

<!-- 切面 -->
<bean id="aspectBean" class="com.javaee.spring.AspectBean"></bean>
<!-- 业务逻辑 -->
<bean id="aspectBiz" class="com.javaee.spring.biz.AspectBiz"></bean>

<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
		<!-- 配置切入点 -->
		<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
		
		<!-- 配置通知 -->
		<!-- 前置通知 -->
		<aop:before method="before" pointcut-ref="aspectPoint" />
	
	</aop:aspect>
</aop:config>

2. 返回后通知(After returning advice)

业务类

package com.javaee.spring.biz;

public class AspectBiz {
	public void save() {
		System.out.println("AspectBiz ------ save");
	}
}

切面类

public class AspectBean {

    public void returning() {
		System.out.println("------返回后通知------");
	}
}

配置文件

<!-- 切面 -->
<bean id="aspectBean" class="com.javaee.spring.AspectBean"></bean>
<!-- 业务逻辑 -->
<bean id="aspectBiz" class="com.javaee.spring.biz.AspectBiz"></bean>

<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
		<!-- 配置切入点 -->
		<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
		
		<!-- 配置通知 -->
	    <!-- 返回后通知 -->
		<aop:after-returning method="returning" pointcut-ref="aspectPoint" />
	
	</aop:aspect>
</aop:config>

3. 抛出异常后通知(After throwing advice)

业务类

package com.javaee.spring.biz;

public class AspectBiz {
	public void save() {
		System.out.println("AspectBiz ------ save");
		throw new RuntimeException();
	}
}

切面类

public class AspectBean {

	public void throwing() {
		System.out.println("------抛出异常通知------");
	}
}

配置文件

<!-- 切面 -->
<bean id="aspectBean" class="com.javaee.spring.AspectBean"></bean>
<!-- 业务逻辑 -->
<bean id="aspectBiz" class="com.javaee.spring.biz.AspectBiz"></bean>

<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
		<!-- 配置切入点 -->
		<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
		
		<!-- 配置通知 -->
	    <!-- 抛出异常通知 -->
		<aop:after-throwing method="throwing" pointcut-ref="aspectPoint" />
	</aop:aspect>
</aop:config>

4. 后置通知(After(finally) advice)

业务类

package com.javaee.spring.biz;

public class AspectBiz {
	public void save() {
		System.out.println("AspectBiz ------ save");
	}
}

切面类

public class AspectBean {

    public void after() {
    	System.out.println("------后置通知------");
    }
}

配置文件

<!-- 切面 -->
<bean id="aspectBean" class="com.javaee.spring.AspectBean"></bean>
<!-- 业务逻辑 -->
<bean id="aspectBiz" class="com.javaee.spring.biz.AspectBiz"></bean>

<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
		<!-- 配置切入点 -->
		<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
		
		<!-- 配置通知 -->
	    <!-- 后置通知 -->
		<aop:after method="after" pointcut-ref="aspectPoint" />
	
	</aop:aspect>
</aop:config>

5. 环绕通知(Around Advice)

注意 环绕通知方法参数必须是(ProceedingJoinPoint)

业务类

package com.javaee.spring.biz;

public class AspectBiz {
	public void save() {
		System.out.println("AspectBiz ------ save");
	}
}

切面类

public class AspectBean {

	public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("------环绕通知------");
        return pjp.proceed();
    }
}

配置文件

<!-- 切面 -->
<bean id="aspectBean" class="com.javaee.spring.AspectBean"></bean>
<!-- 业务逻辑 -->
<bean id="aspectBiz" class="com.javaee.spring.biz.AspectBiz"></bean>

<!-- 切面配置 -->
<aop:config>
	<aop:aspect id="aspectBeanAop" ref="aspectBean">
		<!-- 配置切入点 -->
		<aop:pointcut id="aspectPoint" expression="execution(* com.javaee.spring.biz.*.*(..))" />
		
		<!-- 配置通知 -->
		<!-- 环绕通知 -->
		<aop:around method="around" pointcut-ref="aspectPoint" />
	
	</aop:aspect>
</aop:config>

Introductions

简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现来代表这些对象。
aop:aspect 中的aop:declare-parents元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此得名)

猜你喜欢

转载自blog.csdn.net/hxdeng/article/details/82904347