解决java Bean 在过滤器(Filter)中注入失效,为null的问题

在项目中引入redis做缓存策略时,往过滤器中引入RedisTemplate redisTemplate;一直报空指针异常。

由于之前一直是在controller,service,dao做@Autowired引入的,从未发生过类似的问题,所以起初猜测可能是jar包有问题,后面排除了这个原因。

导致@Autowired失效的原因大致有两个

1.SpringBoot项目的Bean装配默认规则是根据Application类所在的包位置从上往下扫描!

“Application类”是指SpringBoot项目入口类。这个类的位置很关键:

如果Application类所在的包为:com.root.app,则只会扫描com.root.app包及其所有子包,如果service或dao所在包不在com.root.app及其子包下,则不会被扫描!

即, 把Application类放到dao、service所在包的上级,com.root.Application

我出问题的类确实在Application类子包下面,排除此项。

2.意识到只是拦截器(或过滤器)上会有这样的问题,查询原因应该是:
拦截器执行在自动bean初始化之前导致这个问题的。

造成null的原因是因为拦截器加载是在springcontext创建之前完成的,所以在拦截器中注入实体自然就为null。

注入为null的时候,是通过new的方式创建的拦截器,通过new出来的实例是没有交给spring进行管理的,没有被spring管理的实例,spring是无法自动注入bean的,所以为null

解决方案

可以在过滤器中添加一下代码

    /**
     * 由于在过滤器中无法直接通过Autowired获取Bean,因此需要通过spring上下文来获取IOC管理的实体类
     * 此处主要是用于  RedisUserUtils
     * @param clazz
     * @param request
     * @param <T>
     * @return
     */
    public <T> T getBean(Class<T> clazz, HttpServletRequest request){
        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        return applicationContext.getBean(clazz);
    }

 

然后在需要用到的方法中添加以下代码:request 可以在方法的参数中声明。

RedisUserUtils redisUtils =getBean(RedisUserUtils.class, request);

 

