overview
SpringIoc控制反转,将创建对象的权力交给Spring容器,那么可以类比jdk,jdk中所有创建对象的方式在Spring中也同样支持。
SpringIoC的两个基本功能
- 通过描述管理Bean,包括装配和获取Bean;
- 依赖注入
对象的装配
这里通过类比jdk创建对象的方式来理解SpringIoc装配对象的方式。
jdk创建对象方式:
1.无参构造 new UserDto();
2.有参构造 new UserDto(1,"xxx");
接下来按照以上两种方式来创建对象
无参构造
package com.pl.spring5.demo.config;
import com.pl.spring5.demo.dto.UserDto;
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;
/**
*
*
* @Description: TODO
*
* @ClassName AppConfig
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@Configuration
public class AppConfig {
@Bean("simpleUser")
public UserDto getSimpleUser(){
return new UserDto();
}
}
有参构造
package com.pl.spring5.demo.config;
import com.pl.spring5.demo.dto.UserDto;
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;
/**
*
*
* @Description: TODO
*
* @ClassName AppConfig
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@Configuration
public class AppConfig {
@Bean("simpleUser")
public UserDto getSimpleUser(){
return new UserDto();
}
@Value("${demo.constant.user.age}")
private int age;
@Value("${demo.constant.user.name}")
private String name;
@Bean("ComplexUser")
public UserDto getComplexUser(){
return new UserDto(age,name);
}
}
单元测试
package com.pl.spring5.demo.ioc;
import com.pl.spring5.demo.DemoApplication;
import com.pl.spring5.demo.config.AppConfig;
import com.pl.spring5.demo.dto.UserDto;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
*
* @Description: TODO
*
* @ClassName IocTest
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@SpringBootTest
public class IocTest {
@Autowired
@Qualifier("ComplexUser")
private UserDto complexUser;
@Autowired
@Qualifier("simpleUser")
private UserDto simpleUser;
@Test
public void test1(){
System.out.println(complexUser);
System.out.println(simpleUser);
}
}
SpringBoot中可以实现装配的注解
1.@ComponetScan和@Componet
使用介绍
-
@ComponetScan
这个注解需要和@Configuration结合使用
标明采用何种策略去扫描bean
策略比如:扫描哪些包,或者排除哪些包
-
@Componet
这个注解标明哪个类被扫描到SpringIoC中,生效的前提也是该类在ComponetScan扫描的范围内
notice:
n1:@Compoonet和@Configuration的区别
@Component与@Configuration都可以作为配置类,两者标注的类和其中的bean都是装配到SpringIoC中,两者主要有以下三点区别:
- 单例多例:@Component下使用的@Bean都是多例的,而@Configuration下的bean都是单例的。
- 使用上:@Componet需要配合@ComponetScan使用
- 使用习惯上:@Configuration更多强调这是个配置文件,配置类,里面会配置很多bean,而@Componet强调当前类会装配到SpringIoC中,不太会刻意的配置很多bean,只会注入当前类需要的属性。
n2:@SpringBootApplication
在该注解中包含了@Component注解
@Component默认会扫描当钱包和其子包中的类,这也是springboot项目中启动类相对其他类位置外方的原因。
2.其他常见的注解
比如 @Controller @Service @Repository也都是有装配作用的注解。
属性注入
JDK中对象会有很多变量,对应的Spring的Bean的创建也需要将对象中的变量注入到Bean中。
@Autowired
这个注解是基于类型注入的,byType。
只要被其修饰,该注解就会去SpringIoc中寻找与之类型相匹配的对象实例。
code
Animal
package com.pl.spring5.demo.domain.animal;
/**
* @Auther: pl
* @Date: 2020/9/20 18:56
* @Description:
*/
public interface Animal {
void use();
}
Person
package com.pl.spring5.demo.domain.animal;
/**
* @Auther: pl
* @Date: 2020/9/20 19:37
* @Description:
*/
public interface Person {
public void service();
}
Dog
package com.pl.spring5.demo.domain;
import com.pl.spring5.demo.domain.animal.Animal;
import org.springframework.stereotype.Component;
/**
*
*
* @Description: TODO
*
* @ClassName Dog
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@Component
public class Dog implements Animal {
@Override
public void use() {
System.out.println("你是人类的好朋友");
}
}
User
package com.pl.spring5.demo.domain;
import com.pl.spring5.demo.domain.animal.Animal;
import com.pl.spring5.demo.domain.animal.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
*
* @Description: TODO
*
* @ClassName User
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@Component
public class User implements Person {
@Autowired
private Animal animal = null;
@Override
public void service() {
this.animal.use();
}
}
Junit
package com.pl.spring5.demo.ioc;
import com.pl.spring5.demo.DemoApplication;
import com.pl.spring5.demo.config.AppConfig;
import com.pl.spring5.demo.domain.animal.Person;
import com.pl.spring5.demo.dto.UserDto;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
*
* @Description: TODO
*
* @ClassName IocTest
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@SpringBootTest
public class IocTest {
//@Autowired 按照类型查找,虽然修饰的是User,但是User实现了Person接口,都是一类,所以可以写Person,java中多态的体现。
@Autowired
private Person user;
@Test
public void test2(){
user.service();
}
}
notice:
@Autowired标注方法
他会使用setAnimal方法,在SpringIoC中找到对应的动物注入。
@Autowired标注方法的参数
总结:
@Autowired的作用就是按照类型注入,被他标注的属性,方法(方法中的引用类),方法参数都会被注入。
消除歧义的注入
@Primary和@Quelifiler
比如如果再有一个Cat类实现Animal接口,那么用@Autowired就注入不了了
因为Cat和Dog都属于Animal,属于一类,SpringIoC就不知道需要注入的是哪个类的实例。这时就需要消除歧义的注入了。
@Primary
标注的类,优先注入。
package com.pl.spring5.demo.domain;
import com.pl.spring5.demo.domain.animal.Animal;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/**
*
*
* @Description: TODO
*
* @ClassName Dog
* @Author pl
* @Date 2020/9/20
* @Version V1.0.0
*/
@Component
@Primary
public class Dog implements Animal {
@Override
public void use() {
System.out.println("你是人类的好朋友");
}
}
但是如果Cat也标注,那么还是不知到谁该注入
@Quelifiler
byName,按照名字注入
他和@Autowired一起使用,按照类型和名字匹配出一个符合条件的注入对象
属性普通类型变量注入
notice
bean的获取:
其实依赖注入中的@Autowired,@Primary,@Quelifiler标注的对象,就是bean的获取
也可以通过AnnotationConfigApplicationContext 获取bean,但是如果bean中有属性注入,@value标注的属性,这样获取就会报错,,@value修饰的属性值就会是${demo.constant.user.name}这样一个string类型的值,而不是配置文件中的值。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserDto simpleUser = (UserDto) ctx.getBean("simpleUser");
System.out.println(simpleUser);
如果装配的对象中有引用类型,会先去注入引用类型
会先注入UserDAO,即先装配UserDAO。