spring ioc
=== Inversion of Control (IoC) 控制反转、将对象的创建交给 Spring
2.01 IOC容器优点
-
控制对象的单例、多例
-
控制对象的生命周期、初始化、销毁
-
控制对象的代理机制、获取代理对象
-
管理对象间依赖关系
-
特殊概念: 放入 Spring 容器的对象 特称为 Bean
-
Spring 容器逻辑示意

2.02 环境搭建
=== Spring 框架核心坐标: AOP、CORE、BEANS、EXPRESSION、CONTEXT

- pom.xml
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.0</version></dependency>2.03 容器API
=== 容器API 主要包括: 声明配置、注解容器对象、容器上下文扫描、容器入口
- @Configuration 声明 spring 注解配置类
- @ComponentScan(basePackages = "") 扫描指定包及子包
- @Component(“user”) 将类放入 spring 容器
- ClassPathXmlApplicationContext 基于 xml 配置的上下文容器入口
- AnnotationConfigApplicationContext 基于 java 配置的上下文容器入口
- GenericApplicationContext 通用配置 上下文容器入口
- SpringContextConfiguration.java
package org.example.spring.conf;
import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;
@Configuration@ComponentScan(basePackages = "org.example.spring")public class SpringContextConfiguration {}- User.java
package org.example.spring.entity;
import org.springframework.stereotype.Component;
// @Component // user@Component("user")public class User {
private String username;
@Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; }}- Application.java
package org.example.spring.api;
import org.example.spring.SpringContextConfiguration;import org.example.spring.entity.User;import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringContextConfiguration.class);
// User user = context.getBean(User.class); // System.out.println("user = " + user);
System.out.println("context.getBean(\"user\") = " + context.getBean("user"));
context.close(); }}2.04 容器注解
=== @Component、@Service、@Repository
-
Spring 容器可使用 @Component、@Service、@Repository 注解类放入容器
-
@Component
-
@Service
-
@Repository 使用方式完全一致、仅语义区别
-
UserService.java
package org.example.spring.service;
import org.springframework.stereotype.Component;import org.springframework.stereotype.Repository;import org.springframework.stereotype.Service;
// @Component@Service// @Repositorypublic class UserService { @Override public String toString() { return "UserService{}"; }}- UserDao.java
package org.example.spring.dao;
import org.springframework.stereotype.Repository;
@Repositorypublic class UserDao { @Override public String toString() { return "UserDao{}"; }}2.05 @Bean
=== @Bean 用于复杂对象放入容器、或者第三方类对象放入容器
-
@Bean 默认会将方法名称作为放入容器对象的 ID 值
-
@Bean(name = “dataSource”) name 属性可指定 容器对象 ID 值
-
POM.XML
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.1.0</version></dependency>
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version></dependency>- DataSourceConfiguration.java
package org.example.spring.conf;
import com.zaxxer.hikari.HikariDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;
@Configurationpublic class DataSourceConfiguration {
@Bean(name = "dataSource") // @Bean public HikariDataSource hikariDataSource() { HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/legou?serverTimezone=Asia/Shanghai";
hikariDataSource.setJdbcUrl(jdbcUrl); hikariDataSource.setUsername("root"); hikariDataSource.setPassword("root");
return hikariDataSource; }
}2.06 @Scope
=== @Scope 用于配合 @Component、@Service、@Bean 指定容器对象生命周期
-
Spring 容器对象默认是单实例的、可通过 @Scope 进行控制
-
@Scope value 属性取值有以下
-
ConfigurableBeanFactory.SCOPE_SINGLETON 单例(默认)
-
ConfigurableBeanFactory.SCOPE_PROTOTYPE 多例
-
WebApplicationContext.SCOPE_REQUEST 生命周期属于一次请求中
-
WebApplicationContext.SCOPE_SESSION 生命周期属于一次会话中
-
官网说明

