Spring IoC&DI入门到掌握

DI 是IoC的⼀种实现,Ioc是思想,DI是实现

1、Spring 是包含了众多⼯具⽅法的 IoC 容器,在类上⾯添加 @RestController 和 @Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想

2、IoC: Inversion of Control (控制反转,控制反转就是将控制权给别人), 也就是说 Spring 是⼀个"控制反转"的容器,当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创 建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了. 这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器

3、举例了解Ioc

(1)代码可以用但是可维护性却很低.

public class NewCarExample {
 public static void main(String[] args) {
 Car car = new Car();
 car.run();
 }
 /**
 * 汽⻋对象
 */
 static class Car {
 private Framework framework;
public Car() {
 framework = new Framework();
 System.out.println("Car init....");
 }
 public void run(){
 System.out.println("Car run...");
 }
 }
/**
 * ⻋⾝类
 */
 static class Framework {
 private Bottom bottom;
 public Framework() {
 bottom = new Bottom();
 System.out.println("Framework init...");
 }
 }
 /**
 * 底盘类
 */
 static class Bottom {
 private Tire tire;
 public Bottom() {
 this.tire = new Tire();
 System.out.println("Bottom init...");
 }
 }
 /**
 * 轮胎类
 */
 static class Tire {
 // 尺⼨
 private int size;
 public Tire(){
 this.size = 17;
 System.out.println("轮胎尺⼨:" + size);
 }
 }
}

(2)接下来需求有了变更: 随着对的⻋的需求量越来越⼤, 个性化需求也会越来越多,我们需要加⼯多种尺⼨的轮胎,我们就需要为我们的代码加型号参数size,每个代码都需要加一次size,当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.

public class NewCarExample {
 public static void main(String[] args) {
 Car car = new Car(20);
 car.run();
 }
 /**
 * 汽⻋对象
 */
 static class Car {
 private Framework framework;
 public Car(int size) {
 framework = new Framework(size);
 System.out.println("Car init....");
 }
 public void run(){
 System.out.println("Car run...");
 }
 }
 /**
 * ⻋⾝类
 */
 static class Framework {
 private Bottom bottom;
 public Framework(int size) {
 bottom = new Bottom(size);
 System.out.println("Framework init...");
 }
 }
 /**
 * 底盘类
 */
 static class Bottom {
 private Tire tire;
 public Bottom(int size) {
 this.tire = new Tire(size);
 System.out.println("Bottom init...");
 }
 }
 /**
 * 轮胎类
 */
 static class Tire {
 // 尺⼨
 private int size;
 public Tire(int size){
 this.size = size;
 System.out.println("轮胎尺⼨:" + size);
 }
 }
}

(3)利用Ioc思想,我们可以将零件全外包出去,交给别人管,就需要自己管理车身就可以,如果型号尺寸发生变化我们需要什么就拿什么样的尺寸,创建⼦类的⽅式,改为注⼊传递的⽅式

public class IocCarExample {
 public static void main(String[] args) {
 Tire tire = new Tire(20);
 Bottom bottom = new Bottom(tire);
 Framework framework = new Framework(bottom);
 Car car = new Car(framework);
 car.run();
 }
 static class Car {
 private Framework framework;
 public Car(Framework framework) {
 this.framework = framework;
 System.out.println("Car init....");
 }
 public void run() {
 System.out.println("Car run...");
 }
 }
 static class Framework {
 private Bottom bottom;
 public Framework(Bottom bottom) {
 this.bottom = bottom;
 System.out.println("Framework init...");
 }
 }
 static class Bottom {
 private Tire tire;
 public Bottom(Tire tire) {
 this.tire = tire;
 System.out.println("Bottom init...");
 }
 }
 static class Tire {
 private int size;
 public Tire(int size) {
 this.size = size;
 System.out.println("轮胎尺⼨:" + size);
 }
 }
}
(3.1)这部分代码, 就是IoC容器做的⼯作

(4)资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了 ,我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度,也就是耦合度

4、DI: Dependency Injection(依赖注⼊) :容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊,依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,两者搭配使用,实现对象之间的解耦

5、注入依赖对象,将BookServive注入到BookController中,利用@Autowired注解实现

public class BookController {
 @Autowired
 private BookService bookService;
 @RequestMapping("/getList")
 public List<BookInfo> getList(){
 //获取数据
 List<BookInfo> books = bookService.getBookList();
 return books;
 }
}

