当前位置: 代码网 > it编程>编程语言>Java > Spring AOP 应用

Spring AOP 应用

2024年08月02日 Java 我要评论
AOP:面向切面编程,对面向对象编程的一种补充。AOP可以将一些公用的代码,自然的嵌入到指定方法的指定位置。比如:如上图,我们现在有四个方法,我们想在每个方法执行一开始,输出一个日志信息。但是这样做很麻烦,如果有100个、1000个方法,工作量会很大,而且难以维护。这时候就可以通过AOP进行解决。

1. 介绍

aop:面向切面编程,对面向对象编程的一种补充。

aop可以将一些公用的代码,自然的嵌入到指定方法的指定位置。

比如:

image-20240801160803027

如上图,我们现在有四个方法,我们想在每个方法执行一开始,输出一个日志信息。但是这样做很麻烦,如果有100个、1000个方法,工作量会很大,而且难以维护。这时候就可以通过aop进行解决。

image-20240801161149526


2. 案例实战

2.1 需求分析及环境搭建

环境:springboot + springboot web + springboot aop。

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-test</artifactid>
    <scope>test</scope>
</dependency>
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-aop</artifactid>
</dependency>
<dependency>
    <groupid>org.projectlombok</groupid>
    <artifactid>lombok</artifactid>
</dependency>

目标:控制器业务方法,统一进行日志输出。

新建user类,包含idname属性。

新建usercontroller

@restcontroller
@requestmapping("/user")
public class usercontroller {
    @getmapping("/list")
    public list<user> list(){
        return arrays.aslist(
                new user(1,"张三"),
                new user(2,"李四"),
                new user(3,"王五")
        );
    }
    @getmapping("/getbyid/{id}")
    public user getbyid(@pathvariable("id") integer id){
        return new user(id,"张三");
    }
    @getmapping("/deletebyid/{id}")
    public boolean deletebyid(@pathvariable("id") integer id){
        return true;
    }
}

此时,我们的目标就是使用aop的方式,给这个listdeletebyidgetbyid方法加上日志。

日志要包括调用方法的名称、返回值以及参数列表。

2.3 aop实现

1. 首先我们要让aop知道哪些方法需要被aop处理 -> 通过注解方式进行处理

// 定义一个注解,来标记需要添加日志的方法
@target(elementtype.method)
@retention(retentionpolicy.runtime)
@documented
public @interface logannotation {
    string value() default "";
}

定义好注解后,给需要使用日志的方法添加注解,如:

@logannotation("查询用户")   // 标记目标方法
@getmapping("/getbyid/{id}")
public user getbyid(@pathvariable("id") integer id){
    return new user(id,"张三");
}

2. 实现切面任务

新建logaspect类,这就是生成切面对象的类。我们需要用@component注解进行标注,交给ioc容器进行管理。此外,我们要用@aspect注解标注其为一个切面。

然后,我们要将这个切面与我们刚刚标注的@logannotation注解建立联系,让切面知道从哪个位置进行切入。实现的方法为,新建一个方法,然后给这个方法添加@pointcut("@annotation(自定义注解的全类名)")。这样就成功建立的联系。

确定切入点后,我们就可以写切面的实际任务了。新建一个方法around。此时,我们要将确定切点的方法与切面实际处理任务的方法进行关联。实现的方法为,给实际处理任务的方法添加@around("标记切点的方法名()")注解。

此时,我们只有一个around方法,要用这一个方法对listgetbyiddeletebyid三个方法进行处理。那么around方法如何分辨这三个不同的方法呢?这时就需要用到一个连接点对象proceedingjoinpointaround的返回值为object类型,其要返回所切入方法的返回值。

然后,就可以实现日志输出功能了。

@aspect
@component
@slf4j
public class logaspect {
    @pointcut("@annotation(cn.codewei.aopstudy.annotation.logannotation)")
    public void logpointcut() {
    }
    @around("logpointcut()")
    public object around(proceedingjoinpoint point) throws throwable {
        // 方法名称
        string name = point.getsignature().getname();
        // 通过反射 获取注解中的内容
        methodsignature signature = (methodsignature) point.getsignature();
        method method = signature.getmethod();
        logannotation annotation = method.getannotation(logannotation.class);
        string value = annotation.value();
        // 输出日志
        log.info("方法名称:{}, 方法描述: {}, 返回值: {}, 参数列表: {}", name, value, point.proceed(), point.getargs());
        // 返回切入方法的返回值
        return point.proceed();
    }
}

3. @pointcut

  • 使用within表达式匹配

​ 匹配com.leo.controller包下所有的类的方法

@pointcut("within(com.leo.controller..*)")
public void pointcutwithin(){
}
  • this匹配目标指定的方法,此处就是hellocontroller的方法
@pointcut("this(com.leo.controller.hellocontroller)")
public void pointcutthis(){
}
  • target匹配实现userinfoservice接口的目标对象
@pointcut("target(com.leo.service.userinfoservice)")
public void pointcuttarge(){
}
  • bean匹配所有以service结尾的bean里面的方法

    注意:使用自动注入的时候默认实现类首字母小写为bean的id

@pointcut("bean(*serviceimpl)")
public void pointcutbean(){
}
  • args匹配第一个入参是string类型的方法
@pointcut("args(string, ..)")
public void pointcutargs(){
}
  • @annotation匹配是@controller类型的方法
@pointcut("@annotation(org.springframework.stereotype.controller)")
public void pointcutannocation(){
}
  • @within匹配@controller注解下的方法,要求注解的@controller级别为@retention(retentionpolicy.class)
@pointcut("@within(org.springframework.stereotype.controller)")
public void pointcutwithinanno(){
}
  • @target匹配的是@controller的类下面的方法,要求注解的@controller级别为@retention(retentionpolicy.runtime)
@pointcut("@target(org.springframework.stereotype.controller)")
public void pointcuttargetanno(){
}
  • @args匹配参数中标注为@sevice的注解的方法
@pointcut("@args(org.springframework.stereotype.service)")
public void pointcutargsanno(){
}
  • 使用excution表达式
@pointcut(value = "execution(public * com.leo.controller.hellocontroller.hello*(..))")
public void pointcut() {
}
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com