=== 代码示例
- Person.java
package org.example.spring.entity;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;
// @Component// @Scope("prototype")// @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public class Person {}- SpringContextConfiguration.java
package org.example.spring.conf;
import org.example.spring.entity.Person;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Scope;
@Configuration@ComponentScan(basePackages = "org.example.spring")public class SpringContextConfiguration {
@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Person person() { return new Person(); }
}- Application.java
package org.example.spring.api;
import org.example.spring.conf.SpringContextConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.Scope;
public class Application { public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringContextConfiguration.class);
Person person1 = context.getBean(Person.class); Person person2 = context.getBean(Person.class);
System.out.println(person1 == person2);
context.close(); }}2.07 @Bean 生命周期控制
=== org.springframework.context.annotation.Bean
- Bean.java
package org.springframework.context.annotation;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.core.annotation.AliasFor;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Bean {
@AliasFor("name") String[] value() default {};
@AliasFor("value") String[] name() default {};
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}- Student.java
package org.example.spring.entity;
public class Student {
public void init() { System.out.println("init..."); }
public void destroy() { System.out.println("destroy..."); }
}- SpringContextConfiguration.java
package org.example.spring.conf;
import org.example.spring.entity.Person;import org.example.spring.entity.Student;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Scope;
@Configuration@ComponentScan(basePackages = "org.example.spring")public class SpringContextConfiguration {
@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public Person person() { return new Person(); }
@Bean(initMethod = "init", destroyMethod = "destroy") public Student student() { return new Student(); }
}2.08 @Value
=== @Value 注解用于给容器对象注入属性值、可使用 ${} 表达式、或调用API
-
@Value(“直接赋值”)
-
@Value(”#{解析表达式}”)
-
@Value(”${获取上下文对象或变量}”)
-
@Value 常配合 @PropertySource 读取 properties 配置并注入值
-
Foo.java
package org.example.spring.entity;
import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;
import java.util.Arrays;import java.util.List;import java.util.Map;import java.util.Set;
@Componentpublic class Foo {
@Value("我爱你中国") private String string;
@Value("#{T(java.lang.Math).random()}") private Double number;
@Value("1, 2, 3") private String[] array;
@Value("11, 22, 33") private List<String> list;
@Value("111, 222, 333") private Set<String> set;
@Value("#{{k1: 'v1', k2: 'v2'}}") private Map<String, String> map;
@Override public String toString() { return "Foo{" + "string='" + string + '\'' + ", number=" + number + ", array=" + Arrays.toString(array) + ", list=" + list + ", set=" + set + ", map=" + map + '}'; }}2.09 @PropertySource
=== @PropertySource 用于读取 Java 标准 properties 配置文件
-
@PropertySource(“classpath:properties文件路径”)
-
@PropertySource 常 配合 @Value 读取配置文件并注入键值
-
src/main/resources/hikaricp.properties
hikaricp.driverClassName=com.mysql.cj.jdbc.Driverhikaricp.jdbcUrl=jdbc:mysql://127.0.0.1:3306/legou?serverTimezone=Asia/Shanghaihikaricp.username=roothikaricp.password=root- DataSourceConfiguration.java
package org.example.spring.conf;
import com.zaxxer.hikari.HikariDataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;
@Configuration@PropertySource(value = "classpath:hikaricp.properties", ignoreResourceNotFound = true)public class DataSourceConfiguration {
@Value("${hikaricp.driverClassName}") private String driverClassName;
@Value("${hikaricp.jdbcUrl}") private String jdbcUrl;
@Value("${hikaricp.username}") private String username;
@Value("${hikaricp.password}") private String password;
@Bean(name = "dataSource") public HikariDataSource hikariDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
System.out.println("driverClassName = " + driverClassName); System.out.println("jdbcUrl = " + jdbcUrl); System.out.println("username = " + username); System.out.println("password = " + password);
hikariDataSource.setDriverClassName(driverClassName); hikariDataSource.setJdbcUrl(jdbcUrl); hikariDataSource.setUsername(username); hikariDataSource.setPassword(password);
return hikariDataSource; }
}2.10 Condition
=== org.springframework.context.annotation.Condition
-
Condition 接口用于实现条件控制 Bean 是否放入 Spring 容器
-
matches 方法返回 true 则表示放入、否则不放入
-
matches 方法参数 ConditionContext 可获取当前 Spring 上下文
-
@Conditional 注解用于指定 Condition 接口实现类
-
@Conditional 注解是 SpringBoot 框架实现自动装配的核心原理
-
Condition.java
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterfacepublic interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}=== 此处示例通过 properties 配置控制对象是否放入容器
- src/main/resources/application.properties
global.custom.apple.create=true- Apple.java
package org.example.spring.entity;
public class Apple { @Override public String toString() { return "Apple{}"; }}- AppleCondition.java
package org.example.spring.condition;
import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.env.Environment;import org.springframework.core.type.AnnotatedTypeMetadata;
public class AppleCondition implements Condition {
@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 获取 spring 环境 Environment environment = context.getEnvironment(); String createKey = "global.custom.apple.create";
// 2. 获取 global.custom.apple.create 上下文 key Boolean property = environment.getProperty(createKey, boolean.class);
// 3. 返回 key 对应值 return Boolean.TRUE.equals(property); }}- SpringContextConfiguration.java
package org.example.spring.conf;
import org.example.spring.entity.Apple;import org.example.spring.entity.Person;import org.example.spring.entity.Student;import org.example.spring.ondition.AppleCondition;import org.springframework.beans.factory.config.ConfigurableBeanFactory;import org.springframework.context.annotation.*;
@Configuration@ComponentScan(basePackages = "org.example.spring")@PropertySource(value = "classpath:application.properties")public class SpringContextConfiguration {
@Bean @Conditional(AppleCondition.class) public Apple apple() { return new Apple(); }
}- Application.java
AnnotationConfigApplicationContext context...
Apple apple = context.getBean(Apple.class);System.out.println(apple);- Console
global.custom.apple.create=true # 可注入spring容器,可获得 Apple 对象global.custom.apple.create=false # 未注入spring容器
留言评论