项目遇到的问题:1. 在不被Spring容器管理的对象中无法注入Spring管理的对象

前言

  1. Redis缓存实现
    1.1 自定义Redis缓存实现类
    1.2 创建生成Spring工厂工具类
  2. 小结

前言

1.项目场景:开发中遇到使用Mybatis实现二级缓存。项目中涉及>到使用Redis来存储二次查询的数据,但对于存储的对象类型我们需要去自定义一个RedisCache类并实现’'Cache"以此来重新里面>的"put"和“get”方法。


2 但是出现一个问题,我们在自定义这个RedisCache中使用@Autowired注解注入操作Redis的"RedisTemplate对象"的时候,
显示为null,通过研究发现我发先’非Spring容器管理的类中’去使用
/注入由Spring容器管理的RedisTemplate对象。


3.解决方案为:创建一个Spring工厂,然后在RedisCache这个自定义类中引入此工厂,在通过工厂的getBean() 获取到RedisTemplate这个对象即可。

1. Redis缓存实现

1.1 自定义Redis缓存实现类

//自定义Redis缓存实现
public class RedisCache implements Cache {

    //当前放入缓存的mapper的namespace
    private final String id;

    //必须存在构造方法
    public RedisCache(String id) {
        System.out.println("id:=====================> " + id);
        this.id = id;
    }

    //返回cache唯一标识
    @Override
    public String getId() {
        return this.id;
    }


    //缓存放入值  redis RedisTemplate   StringRedisTemplate
    @Override
    public void putObject(Object key, Object value) {
        System.out.println("key:" + key.toString());
        System.out.println("value:" + value);
//        //通过application工具类获取redisTemplate
//        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        //使用redishash类型作为缓存存储模型  key   hashkey  value
        getRedisTemplate().opsForHash().put(id.toString(),getKeyToMD5(key.toString()),value);


        //根据UserDAO模块,设置对应的缓存超时时间
        if(id.equals("com.zk.dao.UserDAO")){
            //缓存超时  client  用户   client  员工
            getRedisTemplate().expire(id.toString(),1, TimeUnit.HOURS);
        }

        //若是CityDAO模块,设置另一个超时时间
        if(id.equals("com.zk.dao.CityDAO")){
            //缓存超时  client  用户   client  员工
            getRedisTemplate().expire(id.toString(),30, TimeUnit.MINUTES);
        }

        //.....根据不同业务模块,设置不同缓存超时时间




    }

    //获取中获取数据
    @Override
    public Object getObject(Object key) {
        System.out.println("key:" + key.toString());
//        //通过application工具类获取redisTemplate
//        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        //根据key 从redis的hash类型中获取数据
        return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString()));
    }


    /**
     * 1. removeObject()此烦啊发暂时没什么用
     *      注意:这个方法为mybatis保留方法 默认没有实现 后续版本可能会实现
     */
    @Override
    public Object removeObject(Object key) {
        System.out.println("根据指定key删除缓存");
        return null;
    }

    /**
     * 1. 我们执行redis的删除方法,默认走的是clear()这个方法
     *      对于redis执行增删改操作,都会走此方法将redis的缓存清空掉
     */
    @Override
    public void clear() {
        System.out.println("清空缓存~~~");
        //清空namespace
        getRedisTemplate().delete(id.toString());//清空缓存
    }

    //用来计算缓存数量
    @Override
    public int getSize() {
        //获取hash中key value数量
        return getRedisTemplate().opsForHash().size(id.toString()).intValue();
    }


    //封装redisTemplate。定义成私有,表示该方法只能内部被调用使用
    private RedisTemplate getRedisTemplate(){
        //通过application工具类获取redisTemplate
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}



1.2 创建生成Spring工厂工具类

package com.zk.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

//用来获取springboot创建好的工厂

/**
 * 1. 由于我们在使用分布式缓存时,需要用到'RedisTemplate'或'StringRedisTemplate'其中的一个对象。
 * 我们这里分布式缓存用到对象,所以这里使用"RedisTemplate"这个对象。
 *      ➀. 如果RedisTemplate这个类是工厂管理,我们就可以直接使用@Autowired注入这个对象,但是这个类的实例化
 *      是由mybatis实例化的(创建的)-->而mybatis中的Cache对象不是由工厂去管理的对象,我们要在自定义的
 *      RedisCache这个类下面去注入并使用'RedisTemplate',是无法注入的。
 *
 *      ➁. 因此若要拿到'RedisTemplate'这个对象,就需要到Spring的工厂中去获取,而"ApplicationContext这个工厂"是
 *      我们最大的工厂,因此我们就需要获取到ApplicationContext这个工厂,然后根据这个工厂中的"getbean(String bean)"
 *      传入要获取的对象字符串名,去工厂中获取'RedisTemplate'这个对象。
 *
 *
 * 2. 所以我们可以去创建类,比如创建一个(ApplicationContextUtils)类,并让这个类去实现'ApplicationContextAware接口',
 * 此时当我们去实现'ApplicationContextAware接口后'就代表springboot它会自动帮我们创建一个工厂,当springboot帮我们创建号
 * 工厂后,springboot会通过"setApplicationContext()"这个方法以参数的形式给我们返回创建号的这个工厂。
 *
 * 3. 之后我们就可以获取到这个"ApplicationContext工厂"-->并调用getBean()获取我们需要的"RedisTemplate对象"执行
 * redis中的方法。
 *      --->你日后如果给我传进来的是"RedisTemplate",那么入参就是小写的'redisTemplate'。
 *
 *
 */
@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    //
    /**
     * 1. 将springboot给我们创建的工厂定义为全局静态变量,将此工厂对象保留下来
     * 2. 下面"setApplicationContext()"给我们返回的工厂我们就用此对象去接受springboot
     * 给我们返回的这个'ApplicationContext'工厂。
     */
    private static ApplicationContext applicationContext;

    //将创建好工厂以参数形式传递给这个类
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    /**
     * 1. 日后我们可以通过springboot给我们创建的这个工厂,去获取对应我们所需要的
     * 对象的方法,通过getBean()去获取
     * 2. 下面方法的入参我们定义称我们锁需要获取的对象的小写形式,他就会给我们返回具体的对象。
     */
    //提供在工厂中获取对象的方法 //RedisTemplate  redisTemplate
    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }

}

2. 问题小结

1. 在不被Spring容器管理的对象中无法注入Spring管理的对象问题
的小结。

⑴ 简单来说就是:对象RedisCache独立于spring容器(RedisCache实际
上是由mybatis框架实现初始化,myabtis所管理的),而处理Redis的
RedisTemplate实例由spring容器管理。如果直接在RedisCache类中使
用@Autowired注入RedisTemplate对象,在运行时RedisTemplate实例
对象将是null。

⑵ 导致错误。因为RedisTemplate实例对象只有在spring容器中才能使
用,那么在RedisCache这个非spring容器管理的对象想要使用的话,就
要先取得spring的上下文对象,然后再通过上下文对象去获取
RedisTemplate实例对象即可。



1.1 上下文对象的获取,项目采用的是springboot,可以通过以下方式拿到spring的上下对象。

⑴ 方式一:定义一个持有上下文对象的类,将该类交由spring容器管理,然后通过该类去获取上下文对象
    @Component
    public class ApplicationContextHolder implements ApplicationContextAware {
        private static ApplicationContext context;

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            context = applicationContext;
        }

        public static ApplicationContext getContext() {
            return context;
        }
    }

  
⑶ 目标对象的生成
    ➀ 通过调用context的getBean方法获取在spring容器中管理的对
    象。下面获取RedisTemplate对象的方法。
 
    # 通过方法ApplicationContextHolder中的静态context对象:
    
private static SimpMessagingTemplate simpMessagingTemplate = ApplicationContextHolder.getContext()
.getBean(RedisTemplate.class);


-
上一篇:MarkDwon学习


下一篇:SpringBoot开发二十四-Redis入门以及Spring整合Redis