问题背景
这个问题,其实分为四个问题,思路是一致的。
- 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的调用
}