6、五大类注解:@Controller、@Service、@Repository、@Component、@Configuration(不是固定用哪个他们的替代性高可以相互替代使用)方法注解:@Bean(Spring bean是Spring框架在运⾏时管理的对象, Spring会给管理的对象起⼀个名字

(1)使⽤ 五大注解存储bean 的代码如下所⽰

(1.1)@Service(服务存储),存储交给将UserService交给spring管理

@Service
public class UserService {
 public void sayHi(String name) {
 System.out.println("Hi," + name);
 }
}

 (1.1)获取交给Spring管理的Bean

@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //从Spring中获取UserService对象
 UserService userService = context.getBean(UserService.class);
 //使⽤对象
 userService.sayHi();
 }

(1.2) @Controlle(控制器存储)

@Controller // 将对象存储到 Spring 中
public class UserController {
 public void sayHi(){
 System.out.println("hi,UserController...");
 }
}
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //从Spring上下⽂中获取对象
 UserController userController = context.getBean(UserController.class);
 //使⽤对象
 userController.sayHi();
 }
}

(1.3)@Repository(仓库存储)

@Repository
public class UserRepository {
 public void sayHi() {
 System.out.println("Hi, UserRepository~");
 }
}
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //从Spring上下⽂中获取对象
 UserRepository userRepository = context.getBean(UserRepository.class);
 //使⽤对象
 userRepository.sayHi();
 }
}

 (1.4)@Component(组件存储)

@Component
public class UserComponent {
 public void sayHi() {
 System.out.println("Hi, UserComponent~");
 }
}
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //从Spring上下⽂中获取对象
 UserComponent userComponent = context.getBean(UserComponent.class);
 //使⽤对象
 userComponent.sayHi();
 }
}

 (1.5)@Configuration(配置存储)

@Configuration
public class UserConfiguration {
 public void sayHi() {
 System.out.println("Hi,UserConfiguration~");
 }
}
@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //从Spring上下⽂中获取对象
 UserConfiguration userConfiguration = context.getBean(UserConfiguration.class)
 //使⽤对象
 userConfiguration.sayHi();
 }
}
其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".
@Component 是⼀个元注解,也就是说可以注解其他类注解@Controller,@Service@Repository 等. 这些注解被称为 @Component 的衍⽣注解,运用其他注解是为了让其他程序员更好的区分当前类的⽤途
(2)⽅法注解 @Bean(要搭配类注解使用才能获取到)
(2.1)类注解是添加到某个类上的, 但是存在两个问题:
(2.1.1) 使⽤外部包⾥的类, 没办法添加类注解
(2.1.2) ⼀个类, 需要多个对象, ⽐如多个数据源这种场景, 我们就需要使⽤⽅法注解 @Bean
@Component
public class BeanConfig {
 @Bean
 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;
 }
}

(2.2)获取Bean

@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication)
 //根据bean名称, 从Spring上下⽂中获取对象
 User user1 = (User) context.getBean("user1");
 User user2 = (User) context.getBean("user2");
 System.out.println(user1);
 System.out.println(user2);
 }
}

(2.3)重命名Bean(name可以省略,只有一个名称时{}也可以省略)

@Bean(name = {"u1","user1"})
public User user1(){
 User user = new User();
 user.setName("zhangsan");
 user.setAge(18);
 return user;
}

(2.4)有时候我们获取不到我们的Spring管理的对象,因为我们在获取时候会对我们的文件包来进行扫描,如果扫描不到就获取不到,我们可以用@ComponentScan({"com.example.demo"})注解来指定扫描的路径,可以加多个路径,我们上面没用这个注解是因为我们的这个注解已经包含在启动类@SpringBootApplication 中了,我们尽量要把启动类放在我们希望扫描的包的路径下, 这样我们定义的bean就都可以被扫描到

7、ApplicationContext VS BeanFactory(获取bean对象, 是⽗类BeanFactory提供的功能)

(1)继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和
ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽
ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外,
它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
(2)从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽
BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)
8、依赖注⼊, Spring也给我们提供了三种⽅式:
(1)属性注⼊(Field Injection)
(2)构造⽅法注⼊(Constructor Injection)
(3)Setter 注⼊(Setter Injection)
 
