SpringAop使用:
话不多说,直接上代码
一、添加相关maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、创建切面类(我这里使用的是环绕通知)
package com.yidian.aspect;
import com.yidian.entity.Fans;
import com.yidian.entity.ReturnInfo;
import com.yidian.entity.UserInfo;
import com.yidian.service.FansService;
import com.yidian.service.MessageService;
import com.yidian.service.UserInfoService;
import com.yidian.util.JpushUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Aspect
@Component
@Slf4j
public class SpringAopp {
@Resource
FansService fansService;
@Resource
UserInfoService userInfoService;
@Resource
MessageService messageService;
//切点
@Pointcut("execution(public * com.yidian.controller.FansController.insertFans(..))")
private void insertFans(){};
//增加粉丝通知
@Around("insertFans()")
public ReturnInfo fansMessage(ProceedingJoinPoint joinPoint) throws Throwable {
ReturnInfo returnInfo = new ReturnInfo();
//拿到下标为0和1的参数
Object[] args = joinPoint.getArgs();
Integer userId= (Integer) args[0];
Integer fansId= (Integer) args[1];
//获取返回值
ReturnInfo returnInfo1 = (ReturnInfo) joinPoint.proceed();
String b = returnInfo1.getMsg();
//通过粉丝ID查询相关信息
UserInfo userInfo = userInfoService.selectUserInfoById(fansId);
//判断
if (!b.equals("不能重复关注") && !b.equals("已被对方拉黑,无法关注!")) {
messageService.insertMsg(2, userId, fansId, 1, userInfo.getNickName() + "关注了您,成为您的粉丝",0);
JpushUtil.sendPushByAlias(userId.toString(),"",userInfo.getNickName()+"关注了您,成为您的粉丝","","");
returnInfo.setObject(returnInfo1.getObject());
returnInfo.setType(returnInfo1.getType());
returnInfo.setMsg(b);
}else {
returnInfo.setObject(returnInfo1.getObject());
returnInfo.setType(returnInfo1.getType());
returnInfo.setMsg(b);
}
return returnInfo;
}
}
AOP 知识详解
一、AOP的概念
AOP(Aspect Oriented Programming),即为面向切面编程。在软件开发中,散布于应用中多处的功能被称为横切关注点(cross-cutting concern),通常来说,这些横切关注点从概念上是与应用的业务逻辑分离的。比如,声明式事务、日志、安全、缓存等等,都与业务逻辑无关,可以将这些东西抽象成为模块,采用面向切面编程的方式,通过声明方式定义这些功能用于何处,通过预编译方式和运行期动态代理实现这些模块化横切关注点程序功能进行统一维护,从而将横切关注点与它们所影响的对象之间分离出来,就是实现解耦。
横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。这样做有两个优点:
1)每个关注点都集中于一个地方,而不是分散到多处代码中;
2)服务模块更简洁,因为它们只包含主要的关注点的代码(核心业务逻辑),而次要关注点的代码(日志,事务,安全等)都被转移到切面中。
通知(Advice)
切面类有自己要完成的工作,切面类的工作就称为通知。通知定义了切面是做什么以及何时使用。
“做什么”,即切面类中定义的方法是干什么的;
“何时使用”,即5种通知类型,是在目标方法执行前,还是目标方法执行后等等;
“何处做”,即通知定义了做什么,何时使用,但是不知道用在何处,而切点定义的就是告诉通知应该用在哪个类的哪个目标方法上,从而完美的完成横切点功能。
Spring切面定义了5种类型通知:
1)前置通知(Before):在目标方法被调用之前调用通知功能。
2)后置通知(After):在目标方法完成之后调用通知,不会关心方法的输出是什么。
3)返回通知(After-returning): 在目标方法成功执行之后调用通知。
4)异常通知(After-throwing):在目标方法抛出异常后调用通知。
5)环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为。
切面(Aspect)
切面是通知和切点的结合,通知和切点共同定义了切面的全部内容。因为通知定义的是切面的"要做什么"和"在何时做",而切点定义的是切面的"在何地做"。将两者结合在一起,就可以完美的展现切面在何时,何地,做什么(功能)。
连接点(Join point)
即被通知的类中的方法都可能成为切点,所以这些都是连接点,定义成切点之后,这个连接点就变成了切点,通知的类可能是一个类,也有可能是一个包底下的所有类,所以连接点可以成千上万来记,是一个虚概念,可以把连接点看成是切点的集合。
切点(Poincut)
在被通知的类上,连接点谈的是一个飘渺的大范围,而切点是一个具体的位置,用于缩小切面所通知的连接点的范围。
前面说过,通知定义的是切面的"要做什么"和"在何时做",是不是没有去哪里做,而切点就定义了"去何处做"。
切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或者是使用正则表达式定义所匹配的类和方法名称来指定切点。说白了,切点就是让通知找到"发泄的地方"。
引入(Introduction)
引入这个概念就比较高大尚,引入允许我们向现有的类添加新方法或属性。
主要目的是想在无需修改A的情况下,引入B的行为和状态。
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入:
编译期:
切面在目标类编译时被织入。需要特殊的编译器,是AspectJ的方式,不是spring的菜。
类加载期:
切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器,它可以在目标类被引入应用之前
增强该目标类的字节码。AspectJ5支持这种方式。
运行期:
切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态的创建
一个代理对象。而这正是Spring AOP的织入切面的方式。