Springboot中Bean的注入
我们都知道,Springboot可以使用方法级别注解(@Bean)和类级别注解(@Controller、@Component、@Service等)加包扫描的方式注入Beans。实现交给Spring容器管理。这样我们就能很方便的实现,在Controller中注注入Service,在Service中注入Mapper。比较常用的就是Service的注入(@Autowired)与使用,我们会经常利用封装好的Service层调用dao层的接口进行业务逻辑应用的处理。比如Controller中注入Service来根据不同情况调用Service中的方法来完成功能实现。
自己new的无法注入
但是这其中有一个问题,就是当我们需要自己手动new对象的时候,这个对象对应的类是无法交给Spring管理的。这种情况通常发生在一些需要自己处理的参数,比如监听器类。我是使用阿里云EasyExcel对表格内容进行操作时碰到的。可以看到下图中参数列表中除了流的要求外还要有一个ReadListener的监听器。
大概场景就是需要把表格里的数据读取出来并且进行相应的数据库操作。这时候我们这个监听器对象Listener就会需要使用到Service层的方法区操作。按照平常,我们只需要在这个Listener类中@Autowired注入Service然后直接用就好了。但是这个Listener又无法交给Spring管理。如果我们注入就会发现会报错(应该是空指针异常),无法注入。因为@Autowired注入时是将类交给Springboot管理,而new出来的实例脱离了Springboot的管理,两个东西不在一个管理者管理下,所以没法联系在一起,@Autowired注入就会为null,就会出现空指针的情况。
解决办法1️⃣
既然我们在参数中new了一个Listener,那么其实我们在new的时候就可以把Service通过参数的方式传递进去,我们就可以使用了。具体实现:首先我们得在自己的Listener中添加有参构造和无参构造(参数其实就是Service) 我们其实可以从上层调用方法的时候就一步一步的把注入的Service一步步传递过来。在我们new Listener的时候,就把Service作为参数传递进这个Listener监听器类中,那我们就可以在这个监听器中随心所欲的使用Service完成我们想要的操作了。
进阶办法
New出来的数据实体类是取不到Springboot的yml参数值的
@Component
@Data
public class GptConfig {
@Value("${gpt.token}")
private String token;
}
yml的token参数
gpt:
token: Bearer sk-pG6eVk8Zju6TnkoM018hT3BlbkF
new创建实例无法加载yml的参数token为空
GptConfig gptConfig1 = new GptConfig();
String token = gptConfig.getToken();//token为空
注入的方法可以加载yml参数
@Autowired
private GptConfig gptConfig2;
String token1 = gptConfig2.getToken();//token:Bearer sk-pG6eVk8Zju6TnkoM018hT
但是某些类不想交给Spring管理,也就是没有加@Component
这样就无法使用Spring的注解注入实体类
所以需要用另一个方法,也就是借助Spring上下文的联系,通过getBean方法获取,boot中的实例对象。
GptConfig gptConfig = (GptConfig) com.example.utils.SpringBeanUtil.getBean("gptConfig");
String token3 = gptConfig.getToken();//token:Bearer sk-pG6eVk8Zju6TnkoM018hT
注意注意
com.example.utils.SpringBeanUtil.getBean("gptConfig");
getBean()里面的类名默认小写开头!!!,不然会报找不到这个bean名称
这样就能实现需求。
SpringBeanUtil工具包如下
package com.example.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringBeanUtil implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext_) throws BeansException {
applicationContext = applicationContext_;
}
/**
* 获取applicationContext
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean.
* @param name
* @return
*/
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}