(1)属性注入(将UserService类注入到UserController类中)利用 @Autowired注解
import org.springframework.stereotype.Service;
@Service
public class UserService {
 public void sayHi() {
 System.out.println("Hi,UserService");
 }
}
@Controller
public class UserController {
 //注⼊⽅法1: 属性注⼊
 @Autowired
 private UserService userService;
 public void sayHi(){
 System.out.println("hi,UserController...");
 userService.sayHi();}
}

(1.1)获取Controller中的sayHi方法

@SpringBootApplication
public class SpringIocDemoApplication {
 public static void main(String[] args) {
 //获取Spring上下⽂对象
 ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
 //从Spring上下⽂中获取对象
 UserController userController = (UserController) context.getBean("userController")
 userController.sayHi();
 }
}

(2)构造方法注入

@Controller
public class UserController2 {
 //注⼊⽅法2: 构造⽅法
 private UserService userService;
 @Autowired
 public UserController2(UserService userService) {
 this.userService = userService;
 }
 public void sayHi(){
 System.out.println("hi,UserController2...");
 userService.sayHi();
 }
}

(3)Setter 注⼊

@Controller
public class UserController3 {
 //注⼊⽅法3: Setter⽅法注⼊
 private UserService userService;
 @Autowired
 public void setUserService(UserService userService) {
 this.userService = userService;
 }
 public void sayHi(){
 System.out.println("hi,UserController3...");
 userService.sayHi();
 }
}

9、三种注⼊优缺点分析

(1)属性注⼊
优点: 简洁,使⽤⽅便;
缺点:
(1.1)只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
(1.2)不能注⼊⼀个Final修饰的属性
(2)构造函数注⼊(Spring 4.X推荐)
优点:
(2.1)可以注⼊final修饰的属性
(2.2)注⼊的对象不会被修改
(2.3)依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.
(2.4)通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
缺点:
注⼊多个对象时, 代码会⽐较繁琐
(3)Setter注⼊(Spring 3.X推荐)
优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
缺点:
(3.1)不能注⼊⼀个Final修饰的属性
(3.2)注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.
10、当有两个Bean对象的时候
(1)报错举例
@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;
 }
}

我们如果像这样调用就会出错,因为此时不知道注入的时user1还是user2

@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);
 }
}

(2)解决办法(@Primary指定默认的Bean

@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;
 }
}

(3)解决办法:(使⽤@Qualifier注解:指定当前要注⼊的bean对象。 在@Qualifier的value属性中,指定注⼊的bean 的名称,@Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

@Controller
public class UserController {
 @Qualifier("user2") //指定bean名称
 @Autowired
 private User user;
 public void sayHi(){
 System.out.println("hi,UserController...");
 System.out.println(user);
 }
}

(4)解决办法:(使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称

@Controller
public class UserController {
 @Resource(name = "user2")
 private User user;
 public void sayHi(){
 System.out.println("hi,UserController...");
 System.out.println(user);
 }
}

11、@Autowird 与 @Resource的区别

(1)@Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
(2) @Autowired 默认是按照类型注⼊,⽽@Resource是按照名称注⼊. 相⽐于 @Autowired 来说, @Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean
12、 Spring, Spring Boot 和Spring MVC的关系以及区别
(1) Spring: 简单来说, Spring 是⼀个开发应⽤框架,什么样的框架呢,有这么⼏个标签:轻量级、⼀ 站式、模块化,其⽬的是⽤于简化企业级应⽤程序开发
(2) Spring MVC: Spring MVC是Spring的⼀个⼦框架, Spring诞⽣之后, ⼤家觉得很好⽤, 于是按照MVC 模式设计了⼀个 MVC框架(⼀些⽤Spring 解耦的组件), 主要⽤于开发WEB应⽤和⽹络接⼝,所以, Spring MVC 是⼀个Web框架.
(3) Spring Boot: Spring Boot是对Spring的⼀个封装, 为了简化Spring应⽤的开发⽽出现的,中⼩型 企业,没有成本研究⾃⼰的框架, 使⽤Spring Boot 可以更加快速的搭建框架, 降级开发成本, 让开发 ⼈员更加专注于Spring应⽤的开发,⽽⽆需过多关注XML的配置和⼀些底层的实现.
(4) Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的⼀个
MVC 框架,⽽Spring Boot 是基于Spring的⼀套快速开发整合包
(5 )web相关功能是Spring MVC提供的,项目整体框架是通过SpringBoot搭建的
  • 54
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值