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();
}
}
@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)
@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的关系以及区别
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的⼀套快速开发整合包
)web相关功能是Spring MVC提供的,项目整体框架是通过SpringBoot搭建的