<think>嗯,用户问的是Redis中getCacheObject方法返回null的原因和解决方法。首先,我需要回忆一下之前用户提供的引用内容,特别是引用[1]和[2],里面提到了缓存击穿、值处理和逻辑错误的情况。 首先,用户提到getCacheObject返回null,可能有很多原因。常见的比如缓存未命中,这时候数据确实不在缓存里,可能没有设置或者过期了。然后,引用[1]里的代码示例显示,RedisCache类的get方法存在先检查存在再获取的逻辑,这中间有时间差,如果缓存刚好在这时过期,就会返回null。这种情况属于缓存击穿吗?还是缓存穿透? 然后,引用[2]里提到对值进行缓存,设置短过期时间,这样可以避免频繁查询数据库。比如当数据库查询结果为时,缓存一个null,并设置5分钟过期,这样下次请求就不会直接打到数据库。所以用户的问题可能涉及这种情况,是否getCacheObject没有处理值缓存? 另外,缓存雪崩也有可能,如果大量key同时过期,导致请求直接访问数据库,但返回null的情况可能更偏向于缓存击穿,即某个热点key过期后,高并发请求同时访问数据库。 还有可能是序列化问题,比如存储和读取时使用的序列化方式不一致,导致反序列化失败,返回null。比如引用[3]提到Jackson和FastJson的不同版本,可能配置错误导致。 还有,内存淘汰策略,当内存不足时,Redis自动删除一些key,导致缓存丢失。这时候需要检查内存使用情况和淘汰策略设置。 再考虑代码逻辑问题,比如是否在写入缓存时出现异常,导致数据没有正确存入,或者在获取时key生成规则不一致,导致无法正确读取。引用[4]里的工具类封装可能涉及到key的生成方式,需要检查是否正确。 解决方案方面,首先是值缓存,即使数据库查询为,也缓存null并设置过期时间,避免穿透。然后,缓存预热,在系统启动时加载热点数据。使用互斥锁或分布式锁防止缓存击穿,保证只有一个线程去查数据库。检查序列化配置是否一致,确保读写使用相同的序列化方式。调整内存淘汰策略,比如LRU或TTL,避免关键数据被删除。还可以用布隆过滤器过滤无效请求,防止大量不存在的key查询。 需要结合用户的代码示例,比如引用[1]中的get方法逻辑是否有问题,是否存在先检查存在再获取导致的竞态条件。用户提到的getCacheObject可能也存在类似的逻辑错误,需要检查是否存在类似的“先检查存在再获取”的问题,导致在检查后、获取前缓存过期,从而返回null。 总结起来,可能的原因包括缓存未命中、缓存击穿、缓存穿透、序列化问题、内存淘汰、代码逻辑问题等。解决方法对应每个原因进行处理,比如值缓存、互斥锁、缓存预热、检查序列化配置、调整内存策略、修复代码逻辑等。</think>### Redis的getCacheObject返回null原因及解决方法 #### 一、可能原因分析 1. **缓存未命中** - 数据未加载到缓存中(如首次访问、缓存预热未完成) - 缓存已过期或主动删除(如设置了TTL或手动执行`DEL`命令)[^2] 2. **缓存击穿(Hot Key失效)** - 高并发场景下,某个热点Key过期后大量请求直接穿透到数据库[^2] 3. **缓存穿透(无效Key请求)** - 频繁查询数据库中不存在的数据(如恶意攻击或参数异常),导致缓存未存储无效Key[^2] 4. **序列化/反序列化问题** - 缓存存储与读取时使用的序列化方式不一致(如Jackson与FastJson混用)[^3] - 反序列化失败导致返回`null`(如字段类型不匹配) 5. **内存淘汰策略触发** - Redis内存不足时,根据`maxmemory-policy`自动淘汰部分Key[^2] 6. **代码逻辑缺陷** - 存在类似“先检查Key存在,再获取值”的竞态条件(如引用[1]中的问题) --- #### 二、解决方案 ##### 1. 值缓存策略 ```java public Object getCacheObjectWithNull(String key) { Object value = redisTemplate.opsForValue().get(key); if (value == null) { value = db.query(key); // 数据库查询 redisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS); // 缓存值 } return value; } ``` - **作用**:即使数据库返回`null`,仍缓存值并设置短过期时间(如300秒),避免重复穿透[^2] ##### 2. 缓存击穿防护 ```java public Object getWithMutex(String key) { Object value = redisTemplate.opsForValue().get(key); if (value == null) { synchronized (this) { // 分布式环境下需用Redis分布式锁 value = redisTemplate.opsForValue().get(key); if (value == null) { value = db.query(key); redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES); } } } return value; } ``` - **作用**:通过互斥锁限制单一线程重建缓存 ##### 3. 布隆过滤器 ```java // 初始化布隆过滤器 BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(), 1000000, 0.01); public Object getWithBloomFilter(String key) { if (!bloomFilter.mightContain(key)) { return null; // 直接拦截无效请求 } return redisTemplate.opsForValue().get(key); } ``` - **作用**:拦截明显无效的Key请求[^2] ##### 4. 序列化配置检查 确保RedisTemplate配置一致性: ```yaml spring: redis: host: localhost port: 6379 lettuce: pool: max-active: 8 # 使用Jackson序列化 value-default-type: com.example.Entity ``` ```java @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); return template; } ``` ##### 5. 内存优化策略 - 调整`redis.conf`配置: ```conf maxmemory 2gb maxmemory-policy allkeys-lru # 优先淘汰最近最少使用的Key ``` --- #### 三、排查步骤 1. **检查Key是否存在** ```bash redis-cli EXISTS your_key ``` 2. **查看TTL剩余时间** ```bash redis-cli TTL your_key ``` 3. **验证序列化数据** ```bash redis-cli GET your_key # 检查原始数据格式 ``` 4. **监控内存使用** ```bash redis-cli INFO memory ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值