什么是IoC
其实IoC我们在前⾯已经使⽤了, 我在前⾯讲到, 在类上⾯添加 @RestController 和 @Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交 给Spring管理, 就是IoC思想
IoC:Inversion of Control (控制反转),也就是说Spring是一个"控制反转"的容器
控制反转
什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了 也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创 建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了. 这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器
IoC的优点
-
资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了
-
我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度
IoC的使用
要把某个对象交给IOC容器管理,需要在类上添加注解例如@Component ⽽Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解
共有两类注解可以实现:
1.类注解:@Controller、@Service、@Repository、@Component、@Configuration
2.方法注解:@Bean
Controller(控制器存储)
java
@Controller // 将对象存储到 Spring 中
public class UserController {
public void sayHi(){
System.out.println("hi,UserController...");
}
}
如何观察这个对象已经存在Spring容器当中了呢? 接下来我们学习如何从Spring容器中获取对象
java
@SpringBootApplication
public class BeanApplication {
public static void main(String[] args) {
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(BeanApplication.class, args);
//从Spring上下文获取对象 //根据bean类型获取bean
UserControll userControll = context.getBean(UserControll.class);
//使用对象
userControll.sayHi();
}
}
运行结果
如果把@Controller删掉, 再观察运⾏结果
报错信息显⽰: 找不到类型是: com.example.demo.controller.UserController的bean
获取bean对象的其他方式
上述代码是根据类型来查找对象,如果Spring容器中,同一个类型存在多个bean的话,怎么开获取捏?
java
@SpringBootApplication
public class BeanApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(BeanApplication.class, args);
UserControll userControll1 = context.getBean(UserControll.class); //根据bean类型获取bean
userControll1.sayHi();
UserControll userControll2 = (UserControll) context.getBean("userControll"); //根据bean名称获取bean
userControll2.sayHi();
UserControll userControll3 = context.getBean("userControll",UserControll.class);//根据bean名称和类型来获取bean
userControll3.sayHi();
}
}
注意:类采用大驼峰命名,采用类名获取bean时,要用小驼峰
运行结果
其余的注解@Service、@Repository、@Component、@Configuration,使用的方法不变,将上述@Controller依次换成@Service、@Repository、@Component、@Configuration这些注解,程序也能照常运行
类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
其实这些注解里面都有一个注解@Component,说明它们本⾝就是属于 @Component 的"⼦类",
@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service , @Repository 等. 这些注解被称为 @Component 的衍⽣注解. @Controller , @Service 和 @Repository ⽤于更具体的⽤例(分别在控制层, 业务逻辑层, 持 久化层), 在开发过程中, 如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更 好的选择
方法注解@Bean
类注解是添加到某个类上的, 但是存在两个问题:
-
使⽤外部包⾥的类, 没办法添加类注解
-
⼀个类, 需要多个对象, ⽐如多个数据源 这种场景, 我们就需要使⽤⽅法注解 @Bean
举例 我们创建一个类UserInfo,包含id和name,让后在UserControll类中进行初试化,在主函数中进行调用
java
@Controller
public class UserControll {
@Bean
public UserInfo userInfo1(){
UserInfo userInfo = new UserInfo(1,"zhangsan");
return userInfo;
}
public void sayHi(){
System.out.println("Hi Controller");
}
}
java
public class BeanApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(BeanApplication.class, args);
UserControll userControll1 = context.getBean(UserControll.class); //根据bean类型获取bean
userControll1.sayHi();
UserInfo userInfo = context.getBean(UserInfo.class);
System.out.println(userInfo);
}
}
在不加@Bean时
报错显示找不到UserInfo
加上@Bean后
当一个类定义多个对象的时候,如下
java
@Controller
public class UserControll {
@Bean
public UserInfo userInfo1(){
UserInfo userInfo = new UserInfo(1,"zhangsan");
return userInfo;
}
@Bean
public UserInfo userInfo2(){
UserInfo userInfo = new UserInfo(2,"lisi");
return userInfo;
}
public void sayHi(){
System.out.println("Hi Controller");
}
}
运行结果如下
报错信息显⽰: 期望只有⼀个匹配, 结果发现了两个, userInfo1, userInfor2
这时候我们可以通过给Bean重命名,让后通过上述说的,通过名称来获取bean,如下
java
@Controller
public class UserControll {
@Bean("u1")
public UserInfo userInfo1(){
UserInfo userInfo = new UserInfo(1,"zhangsan");
return userInfo;
}
@Bean("u2")
public UserInfo userInfo2(){
UserInfo userInfo = new UserInfo(2,"lisi");
return userInfo;
}
public void sayHi(){
System.out.println("Hi Controller");
}
}
java
@SpringBootApplication
public class BeanApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(BeanApplication.class, args);
UserControll userControll1 = context.getBean(UserControll.class); //根据bean类型获取bean
userControll1.sayHi();
UserInfo userInfo1 = (UserInfo) context.getBean("u1");
UserInfo userInfo2 = (UserInfo) context.getBean("u2");
System.out.println(userInfo1);
System.out.println(userInfo2);
}
}
运行结果
DI介绍
DI: Dependency Injection(依赖注⼊)容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。程序运行时需要某个资源,此时容器就为其提供这个资源
DI的使用
依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象. 在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作. 简单来说, 就是把对象取出来放到某个类的属性中.
关于依赖注⼊, Spring也给我们提供了三种⽅式:
-
属性注⼊(Field Injection)
-
构造⽅法注⼊(Constructor Injection)
-
Setter 注⼊(Setter Injection)
我们在Controller类中实现Configuration类中的方法,代码如下
我们可以看到,在加了@Autowired的注解的时候,类Configuration中的方法时可以被正常调用的,运行结果也时正常的,如果没有加@Autowired,,运行结果如下
构造方法注入和Setter注入方法如下
java
@Controller
public class UserControll {
//@Autowired
private Configuration configuration;
@Autowired //构造方法注入
public UserControll(Configuration configuration){
this.configuration=configuration;
}
public void sayHi(){
System.out.println("Hi Controller");
configuration.sayHi();
}
}
Setter注入
java
@Controller
public class UserControll {
//@Autowired
private Configuration configuration;
@Autowired //Setter注入
public void setConfiguration(Configuration configuration){
this.configuration=configuration;
}
public void sayHi(){
System.out.println("Hi Controller");
configuration.sayHi();
}
}
三种注⼊优缺点分析
属性注⼊ :
优点: 简洁,使⽤⽅便;
缺点: 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指 针异常)不能注⼊⼀个Final修饰的属性
构造函数注⼊(Spring 4.X推荐)
优点: 可以注⼊final修饰的属性 ,注⼊的对象不会被修改 , 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法 是在类加载阶段就会执⾏的⽅法. ,通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
缺点:注入多个对象时,代码会比较繁琐
Setter注⼊(Spring 3.X推荐)
优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
缺点: 不能注⼊⼀个Final修饰的属性 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.
@Autowired存在问题
当同意类型存在多个bean时,使用Autowired会存在问题
java
@Component
public class BeanConfig {
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
java
@Controller
public class UserController {
@Autowired
private UserService userService;
//注⼊user
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
userService.sayHi();
System.out.println(user);
}
}
运行结果入下
报错的原因是,⾮唯⼀的 Bean 对象。
如何解决上述问题呢?Spring提供了以下⼏种解决⽅案: @Primary @Qualifier @Resource
使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现.
java
@Component
public class BeanConfig {
@Primary //指定该bean为默认bean的实现
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
使⽤@Qualifier注解:指定当前要注⼊的bean对象。 在@Qualifier的value属性中,指定注⼊的bean 的名称。
@Qualifier注解不能单独使⽤,必须配合@Autowired使⽤
java
@Controller
public class UserController {
@Qualifier("user2") //指定bean名称
@Autowired
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。
java
@Controller
public class UserController {
@Resource(name = "user2")
private User user;
public void sayHi(){
System.out.println("hi,UserController...");
System.out.println(user);
}
}
创作不易,点个赞把