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

本文详细介绍了在WebSocket中如何使用Spring框架的Bean注入,包括在WebSocketHandler中手动获取bean实例的方法,以及在SpringBoot+WebSocket环境下如何正确地进行Bean注入,避免NullPointException的发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题背景

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

  • 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的调用
}
在使用Apache POI结合EasyPOI等库进行Excel导入导出操作时,经常会遇到将实体中标注了`@Head`注解的字段与其对应的中文标题相绑定的需求。为了方便地获取这些字段的中文名称,在实体上可以配合其他元数据注解如`@ExcelEntity`或直接利用反射机制读取相关属性的信息。 下面以一种常见的方式来展示如何在一个标记有`@Sheet`的中获得所有标注为`@Head`字段的中文名: ### 方案概述 1. **引入必要的依赖**:确保项目中包含了Apache POI以及EasyPOI(如果选用该库的话)的相关jar包; 2. **定义实体及相应注解**:创建一个用于表示Excel某一行记录的对象模型,并在其各成员变量前加上合适的标签说明其角色; 3. **编写工具方法**:借助Java内置API解析目标Class实例内的私有/公有Field元素,过滤出那些携带有所需元信息的目标组件并整理成易于访问的形式返回给调用者; ### 实现细节 #### 引入依赖 对于Maven工程来说,应在项目的`pom.xml`里加入如下片段来声明所需的外部资源引用: ```xml <!-- Apache POI Core Library --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${apache.poi.version}</version> </dependency> <!-- Optional: EasyPoi extension library for more convenient operations --> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.6.8</version> </dependency> ``` 请注意替换`${apache.poi.version}`为你想要使用的具体版本号。 #### 定义实体 考虑这样一个场景——我们拥有一个人事档案管理系统里的员工基本信息表单模板,它应该具备姓名、性别、出生日期这几个基本要素。此时就可以设计似这样的POJO结构: ```java import cn.afterturn.easypoi.excel.annotation.Excel; import cn.afterturn.easypoi.excel.annotation.ExcelTarget; // 指明当前是Excel数据映射的目的载体之一 @ExcelTarget("employeeInfo") public class EmployeeDTO implements Serializable { private static final long serialVersionUID = 1L; // 使用EasyPoi提供的@Excel代替原生@Head简化开发过程 @Excel(name="编号", orderNum = "0") private Integer id; @Excel(name="名字", orderNum = "1") private String name; @Excel(name="性别", replace={"男_1","女_2"}, orderNum = "2") private Integer gender; @Excel(name="出生年月日", format = "yyyy-MM-dd", orderNum = "3") private Date birthDate; // Getters and setters... } ``` #### 编写辅助函数 有了上述准备之后,现在我们可以着手编写专门负责检索带有`@Excel/@Head`修饰符的所有非静态成员变量的方法了。这里提供了一种基于反射技术实现思路的例子: ```java import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 获取指定型内所有已由{@link Excel} 或 {@link Head}(视实际环境而定) 注释过的公共&私有字段, * 并将其对应的显示文本抽取出来形成关联列表。 */ public class FieldUtils { /** * 根据传入的class对象查找其中所有的@Excel/@Head字段,并收集它们的序号与中文名称之间的对应关系 * * @param clazz 要分析的具体别 * @return 包含找到的字段位置和文字解释的地图集合 */ public static Map<Integer, String> getAnnotatedFieldNames(Class<?> clazz) { List<Field> annotatedFields = new ArrayList<>(); while (clazz != null && !Object.class.equals(clazz)) { for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Excel.class)) { annotatedFields.add(field); } else if (/* 如果不是EasyPoi则判断是否含有自定义或者其他第三方*/ false) {} } clazz = clazz.getSuperclass(); // 继承链向上追溯直到顶级父级为止 } Map<Integer, String> result = new HashMap<>(annotatedFields.size()); for (Field f : annotatedFields) { Excel excelAnn = f.getAnnotation(Excel.class); if (excelAnn == null || StringUtils.isEmpty(excelAnn.name())) continue; int index = NumberUtils.toInt(StringUtils.defaultIfBlank(excelAnn.orderNum(), "-1"), -1); if(index >= 0){ result.put(index, excelAnn.name()); } } return Collections.unmodifiableMap(result); // 返回不可变视图防止外界误修改内部状态 } } // Usage example within a service method or controller action: Map<Integer, String> columnTitles = FieldUtils.getAnnotatedFieldNames(EmployeeDTO.class); for(Map.Entry<Integer, String> entry : columnTitles.entrySet()){ System.out.println("Column Index:" +entry.getKey()+ ", Title:"+entry.getValue()); } ``` 以上代码展示了怎样通过递归遍历整个继承体系直至根节点处搜集到所有符合条件的候选者。然后按照各自所属的位置索引组织进哈希表当中,便于后续快速定位某个特定单元格应当填充什么样的提示文案。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值