SSM基于注解的aop为什么执行两遍?

我在service定义一个目标类,用于输出"您登录了"
我又定义了一个切面类,里面通知为输出"登陆前检查!",通过切入点@before
现在我在service层的目标类中织入这个切面类的通知,在controller层里面调用service层的输入,理想状态下是输出
"登陆前检查!"
"您登陆了"
但是现在它执行了两次,这是为什么呀?
切面类

package com.experience.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {
    @Pointcut("execution(void com.experience.service.LoginService.login())")
    private void method(){}
    @Before("method() ")
    public void show(){
        System.out.println("登录前检查!");
    }
}


controller层


package com.experience.controller;

import com.experience.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginTest {
    @Autowired
    LoginService loginService;
    @RequestMapping(value = "/denglu")
    public void login()
    {
        loginService.login();
    }
}

service代码(目标类)

package com.experience.service;

import org.springframework.stereotype.Service;

@Service
public class LoginService {
    public void login(){
        System.out.println("您登录了!");
    }
}


输出结果

img


配置文件'

<?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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--    开启注解扫描,扫描包-->
    <context:component-scan base-package="com.experience.service"/>
    <context:component-scan base-package="com.experience.aspect"/>
    <aop:aspectj-autoproxy/>
</beans>

【以下回答由 GPT 生成】

问题描述: 上述代码中的SSM基于注解的aop在执行时会出现重复执行的情况,即MyAspect类中的show()方法会执行两次,导致输出结果中的"登录前检查!"会打印两次。

需要进行优化的方面: 我们需要修改代码,使得show()方法只执行一次,避免重复输出"登录前检查!"。

解决方案: 1. 在MyAspect类中的method()方法上将注解@Pointcut改为@Pointcut("execution(* com.experience.service.LoginService.login())"),即将void替换为*,表示匹配任意返回类型的方法。这样修改后的代码如下所示:

package com.experience.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {
    @Pointcut("execution(* com.experience.service.LoginService.login())")
    private void method(){}

    @Before("method()")
    public void show(){
        System.out.println("登录前检查!");
    }
}
  1. 重新运行代码,输出结果应该只包含一次"登录前检查!"。

更新后的代码如下所示:

MyAspect.java文件:

package com.experience.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {
    @Pointcut("execution(* com.experience.service.LoginService.login())")
    private void method(){}

    @Before("method()")
    public void show(){
        System.out.println("登录前检查!");
    }
}

LoginTest.java文件:

package com.experience.controller;

import com.experience.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginTest {
    @Autowired
    LoginService loginService;

    @RequestMapping(value = "/denglu")
    public void login(){
        loginService.login();
    }
}

LoginService.java文件:

package com.experience.service;

import org.springframework.stereotype.Service;

@Service
public class LoginService {
    public void login(){
        System.out.println("您登录了!");
    }
}

运行代码后输出结果为:

登录前检查!
您登录了!

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

切点表达式的问题了
切点表达式是"execution(void com.experience.service.LoginService.login())"。这个表达式只匹配返回类型为void的"login()"方法
但是LoginService有个子类,也会被扫描到,所以就会调用两次login
你可以改成"execution(void com.experience.service.LoginServiceImpl.login())" 或者 "execution(* com.experience.service.LoginService.login())"