关于controller调用controller/service调用service/util调用service/websocket中autowired的解决方法

问题背景

这个问题,其实分为四个问题,思路是一致的。

  • controller调用controller
  • service调用service
  • util调用service
  • websocket中autowired
  • SpringUtils.getBean()获取bean

修订版本

2024-04-01 补充

  • 新增WebSocketHandler

2020-11-15 补充

  • 优化SpringUtil.getBean方法,当使用本地调试Main方法的时候可以忽略错误,因为不存在beanFactory
  • 优化markdown代码着色

2020-01-05 补充

  • 新增SpringUtils.getBean()的工具类,在Spring项目内可以很方便使用。

WebSocketHandler

在WebSocket中,无法直接使用@Autowired注解进行依赖注入。因为WebSocket是基于HTTP协议的,而@Autowired是Spring框架提供的依赖注入功能,它们之间没有直接的关系。

但是,你可以通过其他方式来实现类似的功能。例如,你可以在WebSocket的初始化方法中手动获取需要的bean实例:
在这里插入图片描述

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyWebSocketHandler extends TextWebSocketHandler {

    private ApplicationContext applicationContext;

    public MyWebSocketHandler(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 在这里手动获取需要的bean实例
        MyService myService = applicationContext.getBean(MyService.class);
        // 使用myService进行操作
    }
}

这样,虽然不能直接使用@Autowired注解,但仍然可以实现依赖注入的功能。

调用实战

例如我现在有个AppConfig,需要在EmailUtil里面调用

@Component
@Data
@ConfigurationProperties(prefix = "system") // 接收application.yml中的myProps下面的属性
public class AppConfig {
    public String filepath;
    public String urlpath;
}

然后EmailUtils中定义一个静态的AppConfig

@Component
public class EmailUtil {
    @Autowired
    public static AppConfig appConfig;

 	public static void sendActiveEmail(String toEmail,String checkCode,HttpServletRequest request){
        EmailUtil.sendEmail(toEmail,"iXXX激活邮件", "您正在进行iXXX账号认证,请点击该链接激活:"+appConfig.getUrlpath()+"/xxxx/active/"+toEmail+"/"+checkCode+" 。如非本人操作,请忽略该信息。");
    }
 }

然后随便定义一个config,或者找到之前已经定义的,追加以下set代码

@Configuration
public class ConfigurationFilter{

	@Autowired
	public void setAppConfig(AppConfig appConfig){
		EmailUtil.appConfig=appConfig;
	}
	
}

这样就完成了,发送的时候可以成功获取对应的值,不会发生NullPointException。

WebSocket注入@Bean@Autowired

在WebSocket也是这样注入,因 SpringBoot+WebSocket 对每个客户端连接都会创建一个 WebSocketServer(@ServerEndpoint 注解对应的) 对象,Bean 注入操作会被直接略过,因而手动注入一个全局变量

@ServerEndpoint("/im/{userId}/{toUserId}")
@RestController
public class ImController {
     public static ChatMessageRepository chatMessageRepository;
     //......
 }

可以配置在WebSocketConfig里面

/**
 * 开启WebSocket支持
 * @author zhengkai
 */
@Configuration
public class WebSocketConfig {
    /**
     * ServerEndpointExporter 用于扫描和注册所有携带 ServerEndPoint 注解的实例,
     * 若部署到外部容器 则无需提供此类。
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    /**
     * 因 SpringBoot WebSocket 对每个客户端连接都会创建一个 WebSocketServer(@ServerEndpoint 注解对应的) 对象,Bean 注入操作会被直接略过,因而手动注入一个全局变量
     *
     * @param messageService
     */
    @Autowired
    public void setMessageService(ChatMessageRepository chatMessageRepository) {
        ImController.chatMessageRepository = chatMessageRepository;
    }
}

SpringUtils.getBean

根据上下文获取beanConfigurableListableBeanFactory.getBean(),可以有两种参数类型:

  • 传入String格式的类名
  • 传入类.Class的
package com.softdev.system.demo.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

/**
 * SpringUtils
 * @author zhengkai.blog.csdn.net
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor {

    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    public static ConfigurableListableBeanFactory getBeanFactory() {
        return beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        if(getBeanFactory()==null){
            //zhengkai.blog.csdn.net
            System.out.println("本地调试Main模式,没有BeanFactory,忽略错误");
            return null;
        }else{
            T result = (T) getBeanFactory().getBean(name);
            return result;
        }
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param name
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> name) throws BeansException {
        if(getBeanFactory()==null){
            //zhengkai.blog.csdn.net
            System.out.println("本地调试Main模式,没有BeanFactory,忽略错误");
            return null;
        }else{
            T result = (T) getBeanFactory().getBean(name);
            return result;
        }
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }

}

直接使用SpringUtils.getBean(StringRedisTemplate.class);调出对应的bean

/**
 * WebSocketServer
 * @author zhengkai.blog.csdn.net
 */
@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketServer {

    /**
     * ServerEndpoint不支持注入,可以用SpringUtils获取IOC实例
     */
    private StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
	//....省略redisTemplate的调用
}
  • 14
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值