Spring学习03-[Spring容器核心技术IOC学习进阶]

IOC学习进阶

@Order

可以改变自动注入的顺序

比如有个animal的接口,里面两个实现类 Cat和Dog。这两个都注册为bean。此时注入的时候,就可以用list来进行接收,表示两个都进行注入。
如果还用animal来进行接收,那就会报错,因为@Autowired根据类型找到两个,然后根据名称找,此时找不到就报错

  • 类型接口
  • animal
java 复制代码
public interface Animal {
    void eat();
}
  • 实现类
  • Cat
java 复制代码
@Component
public class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("猫吃老鼠");
    }
}
  • Dog
java 复制代码
@Component
public class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
  • 使用
java 复制代码
  @Autowired
    private List<Animal> animal;

    @Test
    public void testOrder(){
        System.out.println(animal);
    }


可以看到先输出Cat,再输出Dog(先注入了Cat,再注入了Dog)

使用@Order改变注入顺序

此时我们可以用@Order注解自定义注入顺序

用法:@Order(数值)---数值小的先注入
让Dog先注入,Cat后注入

java 复制代码
@Component
@Order(1)
public class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}
java 复制代码
@Component
@Order(2)
public class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("猫吃老鼠");
    }
}
  • 测试
    可以和上面未加@Order进行对比,加了@Order后Dog先进行了注入

实现Ordered接口,重写getOrder方法来改变自动注入顺序

这种方式了解即可

java 复制代码
@Component
@Order(1)
public class Dog implements Animal, Ordered {
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

@DependsOn

改变bean的创建顺序

比如下面的 B负责数据库连接,A负责数据库查询,肯定是B先创建bean,此时就可以用@DependsOn进行调整两个bean的创建顺序

使用

  • 不加 @DependsOn注解
  • A:数据库查询
java 复制代码
@Component
public class A {
    public A() {
        System.out.println("数据库查询");
    }
}
  • B:数据库连接
java 复制代码
@Component
public class B {
    public B() {
        System.out.println("数据库连接");
    }
}


此时可以看到在不加@DependsOn注解的时候,数据库查询bean先创建,明显不对

  • 加 @DependsOn注解
    因为数据库查询依赖数据库连接所以在数据库查询上加@DependsOn注解
    表示:A依赖B,在创建A的时候先创建B
java 复制代码
@Component
@DependsOn("b")
public class A {
    public A() {
        System.out.println("数据库查询");
    }
}
  • 测试
    可以看到,数据库连接先创建,数据库查询后创建

@Lazy

懒加载bean,默认是true:表示懒加载,可以设置成false,表示非懒加载

全局设置-设置所有bean启动时候懒加载

通过配置文件进行设置

@Scope

默认是单例的

bean是单例的,会不会有线程安全问题


正常情况下应该是1000块钱

模拟两个线程取钱和存钱

  • AccountService
java 复制代码
@Component
public class AccountService {

    public int balance=1000;

    // 存钱
    public void add(int money) throws InterruptedException {
        int newMoney = balance + money;
        TimeUnit.SECONDS.sleep(2);
        balance=newMoney;
    }

    //取钱
    public void sub(int money) throws InterruptedException {
        int newMoney = balance - money;
        TimeUnit.SECONDS.sleep(1);
        balance=newMoney;
    }
}
  • 测试类
java 复制代码
@SpringBootTest(classes = StudyApplicaiton.class)
public class AccountServiceTeset {

    @Test
    public void test(@Autowired AccountService accountService, @Autowired AccountService accountService1) throws InterruptedException {
        new Thread(()->{
            try {
                accountService.add(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();


        new Thread(()->{
            try {
                accountService1.sub(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();

        TimeUnit.SECONDS.sleep(3);

        System.out.println(accountService.balance);
        System.out.println(accountService1.balance);
    }
}

可以看到出现问题了,正确应该是1000

代码解释:存钱线程先执行,存钱后睡两秒钟。然后取钱线程执行,执行完之后banlance为500,此时存钱线程睡眠结束,把banlance更新为1500

这就是典型的多线程多任务的多线程案例。存钱任务对应一个线程,取钱任务对应一个任务。

火车售票是典型的多线程单任务多线程案例。多个线程对应一个售票任务。

解决方式

修改bean为多例模式,或者用threadlocal在每个线程复制一份工作变量

@Conditional:条件注解

用于动态决定某个bean是否生效

使用:@Conditional(xxx.class)条件注解,里面带一个实现了Condition接口的类。这个实现了Condition接口的类会重写matches方法,返回值是boolean,如果返回true,满足条件bean可以注册,否则不满足条件无法注册bean

使用

  • 实现了condition的类
java 复制代码
public class UserCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return true;
    }
}
  • bean
java 复制代码
@Conditional(UserCondition.class)
@Component
public class User {

}
  • 测试

bean的生命周期回调方法

什么是bean的生命周期



初始化

bean的初始化可以实现InitializingBean方法也可以在方法上加@PostConstruct注解

循环依赖



需要注意的是原生Spring支持循环依赖,但是SpringBoot不支持循环依赖。如果要让SpringBoot也支持循环依赖,需要在配置文件中开启循环依赖

yaml 复制代码
spring:
  main:
    allow-circular-references: true

解决循环依赖

常用的有spring的三级缓存,或者对某个依赖bean加@lazy注解

相关推荐
时间静止不是简史6 小时前
生产事故排查:Redis 宕机导致补偿队列消费线程死亡,通话录音未上送业务平台
java·redis
2301_32241428049 小时前
基于SpringBoot+Vue的休闲零食超市管理系统的设计与实现59004-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·vue.js·spring boot·redis·mysql·决策树·推荐算法
Canmag 兴隆磁性11 小时前
中大型钕铁硼产品的磁通测量与自动分选:一条产线自动化的工程实现思路
运维·学习·自动化·永磁材料
金銀銅鐵11 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
福建佰胜张工12 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
catoop12 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x12 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事13 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客13 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn18813 小时前
记录本地安装nacos服务的方法及异常
java·开发语言