一、Spring bean
1.1、概述
一句话,被Spring容器管理的bean就是Spring bean。
二、Java bean VS Spring bean
2.1、概述
Java bean是程序员自己new 出来的,Spring bean是Spring工厂创建出来的。
三、配置bean的方式
3.1、概述
所谓配置bean,是指如何将一个普通的Java类交由Spring容器进行管理。
3.2、配置方式
# 第一种
xml
# 第二种
xml + 注解
# 第三种
JavaConfig
# 第四种
@Import
四、@Component vs @Bean
4.1、概述
@Component是Spring中的一个注解,通常标识在一个类上面,用于说明该类是被Spring容器管理的,其内部是通过反射机制调用构造方法完成实例化,@Bean也是一个注解,标识在方法上,通常和@Configuration注解搭配使用,其方法名就是bean的name,通过这种方式程序员可以自己控制bean的实例化过程。
4.2、案例
4.2.1、Car
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:28
* @Description:
*/
@Component
@Getter
@Setter
public class Car implements Serializable {
private String name;
private String description;
public Car() {
System.out.println("Car的无参构造执行了...");
}
public Car(String name, String description) {
this.name = name;
this.description = description;
System.out.println("Car的有参构造执行了...");
}
}
4.2.2、Tank
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:31
* @Description:
*/
@ToString
@Getter
@Setter
public class Tank implements Serializable {
private String name;
private String description;
private String producer;
public Tank() {
System.out.println("Tank的无参构造执行了...");
}
public Tank(String name, String description, String producer) {
System.out.println("Tank的有参构造执行了...");
this.name = name;
this.description = description;
this.producer = producer;
}
}
4.2.3、MySpringConfig
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:29
* @Description:
*/
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {
@Bean
public Tank tank() {
Tank tank = new Tank("东风0001","加满油能跑10000公里","中国");
return tank;
}
}
4.2.4、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
Car car = context.getBean("car", Car.class);
Tank tank = context.getBean("tank", Tank.class);
log.info("car:{},tank:{}", car, tank);
}
}
五、 Spring bean线程安全问题
5.1、概述
我们知道Spring中的bean,默认情况下是单例的,那么Spring中的bean是线程安全的吗?这个需要分情况考虑,bean中是否存在成员变量?bean中的成员变量是怎么处理的?...,针对bean的状态会有不同的处理方案:
情况一:bean是单例的;
情况二:bean是多例的(不会存在线程安全问题);
出现线程安全问题的原因:单实例bean中存在成员变量,并且有对这个bean进行读写的操作,因此出现了线程安全的问题。
5.2、演示Spring bean存在线程安全
5.2.1、UserService
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:55
* @Description:
*/
@Service
public class UserService {
private String username;
public String welcome(String name) {
username = "welcome " + name;
try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
return username;
}
}
5.2.2、MySpringConfig
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:29
* @Description:
*/
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {
}
5.2.3、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
UserService userService = context.getBean(UserService.class);
Random r = new Random();
String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int index = r.nextInt(5);
String name = nameArray[index];
log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService.welcome(name));
}, "线程" + i).start();
}
}
}
5.3、解决方法
上面代码演示了Spring中的bean的确存在着线程安全问题,出现问题我们要解决问题,针对Spring中bean中存在的线程安全,我们可以通过以下方式进行解决:
方案一:将成员变量修改为局部变量(单例bean);
方案二:使用ThreadLocal(单例bean);
方案三:使用同步锁synchronized(单例bean);
方案四:将单例bean设置为多例的;
案例代码如下
5.4、将成员变量修改为局部变量(单例bean)
5.4.1、UserService2
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:55
* @Description: 单例bean线程不安全(解决方式一:将成员变量修改为局部变量)
*/
@Service
public class UserService2 {
public String welcome(String name) {
String username = "welcome " + name;
try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
return username;
}
}
5.4.2、MySpringConfig(同上)
5.4.3、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
UserService2 userService2 = context.getBean(UserService2.class);
Random r = new Random();
String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int index = r.nextInt(5);
String name = nameArray[index];
log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService2.welcome(name));
}, "线程" + i).start();
}
}
}
5.5、使用ThreadLocal(单例bean)
5.5.1、UserService3
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:55
* @Description: 单例bean线程不安全(解决方式二:使用ThreadLocal)
*/
@Service
public class UserService3 {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
public String welcome(String name) {
threadLocal.set("welcome" + name);
try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
return threadLocal.get();
}
}
5.5.2、MySpringConfig(同上)
5.5.3、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
UserService3 userService3 = context.getBean(UserService3.class);
Random r = new Random();
String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int index = r.nextInt(5);
String name = nameArray[index];
log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService3.welcome(name));
}, "线程" + i).start();
}
}
}
5.6、 使用同步锁synchronized(单例bean)
5.6.1、UserService4
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:55
* @Description: 单例bean线程不安全(解决方式三:使用同步锁synchronized)
*/
@Service
public class UserService4 {
private String username;
public synchronized String welcome(String name) {
username = "welcome " + name;
try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
return username;
}
}
5.6.2、MySpringConfig(同上)
5.6.3、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
UserService4 userService4 = context.getBean(UserService4.class);
Random r = new Random();
String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int index = r.nextInt(5);
String name = nameArray[index];
log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService4.welcome(name));
}, "线程" + i).start();
}
}
}
5.7、 将单例bean设置为多例的
5.7.1、UserService5
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/26 14:55
* @Description: 单例bean线程不安全(解决方式四:将单例bean设置为多例的)
*/
@Scope("prototype")
@Service
public class UserService5 {
private String username;
public String welcome(String name) {
username = "welcome " + name;
try {Thread.sleep(100);} catch (Exception e) {e.printStackTrace();}
return username;
}
}
5.7.2、MySpringConfig(同上)
5.7.3、AopFullAnnotationMainApp
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 15:14
* @Description:
*/
@Slf4j
public class AopFullAnnotationMainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
Random r = new Random();
String[] nameArray = new String[]{"张三", "李四", "王五", "赵六", "钱七"};
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
UserService5 userService5 = context.getBean(UserService5.class);
int index = r.nextInt(5);
String name = nameArray[index];
log.info("当前线程:{},当前索引:{},当前name的值:{},当前取出的值:{}", Thread.currentThread().getName(), index, name, userService5.welcome(name));
}, "线程" + i).start();
}
}
}