springboot 使用注解,对注解使用切面后,Controller调用service一直报null的问题解决。

百度后的答案是:

springboot 注解加切面 后controller, service为null

报错问题:"springboot 注解加切面后controller, servise为null" 通常意味着在使用Spring Boot时,通过注解定义的切面成功创建了,但是与之相关联的Controller或Service组件没有被Spring容器正确注入。

可能的原因和解决方法:

组件扫描问题:确保Controller和Service类所在的包在Spring Boot应用的@SpringBootApplication注解或@ComponentScan注解的扫描路径之内。

切面优先级问题:如果使用了AspectJ来创建切面,并且切面的优先级设置不当,可能会导致在切面执行之后,Controller和Service还没有完成依赖注入。可以通过提升切面的优先级来解决这个问题。

切面实现问题:检查切面的实现代码,确保没有在切面中直接通过new关键字来创建Controller或Service的实例,这样会导致Spring容器控制的实例化过程被绕开,从而引发空指针异常。

代理模式问题:Spring AOP默认使用的是代理模式,如果在Service或Controller中直接调用了自己的方法,可能导致此时的调用并没有被AOP代理拦截,因此没有经过切面处理的方法内部调用会导致注入的bean为null。

配置问题:检查Spring Boot的配置文件,确保没有错误配置导致组件创建或注入失败。

版本兼容问题:确保Spring Boot的版本和其他Spring组件的版本之间是兼容的,有时候版本不匹配也会导致组件无法正常注入。

解决方法通常涉及检查和调整配置,确保Spring的组件扫描路径正确,切面优先级设置适当,并且避免在不适当的上下文中直接实例化Bean。如果问题依然存在,可以通过增加日志输出或使用调试工具来进一步诊断问题。

使用上述方法后仍然存在问题。

修改切面页面注解 @Aspect为@EnableAspectJAutoProxy后问题解决。
但是在切面上加@EnableAspectJAutoProxy虽然可以解决service调用时为null的问题,但是切面不起作用了。查阅资料后@EnableAspectJAutoProxy应该加在配置管理的代码中,也就是扫描包的文件。在切面文件中加@Aspect后,切面才可以起作用,最后查找原因,原来是使用切面的页面Controller上有方法上调用了final关键字,需要把所有的方法上的final都去掉,调用的方法才不会报null,同时需要注意的是方法不能是private类型

切面页面如下 LimitAspect.java

package com.turingoal.tms.config;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import com.turingoal.common.exception.TgException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Map;

@Slf4j
@Aspect 
@Component
public class LimitAspect {

    private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Around("@annotation(Limit)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature)pjp.getSignature();
        Method method = signature.getMethod();
        //拿limit的注解
        Limit limit = method.getAnnotation(Limit.class);
        if (limit != null) {
            //key作用:不同的接口,不同的流量控制
            String key=limit.key();
            RateLimiter rateLimiter;
            //验证缓存是否有命中key
            if (!limitMap.containsKey(key)) {
                // 创建令牌桶
                rateLimiter = RateLimiter.create(limit.permitsPerSecond(),limit.timeout(),limit.timeunit());
                limitMap.put(key, rateLimiter);
                log.info("新建了令牌桶={},容量={}",key,limit.permitsPerSecond());
            }
            rateLimiter = limitMap.get(key);
            // 拿令牌
            boolean acquire = rateLimiter.tryAcquire();
            // 拿不到命令,直接返回异常提示
            if (!acquire) {
                log.debug("令牌桶={},获取令牌失败",key);
                throw new TgException(limit.msg());
            }
        }
        return pjp.proceed();
    }

}

注解Limit

package com.turingoal.tms.config;
import org.junit.jupiter.api.Order;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Order(1)
public @interface Limit {

    // 资源key
    String key() default "";

    // 最多访问次数
    int permitsPerSecond();

    // 时间
    long timeout();

    // 时间类型
    TimeUnit timeunit() default TimeUnit.MILLISECONDS;

    // 提示信息
    String msg() default "系统繁忙,请稍后再试";

}

controller调用的方法:切记返回的方法上不能加final

  	@Limit(key = "findToolItems", permitsPerSecond = 1, timeout = 1000, msg = "当前查询较频繁,请稍后再试!")
    @RequestMapping(value = "/findToolItems.gsp", method = { RequestMethod.GET, RequestMethod.POST })
    @Operation(tags = {"XXX"}, summary = "查询[XXX]", description = "通过条件查询[XXX项]")
    public ResponseBean findItems(final ToolItemQuery query) throws TgException {
        query.setIsWorkwear(2);
        Page<Entity> data = service.findItems(query);
        return ResponseBean.page(data);
    }
相关推荐
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭16 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫32 分钟前
泛型(2)
java
超爱吃士力架37 分钟前
邀请逻辑
java·linux·后端
南宫生41 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白661 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp1 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗2 小时前
常用类晨考day15
java
骇客野人2 小时前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言