目录
2.1.1 bean标签是否可以和component-san一起使用呢?
2.6 五大类注解可以不在component-scan包下吗?
2.6.1 即使在component-scan 下,但是如果没有加五大类注解,一样是不能将当前对象存储到Spring:
2.6.2 在扫描路径(component -scan)下的所有子包下的类只要加了五大类注解,同样能存储到Spring中。
2.6.3 可能有的人会问,那出现在扫描路径下的不同包下存在相同的类名的情况,还能正常存储吗?
前言:之前存储Bean时,我们是利用在Spring配置文件中添加一行 bean注册内容才行:
而现在我们只需要一个注解就可以替代之前要写整整一行配置的窘境了,在开始存储之前,我们需要做一些准备工作。
一、配置扫描路径
想要将对象成功的存储到Spring中,我们需要配置一下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到Spring中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--只有在com.java.demo包下的spring才去扫描有没有五大类的注解-->
<content:component-scan base-package="com.java.demo"></content:component-scan>
</beans>
需要注意的是:
其中标红的一行为注册扫描的包:
二、使用注解存储Bean对象
想要将对象存储到Spring中,有两种注解类型可以使用:
- 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
- 方法注解:@Bean。
2.1 @Controller(控制器存储)
使用@Controller 存储 bean 的代码如下所示:
package com.java.demo;
import org.springframework.stereotype.Controller;
@Controller //将当前类存储到spring当中
public class StudentController {
public void sayHi() {
System.out.println("do student controller sayHi()");
}
}
这里我们先用ApplicationContext的方法来读取对象:
import com.java.demo.SController;
import com.java.demo.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//1.得到Spring对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean对象
StudentController studentController =
context.getBean("studentController",StudentController.class);
//3.使用bean对象
studentController.sayHi();
}
}
可能有人会觉得奇怪,在getBean()方法中,传入的第一个参数之前是填写<Bean>标签中的id,但是现在不适用bean标签存储了,应该填什么呢?
先说结论:在使用类注解存Bean的时候,只存在两种情况:
- 正常情况下,只需要将类名的首字母小写即可,也就是上述代码所展示的。
- 特例:原类名如果首字母和第二个字母都是大写的情况下,那么bean的名称就是原类名。
以下为特例的展示:
代码运行结果:
2.1.1 bean标签是否可以和component-san一起使用呢?
经过验证:两者是可以一起存在的。
2.2 @Service(服务存储)
使用@Service存储bean的代码如下所示:
package com.java.demo;
import org.springframework.stereotype.Service;
@Service //将当前类存储到spring当中
public class StudentController2 {
public void sayHi() {
System.out.println("do student controller sayHi()");
}
}
2.3 @Repository
使用@Repository存储bean的代码如下所示:
package com.java.demo;
import org.springframework.stereotype.Repository;
@Repository //将当前类存储到spring当中
public class StudentController3 {
public void sayHi() {
System.out.println("do student controller sayHi()");
}
}
2.4 @Component
package com.java.demo;
import org.springframework.stereotype.Component;
@Component //将当前类存储到spring当中
public class StudentController4 {
public void sayHi() {
System.out.println("do student controller sayHi()");
}
}
2.5 @Configuration
package com.java.demo;
import org.springframework.context.annotation.Configuration;
@Configuration //将当前类存储到spring当中
public class StudentController5 {
public void sayHi() {
System.out.println("do student controller sayHi()");
}
}
这时候我们再使用ApplicationContext的方法来读取对象,会发现这五大类注解并没有什么区别。
2.6 五大类注解可以不在component-scan包下吗?
先说结论吧:不行的,Spring为了保持效率,只会扫描在component-scan包下的注解:
2.6.1 即使在component-scan 下,但是如果没有加五大类注解,一样是不能将当前对象存储到Spring:
2.6.2 在扫描路径(component -scan)下的所有子包下的类只要加了五大类注解,同样能存储到Spring中。
2.6.3 可能有的人会问,那出现在扫描路径下的不同包下存在相同的类名的情况,还能正常存储吗?
一般情况下:是不行的,在加载Bean的时候就出现问题了:
如何修改呢?
- 直接将重名的类名修改掉
- 利用@Controller的方法,让该类名有一个别名。
我们来看看第二种方法的具体实现:
给在demo2下的包SController 添加别名:
运行结果:
2.6 五大类注解之间的关系
观察@Controller、@Service、@Repository、@Configuration 源码发现,可以认为其都是@Component的 “子类”,都是针对@Component的一个扩展。
2.6.1 为什么需要这么多注解
前面我们给大家展示过了这些注解的使用,发现功能好像是没什么区别的,那为何设计了这么多的注解呢?
这和汽车的牌照是一样的,在当今交通网络发达的情况下,每个省的高速路口都可能出现来自不同省份的汽车,这可以帮助交警们迅速的识别这是来自哪个省份的汽车。
同样的注解也是相同,可以帮助程序员们快速的识别当前类的用途:
- @Controller:控制层(业务逻辑层,主要用来验证前端,客户等发来的访问参数)
- @Service:服务层(服务调度,相当于机场里的客服中心:用于导流等)
- @Repository:数据持久层(直接操作数据库)
- @Configuration:配置层
程序的⼯程分层:
2.7 Bean的命名规则(源码)
通过在2.1 的示例,我们可以看出Bean的命名规则主要为两套:
- 正常情况下,只需要将类名的首字母小写即可。
- 特例:原类名如果首字母和第二个字母都是大写的情况下,那么bean的名称就是原类名
那我们来看看Spring关于Bean存储时生成的命名规则,
我们在idea中使用关键字搜索功能 “beanName”可以看到以下内容:
它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下:
2.8 使用方法注解(@Bean)存储Bean对象
需要注意的是@Bean注解是将方法的返回值存到Spring中,所以返回值不能为空。
创建实体类User:
运行结果:
2.8.1 方法注解得配合类注解使用
观察得出结论:@Bean注解得配合类注解使用才行——这是Spring为了提高性能而做出的规定。
试想,这种需要将方法返回值存到Spring中的事件是小众的,Spring是不可能为了这少部分的方法而去扫描在扫描路径下没有类注解的方法的。
再次执行以上代码:
2.8.2 @Bean注解的重命名
为什么需要对@Bean注解进行重命名呢?
这里我们在没有重命名的情况下是使用方法名来获取Bean,这未免有些不妥,不能够很好的表达Bean里面存储着什么,我们要做到望文生义。
观察@Bean的源码我们可以发现,可以利用name和value来进行对@Bean的重命名:
由于name和value具有相同的作用,因此这里我们使用name进行演示:
对@Bean注解起两个名字,这两个名字都是可以使用的,任意用哪个都行。
再给其起了两个名字后,再次使用默认的方法名,发现程序报错。
如果在一个类中存在多个相同返回类型的方法,那么Spring可以正常读取吗?
答案是:可以的。
以下为代码示例:
如果在不同类中@Bean注解下有相同的返回值,会出现报错吗?
答案是不会的,但是会出现新的值覆盖旧的值的情况:
这时候我们可以增加@Order注解来控制注入的顺序这一问题:
@Order里面的int值越小,权重值越高,就越先注入: