ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-08-04 10:31:21  阅读:268  来源: 互联网

标签:缓存 管理 对象 Spring public RedisTemplate id redisTemplate


前言

  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);


-

标签:缓存,管理,对象,Spring,public,RedisTemplate,id,redisTemplate
来源: https://blog.csdn.net/llzz_zzll/article/details/119377728

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有