前言

在一些场景下,我们需要在执行某些方法或者某些代码时,预先执行或者后置执行特定的一些方法,但是我们不想在代码里复写,我们不想在代码里将这些指定的方法写入进去,而是通过抽离的方式,这种场景下aop切面可以帮助我们完成这种操作


提示:以下是本篇文章正文内容,下面案例可供参考

一、AOP是什么?

spring两大核心包括ioc和aop,ioc是控制反转,将对象交由容器进行控制,不用重复的new。
而aop是面向切面编程,aop采用横向抽取机制,取代纵向集成体系的重复性代码。如使用aop实现日志打印,事务管理,安全权限等。

二、代理模式

aop实现在于aop代理。aop代理又分为静态代理和动态代理。

1.什么是代理

为某一个对象创建一个代理对象,程序不直接用原本的对象,而是由创建的代理对象来控制原对象,通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间。

2.静态代理

由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在

通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的。

3.动态代理

在程序运行时,运用反射机制动态创建而成,无需手动编写代码

JDK动态代理
CGLIB动态代理(原理:是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理)

区别
1.jdk动态代理是基于接口的,jdk动态代理是产生一个新的对象实现接口,包裹实现接口的代理目标类,但是这个实现类有特有的方法,那么代理类不能代理这个方法。
2.cglib动态代理是基于集成的,cglib动态代理是产生一个新的对象继承实现类,也就成了代理目标类的子类,那么实现类特有的方法是会被代理的

问题:spring的aop是使用的jdk动态代理还是cglib动态代理

spring 默认情况下会判断代理类有没有实现接口,实现了就是jdk代理,继承就是cglib代理。在springboot中默认配置为cglib动态代理


三,springboot中如何使用注解来实现aop

1.自定义注解

@Target(ElementType.METHOD)   //定义注解的使用范围为方法
@Retention(RetentionPolicy.RUNTIME )
public @interface SaveHotKey {

}

1.@Target :注解的作用目标

@Target(ElementType.TYPE)作用在接口,类,枚举,注解(如@restcontroller)
@Target(ElementType.FIELD)作用于字段,枚举的常量 @filed
@Target(ElementType.METHOD)作用于方法,@reqestmapping
@Target(ElementType.CONSTRUCTOR)作用于构造函数
@Target(ElementType.PARAMETER)作用于方法参数 @param

2.@Retention 注解保留生命周期

source注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
class注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
runtime注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在

2.切面类

业务逻辑,在调用查询接口前,保存一份参数。

@Aspect
@Component
public class SaveHotKeyAspect {

    @Autowired
    private ElasticSearchUtils elasticSearchUtils;

    @Pointcut("@annotation(com.xxxx.cloud.elasticsearch.aop.SaveHotKey)")
    public void checkDataPoint(){

    }

    @Before("checkDataPoint()")  //前置通知
    public void checkBefore(JoinPoint point){
        Object[] args=point.getArgs();
        ResourceQuery query= (ResourceQuery)args[0];
        //存es的热搜词中
        if (StringUtils.isNotBlank(query.getKeyWord())){
            HotKeyWordsPo hotKeyWordsPo = new HotKeyWordsPo();
            hotKeyWordsPo.setId(UUID.randomUUID().toString());
            hotKeyWordsPo.setCreateTime(new Date());
            hotKeyWordsPo.setSearchInput(query.getKeyWord());
            try {
                elasticSearchUtils.addData(hotKeyWordsPo, ESConst.HOT_KEY_INDEX,hotKeyWordsPo.getId());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

/*  @After("checkDataPoint()") //后置通知
    public void checkAfter(){

    }*/
}

1 @Aspect:把当前类标识为一个切面供容器读取

2.@Pointcut:aop切入点,表示在什么条件下触发

3.@annotation:声明以注解的方式来定义切点,注解为SaveHotKey的包路径

4.@Before:前置增强方法的标识,表示在标有注解的方法前执行切面方法

5.其他方式:
除了使用注解方式外,@pointcut中提供匹配规则 execution,写法可以表达为

@Pointcut("execution(* com.xxx.cloud.controller.*.(..))")

总结

execution匹配规则不太熟悉,但是注解方式好像更灵活,可以直接在需要的地方打上注解就完成切面

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