Sprin初学

引入
Spring容器,容器内有各种各样的组件。也可以自己添加组件,就要用到@Bean。在我目前看来,spring就是将各种代码封装起来,然后只需要使用注解@就可以实现各种操作。
我的idea目前spring项目运行代码是这个 不同版本可能不一样ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(SpringIocApplication.class)
SpringIocApplication是主类,context就是容器
容器and注入

  • 获取容器中的组件

context.getBean(name = “”||class = “”)可以是名字获取 也可以是类型获取

  • 定义

组件:框架的底层配置 通常用@Bean或者@Component来向容器内注册一个组件 每一个组件都叫Bean 把组件放进容器中 取出来方法就是getBean 相当于容器内的所有东西都是Bean 组件的四大 特性 名字 类型 对象 作用域
配置文件:指定的配置
配置类:分类管理组件的配置 配置类也是一个组件 通常用@Configuration来向容器注册一个配置类 @Configuration //告诉spring容器 这是一个配置类
创建组件对象的时机:容器启动过程中就会创建组件ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(SpringIocApplication.class) 在这个代码启动过程中就会创建对象

  • 作用域 scope

单实例:所有组件默认都是单实例的 singleton 就是容器内只允许存在一个该组件 然后需要的时候直接从容器中取
多实例:prototype 可以存在多个实例

    @Scope("prototype")
    @Bean("zhangsan")
    public Person zhangsan(){
        Person zhangsan  = new Person();
        zhangsan.setName("zhangsan");
        zhangsan.setAge(18);
        zhangsan.setGender("man");
        return zhangsan;

    }

此处为注册了一个多实例的Bean 名字叫zhangsan 如果在主程序中获得该Bean并赋给一个人叫zhangsan1代码如下
Person zhangsan1 = context.getBean("zhangsan")
如果再创建一个叫zhangsan2的人
·Person zhangsan2 = context.getBean("zhangsan")
如果放在单实例里 if语句判断zhangsan1==zhangsan2 会显示true 因为单实例 这两个实例化的对象都是同一个zhangsan 而多实例的话 就会返回false 因为这是两个不同的对象 只是对象的值一样

注意:单实例 容器启动的时候就会创建单实例
而多实例 容器启动的时候不会创建 什么时候用到 什么时候创建多实例
但是在这里可以用到@Lazy 懒加载对单实例对象 即容器启动的时候不会创建对象 也是什么时候用到什么时候创建

  • MVC分层注解

spring提供了快速的MVC分层注解
@Controller 作用于控制层的注解
@Service 作用于服务层的注解
@Repository 作用于持久层 也就是数据库 Dao包下
@Component 组件 简而言之 除去上面三个 都是Component 但是上面三个也属于Component 只是单独的分开了 便于读编码

  • @Component 和 @Configuration的区别

@Component是一个组件 spring会自动扫描并创建该类的实例 通常都是服务类 工具类
@Configuration是一个配置类 配置类下有很多的Bean注解 通常于Bean搭配使用 会显示声明Bean 然后确保单例

*FactoryBean

如果创建对象比较复杂的时候 可以用工厂方法创建

public class BYDFactory implements FactoryBean<Car> {

//调用此方法给容器中制造对象
@Override
public Car getObject() throws Exception {
    System.out.println("BYDFactory 正在制造car对象");
    Car car = new Car();
    return car;
}

FactoryBean在容器中放的组件的类型 是接口中泛型指定的类型 组件的名字是工厂自己的名字

  • 条件注册 @Condition

场景 判断当前电脑操作系统是Windows还是mac
Windows系统 容器中有bill
mac系统 容器中有jobs

首先实现一个WindowsCondition类去实现Condition接口

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //判断环境变量中的OS 包含Windows 如果包含就是Windows系统
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("OS");

        return property != null && property.contains("Windows");
    }
}

其中的context.getEvironment就是得到环境变量中的OS 因为OS下存放着系统区分 然后如果含有Windows就返回true
然后实现一个bill的Bean

@Conditional(WindowsCondition.class)
    @Bean("bill")
    public Person bill(){
        Person person = new Person();
        person.setName("比尔盖茨");
        person.setAge(22);
        person.setGender("Male");
        return person;
    }

去标明@Condition的实现以及依据是哪个类 当返回true的时候 才会扫描这个Bean 然后让spring把这个Bean放在容器中
除此之外Conditional还可以标在类上 如果满足 就扫描类中的语句 如果不满足 类中有什么都不管了

  • 自动装配 @Autowired
@Autowired // 自动装配 原理:spring 调用容器的getBean
    UserService userService;

    @Autowired
    Person bill;

    @Autowired
    List<Person> persons;

    @Autowired
    Map<String, Person> personMap;

自动装配流程 先按照类型 再按照名称
按照类型如果只找到一个 那么直接注入 如果找到多个 再按照名称 变量名就是名字
但是如果此时变量名也有多个 那么就要用到@Primary@qualifier
@Primary 标注主要的的组件 之后遇到多个变量名会直接按照标有@Primary注解的注入
@qualifier 例如当遇到Person person的时候 spring扫描遇到有多个Person 也没有person变量名 因此在自动注入的时候加上@Qualifier("bill")注解 机会让spring去找名字叫bill的Bean对象 然后来注入

  • 注意:

