IoC container
Container is a software environment that provides necessary support for the operation of a specific component. IoC refers to Inversion of Control
Components assembled in IoC container need some kind of "injection" mechanism
For example, a component needs a datasource. Instead of creating a datasource, it waits for the external to inject the datasource
In this way, different components can share resources, and another component can also inject datasource
Dependency injection method 1 set function
public class BookService { private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } }
Method 2 construction method
public class BookService { private DataSource dataSource; public BookService(DataSource dataSource) { this.dataSource = dataSource; } }
Method 3 assembles bean s in a configuration file
<bean id="userService" class="com.itranswarp.learnjava.service.UserService"> <property name="mailService" ref="mailService" /> </bean>
After writing the configuration file, you can load the configuration file and automatically generate bean s
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Fetch bean
// Get Bean: UserService userService = context.getBean(UserService.class); // Normal call: User user = userService.login("bob@example.com", "password");
In addition to ApplicationContext, there is also a container called BeanFactory
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml")); MailService mailService = factory.getBean(MailService.class);
Annotation configuration
The method of using configuration files is very cumbersome, and we can use annotations to simplify the process
@Component public class MailService { ... }
Use this annotation to define a Bean with the default name of mailservice
Then add the annotation @ Autowired to another bean
@Component public class UserService { @Autowired MailService mailService; ... }
Using @ Autowired simplifies injection
@Configuration is a configuration class
@ComponentScan tells the container to automatically search the package and sub package of the current class, and automatically create the Bean labeled @ component to assemble according to @ Autowired
Using Annotation with automatic scanning can greatly simplify Spring configuration. We only need to ensure that:
- Each Bean is marked as @ Component and @ Autowired injection is used correctly;
- The Configuration class is labeled @ Configuration and @ ComponentScan;
- All beans are in the specified package and sub package.
Inject List
How to inject classes with the same interface but different implementations into the list
First define an interface
public interface Validator { void validate(String email, String password, String name); }
Then the interface is implemented differently
@Component public class EmailValidator implements Validator { public void validate(String email, String password, String name) { if (!email.matches("^[a-z0-9]+\\@[a-z0-9]+\\.[a-z]{2,10}$")) { throw new IllegalArgumentException("invalid email: " + email); } } } @Component public class PasswordValidator implements Validator { public void validate(String email, String password, String name) { if (!password.matches("^.{6,20}$")) { throw new IllegalArgumentException("invalid password"); } } } @Component public class NameValidator implements Validator { public void validate(String email, String password, String name) { if (name == null || name.isBlank() || name.length() > 20) { throw new IllegalArgumentException("invalid name: " + name); } } }
We write this when we inject these bean s
@Component public class Validators { @Autowired List<Validator> validators; public void validate(String email, String password, String name) { for (var validator : this.validators) { validator.validate(email, password, name); } } }
All beans of type Validator will be injected as a List
The order of bean s in the list can be specified using the oder annotation
Use @ Autowired(required=false) to indicate that the injection is optional
Create third-party bean s
Write a java method in the @ Configuration class to create and return it using the @ Bean annotation
@Configuration @ComponentScan public class AppConfig { // Create a Bean: @Bean ZoneId createZoneId() { return ZoneId.of("Z"); } }
Initialization and destruction
@PostConstruct is used to initialize @ PreDestroy for destruction
@Component public class MailService { @Autowired(required = false) ZoneId zoneId = ZoneId.systemDefault(); @PostConstruct public void init() { System.out.println("Init mail service with zoneId = " + this.zoneId); } @PreDestroy public void shutdown() { System.out.println("Shutdown mail service"); } }
The execution process of such a Bean is
1. Construction method
2. Inject according to @ Autowired
3. Initialization
4. Destroy container
alias
Bean s of the same type can be distinguished by aliases when creating beans
@Configuration @ComponentScan public class AppConfig { @Bean("z") ZoneId createZoneOfZ() { return ZoneId.of("Z"); } @Bean @Qualifier("utc8") ZoneId createZoneOfUTC8() { return ZoneId.of("UTC+08:00"); } }
During injection
@Component public class MailService { @Autowired(required = false) @Qualifier("z") // Specifies the ZoneId with the injection name "z" ZoneId zoneId = ZoneId.systemDefault(); ... }
You can specify a bean as @ Primary
@Configuration @ComponentScan public class AppConfig { @Bean @Primary // Specify as primary Bean @Qualifier("z") ZoneId createZoneOfZ() { return ZoneId.of("Z"); } @Bean @Qualifier("utc8") ZoneId createZoneOfUTC8() { return ZoneId.of("UTC+08:00"); } }
When no bean name is specified, the primary bean will be injected
FactoryBean
spring provides a factory pattern using the FactoryBean interface
@Component public class ZoneIdFactoryBean implements FactoryBean<ZoneId> { String zone = "Z"; @Override public ZoneId getObject() throws Exception { return ZoneId.of(zone); } @Override public Class<?> getObjectType() { return ZoneId.class; } }
Injection configuration
Use @ PropertySource to automatically read the configuration file and then use @ Value injection
@Configuration @ComponentScan @PropertySource("app.properties") // Represents app.properties for reading classpath public class AppConfig { @Value("${app.zone:Z}") String zoneId; @Bean ZoneId createZoneId() { return ZoneId.of(zoneId); } }
- "${app. Zone}" means reading the value of the key as app.zone. If the key does not exist, an error will be reported during startup;
- "${app.zone:Z}" means to read the value whose key is app.zone, but if the key does not exist, the default value Z will be used.
Conditional assembly
According to the annotation @ Profile, you can decide whether to create bean s in the current environment
Using the annotation conditional, you can require that a bean be created only when a certain condition is met