文章目录
1 AOP概念
AOP(Aspect-Oriented Programming,面向切面编程),简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或业务封装起来,再把封装功能整合到业务中。 AOP的核心思想就是“将应用程序中的业务逻辑同对其提供支持的通用服务进行分离”好处:
1,便于减少系统的重复代码
2,降低模块间的耦合度
3,有利于未来的可操作性和可维护性。
例如:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
2 AOP入门示例
需求:在服务层用AOP给所有方法(增删改)添加统一日志服务(谁什么时间操作了哪个对象的哪个方法。。。。)
1,添加依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.3.8</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency><!--aop包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
2,编写服务接口和实现类
package com.xii.service.Impl;
import com.xii.entity.Dept;
import com.xii.service.DeptService;
import org.springframework.stereotype.Service;
@Service
public class DeptServiceImpl implements DeptService {
/**
* 添加
* @param dept
*/
@Override
public void add(Dept dept) {
int a = 1/0;
System.out.println("添加操作");
}
/**
* 通过id删除
* @param id
*/
@Override
public void deleteById(Integer id) {
System.out.println("删除操作");
}
/**
* 更新
* @param dept
*/
@Override
public void updateDept(Dept dept) {
System.out.println("更新操作");
}
/**
* 查询操作
*/
@Override
public void queryDept() {
System.out.println("查询操作");
}
}
3,编写日志记录工具类
package com.xii.utils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 通知类 又叫增强类
*/
@Component //三层之外的注解
public class LogRecordUtil {
/**
* 统一记录日志的方法
*
* @param joinPoint
*/
public void logRecord(JoinPoint joinPoint) {
//获取目标对象
String targetName = joinPoint.getTarget().getClass().getName();
//获取方法名称
String methodName = joinPoint.getSignature().getName();
//获取执行方法参数
Object[] args = joinPoint.getArgs();
String argsType = null;
if (args != null && args.length > 0) {
//获得参数类型 反射
argsType = args[0].getClass().getName();
}
System.out.println("admin在" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +
"执行类" + targetName + "中的方法" + methodName + ",该方法参数类型为:" + argsType + "----------模拟日志记录-------------");
}
/**
* 前置通知
*
* @param joinPoint
*/
public void beforeDoSome(JoinPoint joinPoint) {
System.out.println("-------------------------前置通知方法-------------------------");
}
/**
* 环绕通知具体实现方法 相当于前置和后置通知的整合 但是也有区别:
* 1,前后置通知无法决定是否调用业务方法 而环绕通知可以决定是否调用业务方法
* 2,前后置通知无法获取业务方法的返回值,而环绕通知可以获取执行业务方法的返回值
*@param proceedingJoinPoint
* @return
*/
public Object aroundHandler(ProceedingJoinPoint proceedingJoinPoint) {
/* proceedingJoinPoint.getTarget();
proceedingJoinPoint.getSignature().getName();
proceedingJoinPoint.getArgs();*/
System.out.println("-------环绕通知,在执行业务之前干什么---------");
Object result = null;
try {
//调用业务方法
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("业务方法的返回值为:" + result);
System.out.println("-------环绕通知,在执行业务之后干什么---------");
return result;
}
/**
* 异常处理方法
* @param joinPoint
* @param ex
*/
public void exceptionHandle(JoinPoint joinPoint,Exception ex){
System.out.println("-------------------异常业务----------------------");
System.out.println("在执行"+joinPoint.getSignature().getName()+"方法时:出现:"+ex.getClass().getName()+"信息:"+ex.getMessage());
}
/**
* 最终通知
*/
public void finallyHandler(JoinPoint joinPoint){
System.out.println("最终通知-----------无论有没有异常都会执行的方法");
}
}
4,编写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: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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xii"></context:component-scan>
<!--被代理者-->
<!--<bean id="logRecordUtil" class="com.xii.utils.LogRecordUtil"></bean>-->
<!--aop的配置-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointCutA" expression="execution(* com.xii.service.Impl.*.add(..))" />
<aop:pointcut id="pointCutB" expression="execution(* com.xii.service.Impl.*.*(..))" />
<!--切面配置-->
<aop:aspect ref="logRecordUtil">
<!--后置通知-->
<aop:after-returning pointcut-ref="pointCutA" method="logRecord"></aop:after-returning>
<!--前置通知-->
<aop:before pointcut-ref="pointCutA" method="beforeDoSome"></aop:before>
<!--环绕通知-->
<aop:around pointcut-ref="pointCutA" method="aroundHandler"></aop:around>
<!--异常通知-->
<aop:after-throwing pointcut-ref="pointCutA" method="exceptionHandle" throwing="ex"></aop:after-throwing>
<!--最终通知-->
<aop:after method="finallyHandler" pointcut-ref="pointCutA"></aop:after>
</aop:aspect>
</aop:config>
</beans>
5,编写测试类
/**
* 原生
*/
@Test
public void aopTest() {
ClassPathXmlApplicationContext aopContext = new ClassPathXmlApplicationContext("spring_aop.xml");
DeptService deptServiceImpl = aopContext.getBean("deptServiceImpl", DeptService.class);
deptServiceImpl.add(new Dept());
deptServiceImpl.deleteById(1);
deptServiceImpl.queryDept();
deptServiceImpl.updateDept(new Dept());
}
3 aop术语简介
1,切面(Aspect)
切点和增强组成,被抽取的公共模块 如:将日志记录,性能统计,安全控制,事务处理,异常处理等等
2,增强(Advice)又叫通知
通知是切面的具体实现,是放置“切面代码”的类。在不修改原有代码的前提下,为某一个对象增加新的功能,增加的新功能就是增强。切面在某个具体的连接点采取的行为或行动称为通知。
3,切入点(Pointcut)
多个连接点组成一个切入点,可以使用切入点表达式来表示,AOP通过切点定位到特定的连接点。
切入点表达式:
4,连接点(Join point)
程序执行过程中某个特定的点,连接点总是代表某个方法执行的特定位置。类开始初始化前、类初始化后、类的某个方法调用前、类的某个方法调用后、方法抛出异常后等。
5,织入(Weaving)
织入是指将切面代码插入到目标对象的过程(或者将切入点和通知结合的过程称为织入)。
6,目标对象(Target)
包含主业务逻辑的类的对象,包含一个连接点的对象,即被拦截的对象,被代理的对象。
4 通知类型
Before:在目标方法被调用之前调用(前置通知)
AfterReturning:在目标方法被调用之后调用(后置通知)
Around:拦截对目标对象方法的调用(环绕通知)
After:无论目标方法是否正常运行,都一定要执行的通知(最终通知)
AfterThrowing:当目标方法抛出异常时调用(异常通知)
环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别:
1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。
2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用
5 AOP注解
1,接口及实现类同上
2,日志工具类
package com.xiiannotation.utils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 通知类 又叫增强类
*/
@Aspect //增强类注解
@Component
public class LogRecordUtil {
/**
* 配置切入点
*/
@Pointcut(value = "execution(* com.xiiannotation.service.*.*add(..))")
public void pointCutA(){
}
/**
* 统一记录日志的方法 后置
*
* @param joinPoint
*/
@AfterReturning(pointcut = "pointCutA()")
public void logRecord(JoinPoint joinPoint) {
//获取目标对象
String targetName = joinPoint.getTarget().getClass().getName();
//获取方法名称
String methodName = joinPoint.getSignature().getName();
//获取执行方法参数
Object[] args = joinPoint.getArgs();
String argsType = null;
if (args != null && args.length > 0) {
//获得参数类型 反射
argsType = args[0].getClass().getName();
}
System.out.println("admin在" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +
"执行类" + targetName + "中的方法" + methodName + ",该方法参数类型为:" + argsType + "----------模拟日志记录-------------");
}
/**
* 前置通知
*
* @param joinPoint
*/
@Before("pointCutA()")
public void beforeDoSome(JoinPoint joinPoint) {
System.out.println("-------------------------前置通知方法-------------------------");
}
/**
* 环绕通知具体实现方法 相当于前置和后置通知的整合 但是也有区别:
* 1,前后置通知无法决定是否调用业务方法 而环绕通知可以决定是否调用业务方法
* 2,前后置通知无法获取业务方法的返回值,而环绕通知可以获取执行业务方法的返回值
*@param proceedingJoinPoint
* @return
*/
@Around(value = "pointCutA()")
public Object aroundHandler(ProceedingJoinPoint proceedingJoinPoint) {
/* proceedingJoinPoint.getTarget();
proceedingJoinPoint.getSignature().getName();
proceedingJoinPoint.getArgs();*/
System.out.println("-------环绕通知,在执行业务之前干什么---------");
Object result = null;
try {
//调用业务方法
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("业务方法的返回值为:" + result);
System.out.println("-------环绕通知,在执行业务之后干什么---------");
return result;
}
/**
* 异常处理方法
* @param joinPoint
* @param ex
*/
@AfterThrowing(pointcut = "pointCutA()",throwing = "ex")
public void exceptionHandle(JoinPoint joinPoint,Exception ex){
System.out.println("-------------------异常业务----------------------");
System.out.println("在执行"+joinPoint.getSignature().getName()+"方法时:出现:"+ex.getClass().getName()+"信息:"+ex.getMessage());
}
/**
* 最终通知
*/
@After("pointCutA()")
public void finallyHandler(JoinPoint joinPoint){
System.out.println("最终通知-----------无论有没有异常都会执行的方法");
}
}
3,配置
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xiiannotation"></context:component-scan>
<!--开启@Aspest注解扫描-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4、测试
/**
* 注解测试
*/
@Test
public void aopAnnotationTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring_aopannotation.xml");
com.xiiannotation.service.DeptService deptServiceImpl = applicationContext.getBean("deptServiceImpl", com.xiiannotation.service.DeptService.class);
deptServiceImpl.add(new com.xiiannotation.entity.Dept());
deptServiceImpl.deleteById(1);
deptServiceImpl.queryDept();
deptServiceImpl.updateDept(new com.xiiannotation.entity.Dept());
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/75518.html