@Autowired @Resource两者的区别
都可以做自动注入
但是resource是Java 的标准规范库 autowired是spring的库
当切换框架时 autowired需要更改 但是resource就不需要更改
resource 有更强的通用性
同样的 也可以使用构造器来注入 但是这种方法使用的并不是很多

public UserDao(Dog dog){
    System.out.println("UserDao构造器...." + dog);
    this.dog = dog;
}

如代码所示 UserDao构造器会自动注入dog对象

  • Aware 感知接口
    可以将各种属性的值 通过spring调用 自动得到 只需要实现相关的接口即可
@Getter
@Service
public class HahaService implements EnvironmentAware, BeanNameAware {

    private  Environment environment;
    private String beanName;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public String getOs(){
        return environment.getProperty("OS");
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

如代码所示 该Service实现了 EnvironmentAware和BeanNameAware 感知接口 实现了方法 setEnvironment 和setBeanName 就是将所感知到的Name和Envir赋给了service中的属性值 让service可以得到这些值从而返回 注意 该方法需要get方法来得到BeanName

  • Value的强大功能

@Value("字面值") 直接赋值
@Value("${}") 从配置文件中取出某一项的值
@Value("#{SPEL}") Spring Expression Language Spring表达式
直接赋值

 @Value("旺财")
    private String name;
@Value("#{10+20}")
    private String color;

下面都是SPEL表达式赋值
调用类的静态方法进行赋值

 @Value("#{T(java.util.UUID).randomUUID().toString()}")
    private String id;

调用类的非静态方法赋值

    @Value("#{'Hello World'.substring(0,5)}")
    private String msg;

三元运算符赋值

    @Value("#{1 < 2? 'haha':'hehe'}")
    private String msg2;

还可以直接定义进行赋值

    @Value("#{new int[] {1,2,3}}")
    private int[]  array;

从配置文件中赋值就需要告诉spring容器是在哪个配置文件中扫描 因此就需要用到@PropertySource
@PropertySource("classpath:cat.properties")

@Data
@Component
public class Cat {

    @Value("${cat.name:Tom1}") //:是取不到的默认值
    private String name;

    @Value("${cat.age:21}")
    private int age;
}

Cat类的name和age就是从配置文件中取值 在cat.properties配置文件中有

cat.name = Tom
cat.age = 2

因此就可以拿到值
需要注意的是:classpath:cat.properties说明属性来源 把指定的文件导入到容器中 以此来使用从自己的类路径下找
classpath*:cat.properties 从所有包的类路径下找

classpath还可以直接获取文件

try {
            File file = ResourceUtils.getFile("classpath:1.jpg");
            System.out.println("file = " + file);

            int available = new FileInputStream(file).available();
            System.out.println("available = " + available);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

这个就通过classpath获取了配置文件中jpg照片的大小

  • 环境标识 @profile
    @Profile("环境标识") 当这个环境标识被激活的时候 才会加入组件
    场景:
    分为三个环境 dev prod test 当不同的环境运行时 会链接不同的数据库
@Configuration
public class DataSourceConfig {

    @Profile("dev")//
    @Bean
    public MyDataSource dev(){
        MyDataSource myDataSource = new MyDataSource();
        myDataSource.setUrl("dev");
        myDataSource.setUsername("dev");
        myDataSource.setPassword("dev");
        return myDataSource;
    }

    @Profile({"test","default"})
    @Bean
    public MyDataSource test(){
        MyDataSource myDataSource = new MyDataSource();
        myDataSource.setUrl("test");
        myDataSource.setUsername("test");
        myDataSource.setPassword("test");
        return myDataSource;
    }

    @Profile("prod")
    @Bean
    public MyDataSource prod(){
        MyDataSource myDataSource = new MyDataSource();
        myDataSource.setUrl("prod");
        myDataSource.setUsername("prod");
        myDataSource.setPassword("prod");
        return myDataSource;
    }


在该代码中 分别定义了三种不同的DataResource 并且用@profile进行三种不同的环境
定义环境标识dev test prod 激活环境标识 明确告诉spring当前环境 如果不说 就是默认环境 默认是default
现在只需要在主代码中定义具体环境即可

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("prod"); // ✅ 先设置 Profile
        context.register(SpringIocApplication.class);      // ✅ 再注册配置类
        context.refresh();                                // ✅ 最后刷新容器
        DeliveryDao dao = context.getBean(DeliveryDao.class);
        dao.saveDelivery();

此处需要先new一个容器 然后先设置好环境之后再注册配置类 这样容器中扫描组件的时候才会正确扫描

  • 生命周期

  • @Autowired如何实现的?

posted @ 2025-04-27 22:34  big4mart  阅读(1)  评论(0)    收藏  举报