工作中我们想写个线程是很简单的,方式也很多,我在之前的文章Java实现多线程方式详解也介绍过,就不多讲了,但是实际工作中,尤其是采用spirng注解的方式的情况下,有时我们就要在线程中调用Spring的Bean,很多小伙伴还不知道怎么处理,我们今天就来聊聊Java多线程中调用Spring的Bean的几种方式,本文中Spring Boot版本为2.5.2,JDK环境为 1.8,本文中使用到的依赖如下:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.5.2version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.14version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>compilescope>
dependency>
本文使用@PostConstruct 进行测试,@PostConstruct 是一个java的注解,它修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。
当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。前提是该类上有IOC相关注解才会生效,例如@Component(当然使用xml配置的bean信息也可以)
SpringBeanUtil.java
package com.alian.multithread.common;
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 {
//定义静态ApplicationContext
private static ApplicationContext applicationContext;
//重写接口的方法,该方法在启动项目的时候会自动执行
//该类上有IOC相关注解才会生效,例如@Component
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
//通过beanName获取Bean
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
return (T) applicationContext.getBean(beanName);
}
//通过beanClass获取Bean
public static <T> T getBean(Class<T> beanClass) {
return applicationContext.getBean(beanClass);
}
}
我们简单写个发送短信的服务类
PushSmsService.java
package com.alian.multithread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class PushSmsService {
public void pushSms() {
log.info("推送短信服务子线程【{}】执行", Thread.currentThread().getName());
}
}
我们再写一个发送短信的线程类
PushSmsRunnable.java
package com.alian.multithread.thread;
import com.alian.multithread.common.SpringBeanUtil;
import com.alian.multithread.service.PushSmsService;
public class PushSmsRunnable implements Runnable{
@Override
public void run() {
//获取到我们需要的bean
PushSmsService pushSmsService = SpringBeanUtil.getBean(PushSmsService.class);
pushSmsService.pushSms();
}
}
写个简单的测试方法
TestPushSmsService .java
package com.alian.multithread.service;
import com.alian.multithread.thread.PushSmsRunnable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Slf4j
@Service
public class TestPushSmsService {
@PostConstruct
public void testPushSms() {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(new PushSmsRunnable());
thread.start();
}
}
}
运行结果:
2021-11-14 09:38:41 363 [Thread-4] INFO pushSms 11:推送短信服务子线程【Thread-4】执行
2021-11-14 09:38:41 363 [Thread-3] INFO pushSms 11:推送短信服务子线程【Thread-3】执行
2021-11-14 09:38:41 363 [Thread-5] INFO pushSms 11:推送短信服务子线程【Thread-5】执行
我们还可以在线程类中通过构造方法注入所需要的bean,我们先写一个发送邮件的服务类
SendEmailService.java
package com.alian.multithread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SendEmailService {
public void sendEmail() {
log.info("发送邮件服务子线程【{}】执行", Thread.currentThread().getName());
}
}
我们写一个发送邮件的线程类
SendEmailRunnable.java
package com.alian.multithread.thread;
import com.alian.multithread.service.SendEmailService;
public class SendEmailRunnable implements Runnable{
private SendEmailService sendEmailService;
public SendEmailRunnable(SendEmailService sendEmailService){
this.sendEmailService=sendEmailService;
}
@Override
public void run() {
sendEmailService.sendEmail();
}
}
TestSendEmailService.java
package com.alian.multithread.service;
import com.alian.multithread.thread.SendEmailRunnable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Slf4j
@Service
public class TestSendEmailService {
@Autowired
private SendEmailService sendEmailService;
@PostConstruct
public void testSendEmail() {
for (int i = 0; i < 4; i++) {
Thread thread = new Thread(new SendEmailRunnable(sendEmailService));
thread.start();
}
}
}
运行结果:
2021-11-14 09:50:37 991 [Thread-4] INFO sendEmail 11:发送邮件服务子线程【Thread-4】执行
2021-11-14 09:50:37 991 [Thread-5] INFO sendEmail 11:发送邮件服务子线程【Thread-5】执行
2021-11-14 09:50:37 991 [Thread-3] INFO sendEmail 11:发送邮件服务子线程【Thread-3】执行
2021-11-14 09:50:37 991 [Thread-6] INFO sendEmail 11:发送邮件服务子线程【Thread-6】执行
我们先一个个性化服务类
PersonalService.java
package com.alian.multithread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class PersonalService {
public void personalBuss() {
log.info("个性化服务子线程【{}】执行", Thread.currentThread().getName());
}
}
TestPersonalService.java
package com.alian.multithread.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Slf4j
@Service
public class TestPersonalService {
@Autowired
private PersonalService personalService;
@PostConstruct
public void execute() {
for (int i = 0; i < 5; i++) {
new PersonalThread().start();
}
}
private class PersonalThread extends Thread {
@Override
public void run() {
TestPersonalService.this.personalService.personalBuss();
}
}
}
运行结果:
2021-11-14 09:46:55 457 [Thread-3] INFO personalBuss 11:个性化服务子线程【Thread-3】执行
2021-11-14 09:46:55 457 [Thread-6] INFO personalBuss 11:个性化服务子线程【Thread-6】执行
2021-11-14 09:46:55 457 [Thread-7] INFO personalBuss 11:个性化服务子线程【Thread-7】执行
2021-11-14 09:46:55 457 [Thread-5] INFO personalBuss 11:个性化服务子线程【Thread-5】执行
2021-11-14 09:46:55 457 [Thread-4] INFO personalBuss 11:个性化服务子线程【Thread-4】执行
关于线程参数的传递:这三种方式,如果是最终的业务类需要传入参数,则可以通过线程类的有参构造方法传递到线程内。