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注解

相关推荐
菜鸟阿康学习编程几秒前
JavaWeb 学习笔记 XML 和 Json 篇 | 020
xml·java·前端
是小崔啊2 分钟前
Spring源码05 - AOP深入代理的创建
java·spring
等一场春雨31 分钟前
Java设计模式 八 适配器模式 (Adapter Pattern)
java·设计模式·适配器模式
索然无味io42 分钟前
XML外部实体注入--漏洞利用
xml·前端·笔记·学习·web安全·网络安全·php
一弓虽1 小时前
java基础学习——jdbc基础知识详细介绍
java·学习·jdbc·连接池
王磊鑫1 小时前
Java入门笔记(1)
java·开发语言·笔记
马剑威(威哥爱编程)1 小时前
2025春招 SpringCloud 面试题汇总
后端·spring·spring cloud
硬件人某某某1 小时前
Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)
java·开发语言·社区团购小程序·团购小程序·java社区团购小程序
程序员徐师兄1 小时前
Java 基于 SpringBoot 的校园外卖点餐平台微信小程序(附源码,部署,文档)
java·spring boot·微信小程序·校园外卖点餐·外卖点餐小程序·校园外卖点餐小程序
chengpei1472 小时前
chrome游览器JSON Formatter插件无效问题排查,FastJsonHttpMessageConverter导致Content-Type返回不正确
java·前端·chrome·spring boot·json