Java EE 进阶:5. Spring IoCDI(第三弹):Spring DI
目录
4. DI 详解
4.1 属性注入
4.2 构造方法注入
4.3 Setter 注入
4.4 三种注入优缺点分析
4.5 @Autowired 存在问题
方式一:@Primary
方式二:@Qualifier
第一种用法:加在属性上
第二种用法:加在参数上
方式三:@Resource
常见面试题:(频率比较高)
小结
5.练习
6.总结
6.1 Spring,Spring Boot 和 Spring MVC 的关系以及区别
6.2 bean 的命名
6.3 常见面试题
书接上文:Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC~~
4. DI 详解
上面我们讲解了控制反转 IoC 的细节,接下来呢,我们学习依赖注入DI的细节
我们把对象交给 Spring 进行管理了,既然我们能放,自然就能取~~
什么是依赖??👇
上述代码中 BookService 依赖 BookDao,BookDao 就是 BookService 的依赖,如果 BookService 想要正常工作,那么就需要有 BookDao 才能正常工作,BookDao 就是依赖~~
而 DI 表示依赖注入,依赖注入是一个过程,是从容器当中取出对象,注入到 BookDao 里,这就是依赖注入,IoC 讲的是 Bean 的存储,DI 讲的是 Bean 的获取~~
闲聊:IoC 和 DI 也是 Spring 中常见的面试题,因为除了这俩也没啥能问的~~
在上面程序案例中,我们使用了@Autowired 这个注解,完成了依赖注入的操作.
简单来说, 就是把对象取出来放到某个类的属性中.
在一些文章中, 依赖注入也被称之为 "对象注入", "属性装配", 具体含义需要结合文章的上下文来理解关于依赖注入, Spring也给我们提供了三种方式:
1. 属性注入(Field Injection)
2. 构造方法注入(Constructor Injection)
3. Setter 注入(Setter Injection)
接下来,我们分别来看。
下面我们按照实际开发中的模式,将 Service 类注入到 Controller 类中。
4.1 属性注入
在上述代码中👇
bookDao 就是 BookService 的属性,所以是属性注入~~
属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中.
Service 类的实现代码如下:@Service//此时也是交给Spring管理了 public class UserService { public void print(){ System.out.println("do Service"); } }Controller 类的实现代码如下:
@Controller public class HelloController { @Autowired//注入userService,此时userService就有值了 private UserService userService; public void print(){ System.out.println("do Controller"); userService.print(); } }此时 @Autowired 就会从 Spring 容器中找到 service 类型的对象,并且赋值给这个属性
获取 Controller 中的 print 方法:
@SpringBootApplication //加上这个注解之后,不管这个类名叫什么,它都是一个启动类 public class SpringIocDemoApplication { public static void main(String[] args) { //获取Spring上下文对象,直接使用父类接收 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); //DI测试 //从 Spring 上下文中获取对象 HelloController bean=context.getBean(HelloController.class); bean.print(); } }程序执行后就会调用 userService.print();👇
@Controller public class HelloController { @Autowired//注入userService,此时userService就有值了 private UserService userService; public void print(){ System.out.println("do Controller"); userService.print(); } }然后就会打印 “do Service”👇
@Service//此时也是交给Spring管理了 public class UserService { public void print(){ System.out.println("do Service"); } }最终结果如下:
发现确实注入进来了,userService 也是有值的~~
去掉 @Autowired , 再运行一下程序看看结果
也就是说我们把 @Autowired 去掉后,userService 就是一个普通声明了,这个普通说明默认是为 null 的~~
这也验证了 @Autowired:从 Spring 容器中,根据类型获取对象,并赋值~~
那么又有一个问题了,既然是从 Spring 容器中获取对象,那么容器中必须要有这个对象才可以,没有的话是注入不进来的,而我们刚才能获取到是因为加了 @Service 这个注解,如果没有这个注解呢??我们去掉 @Service,再来尝试一下👇
//@Service//此时也是交给Spring管理了 public class UserService { public void print(){ System.out.println("do Service"); } }发现都服务都启动不起来👇
出现这个报错信息就说明 Spring 容器中不存在这个对象
以上两个错误信息一个是属性未赋值,一个是 Spring 容器中不存在这个对象,这是两个不同的异常信息,需要注意分辨~~
注意:@Autowired 是根据类型取查找的,所以我们的名称是任意写的,比如可以写成 us👇而不要求一定与 UserService.java 中 Bean 的名称相同
@Controller public class HelloController { @Autowired//注入userService,此时userService就有值了 private UserService us; public void print(){ System.out.println("do Controller"); us.print(); } }那就来问题了,既然是根据类型找,那上一节我们学 IoC 的时候知道,@Bean 的话,一个类型是可以有多个对象的,那么有多个对象的时候 @Autowired 注入是不是就失败了呢??
这确实是个问题,我们在下面的 4.5 @Autowired 进行演示和讲解~~
4.2 构造方法注入
构造方法注入是在类的构造方法中实现注入,如下代码所示:
@Controller public class HelloController { //构造方法注入 private UserService us; public HelloController(UserService us){ this.us=us; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); } }此时我们发现也是可以运行成功的,证明 us 中也确实有值👇
同样的,我们把构造方法去掉后,就只剩下一个声明了,自然 us 值就为 null 了👇
另外,我们说,当我们构造了有参的构造函数之后,无参的构造函数默认就会没有了,所以我们一般要及时把无参构造函数给补上,因为有些情况可能会出错,比如说像 json 的一些序列化就可能有问题~~
那么现在问题又来了,当我们加上无参构造函数之后貌似又出现了新的问题👇
@Controller public class HelloController { //属性注入 //@Autowired//注入userService,此时userService就有值了 //private UserService us; //构造方法注入 private UserService us; public HelloController(){ } public HelloController(UserService us){ this.us=us; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); } }此时又变成这样了👇
这就涉及到了交给 Spring 管理之后,Spring 创建对象是怎么创建的??
底层原理是依靠 反射 来创建对象的~~
反射知识点:Java数据结构——11.反射 枚举 lambda《干货笔记》
反射 这一概念是我们数据结构阶段存在感非常低的一个知识点,调用的也是构造函数,只不过不是直接的,而此时我们有两个构造方法,一个有参的一个无参的,到底调用哪个呢??默认是用的无参的
那么我们现在把无参的给去掉,再新加一个属性,看两个构造函数,它调用的是哪个👇
@Controller public class HelloController { //构造方法注入 private UserService us; private UserConfig userConfig; // public HelloController(){ // // } public HelloController(UserConfig userConfig, UserService us) { this.userConfig = userConfig; this.us = us; } public HelloController(UserService us){ this.us=us; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }此时我们发现报错信息又发生了变化👇
闲聊:Java EE 阶段学会看错误日志非常重要~~
错误日志要从下往上看,因为堆栈信息是从下往上打的~~
如果能在最底层看懂哪里出错了,那上面那一大堆就不用看了,否则就依次往上看~~
上面错误日志中 instantiate 表示实例化,上述错误日志就是说:我们没找到默认的无参构造方法
那么就得出结论了:当只有一个构造方法时,就只能用这个了,如果出现多个构造方法,就去找默认的无参构造方法,没找到就报错~~
那么我们如何指定构造方法呢??还是使用 @Autowired👇
@Controller public class HelloController { //构造方法注入 private UserService us; private UserConfig userConfig; // public HelloController(){ // // } @Autowired public HelloController(UserConfig userConfig, UserService us) { this.userConfig = userConfig; this.us = us; } public HelloController(UserService us){ this.us=us; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }此时我们发现就成功了👇
此时我们是给带有两个参数的构造方法带上 @Autowired,如果是只给带一个参数的构造方法带的话,那么另一个就会因取值为 null 而报错👇
@Controller public class HelloController { //构造方法注入 private UserService us; private UserConfig userConfig; // public HelloController(){ // // } // @Autowired public HelloController(UserConfig userConfig, UserService us) { this.userConfig = userConfig; this.us = us; } @Autowired public HelloController(UserService us){ this.us=us; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }
总结:如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,默认构造方法是无参的构造方法,如果没有就会报错,也可以用 @Autowired 来明确指定到底使用哪个构造方法
4.3 Setter 注入
@Controller public class HelloController { //setter方法注入 private UserService us; private UserConfig userConfig; public void setUs(UserService us) { this.us = us; } public void setUserConfig(UserConfig userConfig) { this.userConfig = userConfig; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }启动服务后,发现 us 值为 null,还没到 userConfig 就报错了👇
Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set 方法的时候需要加上 @Autowired 注解 ,如下代码所示:
@Controller public class HelloController { //setter方法注入 private UserService us; private UserConfig userConfig; @Autowired public void setUs(UserService us) { this.us = us; } public void setUserConfig(UserConfig userConfig) { this.userConfig = userConfig; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }发现这时候 us 就有值了👇
只不过 userConfig 还没有值,所以报错,当我们给 userConfig 也加上 @Autowired 之后,就可以了👇
@Controller public class HelloController { //setter方法注入 private UserService us; private UserConfig userConfig; @Autowired public void setUs(UserService us) { this.us = us; } @Autowired public void setUserConfig(UserConfig userConfig) { this.userConfig = userConfig; } public void print(){ System.out.println("do Controller"); System.out.println(us); us.print(); userConfig.print(); } }此时 us 和 userConfig 就都注入进来了👇
4.4 三种注入优缺点分析
这部分也是个面试题,只不过频率很低,大家了解就行了,也不建议去背,理解就行了~~
1)属性注入
①优点: 简洁,使用方便;
②缺点:
▪ 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常),说白了:脱离 Spring 就用不了了~~
▪ 不能注入一个 Final 修饰的属性(语法就报错👇)这个就与我们 final 的语法有关了,对于 final 的变量,在这里就两种写法👇
第一种:new 一个对象初始化
那我都 new 了,还 @Autowired 干啥呀??那我不用得了呗,不从 Spring 拿了,我都自己 new 完了~~
所以不行~~
第二种:构造方法初始化
那我都用构造方法了,那不就是构造方法注入了吗??我直接在 构造方法 里赋值了不香吗??
所以不行~~
注意:@Autowired 只会对下一行代码生效,不会对下面所有属性生效
2)构造函数注入(Spring 4.X推荐的)我们此时的 Spring 版本也可以自行查看,是 7.0.10👇
①优点:
▪ 可以注入 final 修饰的属性(上面已经说过了)
▪ 注入的对象不会被修改(其实这点是针对 set 方法来说的👇)@Autowired public HelloController(UserConfig userConfig, UserService us) { this.userConfig = userConfig; this.us = us; }当我们加上 @Autowired 之后,表示我在创建对象的时候使用的构造方法就是这个了👆,在这个地方给它赋值了,之后就不会再被修改了~~
▪ 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.这个就涉及到我们 Java EE 初阶 JVM 类加载机制相关的知识了,最先执行的就是构造方法,所以刚创建对象的时候就有值了👇
Java EE:9.JVM
▪ 通用性好, 构造方法是 JDK 支持的, 所以更换任何框架,他都是适用的就是说不局限于 Spring 框架
②缺点:
▪ 注入多个对象时, 代码会比较繁琐3)Setter注入(Spring 3.X推荐的)
为啥不同版本推荐不同的注入?咱也不知道,可能换领导了吧,使用习惯不一样~~
①优点: 方便在类实例之后, 重新对该对象进行配置或者注入也就是说既然构造方法是个方法,那我们就可以对其重复使用的~~
②缺点:
▪ 不能注入一个 Final 修饰的属性
▪ 注入对象可能会被改变, 因为setter方法可能会被多次调用, 就有被修改的风险.极限的优点就是缺点~~
更多DI相关内容参考: Dependencies :: Spring Framework
4.5 @Autowired 存在问题
闲聊:Setter 注入是 Spring 3.X 推荐的,构造方法注入是 Spring 4.X 推荐的,而咱们程序员呢,就推荐 属性注入,因为简单~~
当同一类型存在多个 bean 时, 使用@Autowired会存在问题
我们先用一个对象试试👇
UserService.java👇
@Service//此时也是交给Spring管理了 public class UserService { @Autowired private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }StudentComponrnt.java👇
@Component public class StudentComponent { @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } // @Bean // public Student s2(String name){ // return new Student(name,12); // } }启动类👇
@SpringBootApplication public class SpringIocDemoApplication { public static void main(String[] args) { //获取Spring上下文对象,直接使用父类接收 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); //从Spring容器中拿到UserService,并调用它的方法 UserService bean=context.getBean(UserService.class); bean.print(); } }运行结果:没问题👇
注意,此时 s3 和 s4 是两个名而已,就好像 CoderYanger 的大小名,指的其实是一个人~~
那如果我们再加一个对象 s2 呢?👇
@Component public class StudentComponent { @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } @Bean public Student s2(String name){ return new Student(name,12); } }运行结果👇
@Autowried 和 构造方法一样,当只有一个的时候没得选,只能用这一个,当出现多个的时候就懵圈了~~
Action 里的内容就跟咱上网搜资料一样,有时候可用,有时候不可用,但咱这次的 Action 是可用的,其中 @Primary 在学习数据库的时候见到过:主键,整体就是在说,让你标识一个为默认的,或者使用 @Qualifier 去指定 Bean,这两个注解就是我们下面要说的解决方案~~
如何解决上述问题呢?Spring提供了以下几种解决方案:
• @Primary
• @Qualifier
• @Resource方式一:@Primary
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现
@Component public class StudentComponent { @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } @Primary @Bean public Student s2(String name){ return new Student(name,12); } }发现默认就使用第二个对象了👇
加在第一个对象上面👇
@Component public class StudentComponent { @Primary @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } // @Primary @Bean public Student s2(String name){ return new Student(name,12); } }此时默认就使用第一个对象了👇
方式二:@Qualifier
第一种用法:加在属性上
使用 @Qualifier 注解:指定当前要注入的 bean 对象。 在@Qualifier的 value 属性中,指定注入的 bean 的名称
也就是在这个位置👇
我们在 @Autowired 中发现,它只有一个属性,默认为 true,也就是说 @Autowired 必须要执行的,我们也没有地方去给 @Autowired 设置名称👇
Spring 有个解决问题的特点:一个注解搞不定,就两个注解搞定,两个注解还搞不定,就三个注解搞定……所以此处用 @Qualifier 去指定它的名称。因为它的源码里有 value👇
• @Qualifier注解不能单独使用,必须配合@Autowired使用
@Service//此时也是交给Spring管理了 public class UserService { @Qualifier("s3")//指定bean名称为s3 @Autowired private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }此时启动服务后拿到的就是 s3 的对象👇
当我们改成 s2 时👇
@Service//此时也是交给Spring管理了 public class UserService { @Qualifier("s2")//指定bean名称为s2 @Autowired private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }此时拿到的对象就是 s2👇
第二种用法:加在参数上
看完 方式三:@Resource 再来看这一条~~
@Qualifier 源码中写了可以加在参数上👇
此时代码👇
@Service//此时也是交给Spring管理了 public class UserService { @Resource(name="s2") private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }@Resource 用到了 s2,s2 在这里用到了 String n,但是 n 上面有 name 和 name2 两个,应该用哪个??👇
@Component public class StudentComponent { @Primary @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } @Bean public String name2(){ return "Ayanger2"; } @Bean public Student s2(String n){ return new Student(n,12); } }运行后发现报错,s2 需要一个单例,但是却发现两个👇
此时就可以用 @Qualifier 去指定名称,比如 name2👇
@Component public class StudentComponent { @Primary @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } @Bean public String name2(){ return "Ayanger2"; } @Bean public Student s2(@Qualifier("name2") String n){ return new Student(n,12); } }此时拿到的就是 name2 对应的对象👇
当然,改成 name 后就是拿到的 name 对应的对象~~
方式三:@Resource
上面两个都是 Spring 的注解,都是 @Autowired 的解决方式,是根据类型的,我们没办法去指定名称,而 @Resource 却是来自 JDK👇
import jakarta.annotation.Resource;闲聊:Java EE 在 2018 年的时候就更名为 jakarta EE 了,在 JDK 8 的时候这个注解就是在 JDK 里面的,在 2017 年的时候就把 Java EE 捐给 eclipse 了,然后 eclipse 就要求他使用 Java 这个名字,所以这时候关于 Java EE 的部分就拆出来了,然后就放在 Jakarta 里面了,所以虽然是拆出来了,但是我们还是倾向于认为它还是 JDK 提供的东西
我们的注解也不是说 Spring 才有的,这并不是什么很新鲜的事情,JDK 中就存在很多注解👇
以上就都是 JDK 提供的,准确而言是 Jakarta 提供的~~
使用@Resource注解:是按照 bean 的名称进行注入。通过 name 属性指定要注入的 bean 的名称
@Service//此时也是交给Spring管理了 public class UserService { @Resource(name="s3") private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }我们此时发现打印的就是 s3 的对象👇
当我们把名称改为 s2 之后👇
@Service//此时也是交给Spring管理了 public class UserService { @Resource(name="s2") private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }此时打印的就是 s2 的对象👇
答疑
Q1:为啥 Spring 中只提到了前两种解决方式呢??
因为 @Primary 和 @Qualifier 都是 Spring 自己的解决方式,而下面的 @Resource 是 JDK 的解决方式,就好比于出现问题优先内部解决~~
Q2:那未来工作中哪种注解比较常用呢??
@Qualifier 和 @Resource,@Primary 使用的非常少,基本见不到~~
依赖注入,通常使用 @Autowired 和 @Resource,放在参数就通常使用 @Qualifier~~
常见面试题:(频率比较高)
@Autowird 与 @Resource的区别
• @Autowired 是 spring 框架提供的注解,而 @Resource 是 JDK 提供的注解
• @Autowired 默认是按照类型注入,而 @Resource 是按照名称注入.闲聊:这句话并不严谨,但是面试官爱听~~
因为@Resource 需要类型一样才能按照名称注入,不然是注入不了的,所以要 类型+名称都要满足的,@Autowired 就是类型,但是名称也是起一点点作用的,我们下面来演示一下👇
@Component public class StudentComponent { @Bean({"s3","s4"}) public Student s1(){ return new Student("CoderYanger",21); } @Bean public String name(){ return "Ayanger"; } @Bean public String name2(){ return "Ayanger2"; } @Bean public Student s2(@Qualifier("name2") String n){ return new Student(n,12); } }@SpringBootApplication //加上这个注解之后,不管这个类名叫什么,它都是一个启动类 public class SpringIocDemoApplication { public static void main(String[] args) { //获取Spring上下文对象,直接使用父类接收 ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args); //从Spring容器中拿到UserService,并调用它的方法 UserService bean=context.getBean(UserService.class); bean.print(); } }当名称为 s1 时👇
@Service//此时也是交给Spring管理了 public class UserService { @Autowired private Student s1; public void print(){ System.out.println("do Service"); System.out.println(s1); } }发现报错了,只需要一个,但是找到了两个👇
我们前面说过,这个名称无所谓,叫啥都行,那么我们接下来改成 s2👇
@Service//此时也是交给Spring管理了 public class UserService { @Autowired private Student s2; public void print(){ System.out.println("do Service"); System.out.println(s2); } }我们发现它又不报错了,找到了👇
难道名称是有效的吗??再改成 s3👇
@Service//此时也是交给Spring管理了 public class UserService { @Autowired private Student s3; public void print(){ System.out.println("do Service"); System.out.println(s3); } }我们发现名称确实是有效的👇
这就是这句话不严谨的原因~~
相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean.
闲聊:所以即使不严谨,也要先回答面试官爱听的,@Autowired 按照类型,@Resource 按照名称~~
Autowired装配顺序(需要掌握)
这也体现出了 @Qualifier 比属性名称优先级要高
我们也可以在代码中体现👇
@Service//此时也是交给Spring管理了 public class UserService { @Qualifier("s2")//指定bean的名称为s2 @Autowired private Student s3; public void print(){ System.out.println("do Service"); System.out.println(s3); } }此时 @Qualifier 指定 s2,但是属性名称指定 s3,我们会发现最终找到的对象是 s2👇
小结
IoC
(1)五大类注解和一个方法注解
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
2. 方法注解:@Bean.其中五大类注解分别代表不同的语义
@Controller:控制层
@Service:业务逻辑层
@Repository:数据层
@Component:组件层
@Configuration:配置层
类注解在代码上生成的对象只有一个,多次获取得到的也只有一个
在使用上可以理解为 @Service、@Repository、@Component、@Configuration 在功能上是可以替换的,但是在语义上是不同的,但是 @Controller 不要与它们替换,接收请求最外层必须使用 @Controller,否则会出现意想不到的错误~~
方法注解使用的场景:
1.代码不在我们这里,在其他人写的代码,那我们想把这个对象交给 Spring 进行管理,我们没有办法去加,就可以通过 @Bean
2.对于一个类型需要创建多个对象的时候,使用 @Bean 注解
(2)ApplicationContext:Spring 的上下文对象,可以理解为 Spring 的运行环境,应用程序运行的过程中需要获取到的一些资源都来自 ApplicationContext,这是一个很大的东西,比如放了 JDK 的版本、环境变量、应用程序对象……可以通过 ApplicationContext 来获取对象
其中一种方式就是 getBean,这个方法来自 Bean工厂👇
(3)Bean 的名称
1.Bean Name 默认为类名的小驼峰
2.如果类名前两位为大写,BeanName 为类名,也可以通过注解的 value 属性来修改
(4)Bean
1.BeanName 为方法名
2.也可以通过 name 属性,来指定 BeanName
DI
(1)三种注入:属性注入、构造方法注入、Setter 注入
属性注入:直接在属性上加上 @Autowired,它会根据类型然后去容器中查找并且赋值
构造方法注入:在构造方法上加上 @Autowired,如果只有一个构造方法,@Autowired 可以省略掉,如果有多个,需要添加上 @Autowired 注解来指明到底使用哪个
Setter 注入:在 set 方法上添加 @Autowired 注解
(2)优缺点:了解一下即可
(3)@Autowired 存在的问题
@Autowired 是根据类型查找的,当同一个类型有多个 Bean 的时候,使用 @Autowired 会出现问题
解决方式:三种
@Primary:指定默认的 bean,如果有多个就会去找这个
@Qualifier:指定 bean 名称
@Resource:指定 bean 名称
5.练习
通过上面的学习, 我们把前面的图书管理系统代码进行调整
Service层的注解, 改成 @Service
Dao层的注解, 改成 @Repository
重新运行代码, 验证程序访问正常
6.总结
6.1 Spring,Spring Boot 和 Spring MVC 的关系以及区别
Spring:简单来说, Spring 是一个开发应用框架,什么样的框架呢,有这么几个标签:轻量级、一站式、模块化,其目的是用于简化企业级应用程序开发.
Spring的主要功能: 管理对象(IoC),以及对象之间的依赖关系(DI), 面向切面编程(还没学), 数据库事务管理(还没学), 数据访问, web框架支持等.
但是Spring具备高度可开放性, 并不强制依赖Spring, 开发者可以自由选择Spring的部分或者全部, Spring可以无缝继承第三方框架, 比如数据访问框架(Hibernate 、JPA), web框架(如Struts、JSF)这是 Spring 相关的👇
所以 IoC 和 DI 都是 Spring 提供的功能,对对象管理,而上面的这些就是 SpringBoot 相关的👇
SpringBoot 主要是基于 Spring 给我们进行一些二次开发,就像上次说的 12306 一样
Spring:类似于火车、高铁
Spring Boot:类似于 12306,基于火车的基础上提供一些功能,不局限于火车~~
比如已经帮我们集成了 tomcat,这就不需要我们自己安装了
Spring MVC:类似火车站,火车票代售点,提供一些对外访问的功能,比如像 RequestMapping,就来自于 SpringMVC👇
Spring MVC:Spring MVC是Spring的一个子框架, Spring诞生之后, 大家觉得很好用, 于是按照MVC 模式设计了一个 MVC框架(一些用Spring 解耦的组件), 主要用于开发WEB应用和网络接口,所以,Spring MVC 是一个Web框架.
Spring MVC基于Spring进行开发的, 天生的与Spring框架集成. 可以让我们更简洁的进行Web层开发, 支持灵活的 URL 到页面控制器的映射, 提供了强大的约定大于配置的契约式编程支持, 非常容易与其他视图框架集成,如 Velocity、FreeMarker等
Spring Boot:Spring Boot是对Spring的一个封装, 为了简化Spring应用的开发而出现的,中小型企业,没有成本研究自己的框架, 使用Spring Boot 可以更加快速的搭建框架, 降级开发成本, 让开发人员更加专注于Spring应用的开发,而无需过多关注XML的配置和一些底层的实现.
Spring Boot 是个脚手架, 插拔式搭建项目, 可以快速的集成其他框架进来.比如想使用SpringBoot开发Web项目, 只需要引入Spring MVC框架即可, Web开发的工作是
SpringMVC完成的, 而不是SpringBoot, 想完成数据访问, 只需要引入Mybatis框架即可.
Spring Boot只是辅助简化项目开发的, 让开发变得更加简单, 甚至不需要额外的web服务器, 直接生成jar包执行即可.
最后一句话总结:Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的一个MVC 框架,而Spring Boot 是基于Spring的一套快速开发整合包.
比如我们的图书系统代码中
整体框架是通过SpringBoot搭建的👇
IoC, DI功能是Spring的提供的
web相关功能是Spring MVC提供的这三者专注的领域不同,解决的问题也不一样, 总的来说,Spring 就像一个大家族,有众多衍生产品, 但他们的基础都是Spring, 用一张图来表示他们三个的关系:
6.2 bean 的命名
1) 五大注解存储的bean
① 前两位字母均为大写, bean名称为类名
② 其他的为类名首字母小写
③ 通过 value属性设置 @Controller(value = "user")
2) @Bean 注解存储的bean
① bean名称为方法名
②通过name属性设置 @Bean(name = {"u1","user1"})
6.3 常见面试题
1) 三种注入方式的优缺点(参考上面课件内容)
2) 常见注解有哪些? 分别是什么作用?
web url映射: @RequestMapping
参数接收和接口响应: @RequestParam, @RequestBody, @ResponseBody
bean的存储: @Controller, @Service, @Repository, @Component, @Configuration,
@Bean
bean的获取: @Autowired, @Qualifier, @Resource
3) @Autowired 和@Resource 区别
4) 说下你对Spring, SpringMVC, Springboot的理解(参考上面课件)