这也就证实了:Bean 的名称默认是小驼峰写法,如果说前两位都为大写,那么 Bean 的名称就是它自己,当然我们也可以通过注解来进行指定(这五种注解指定的方式都一样),这时 Spring 管理的时候就会以我们指定的名称去定义好~~
3.2 为什么要这么多注解?
这个也是和咱们前面讲的应用分层是呼应的. 让程序员看到类注解之后,就能直接了解当前类的用途.
@Controller:控制层, 接收请求, 对请求进行处理, 并进行响应.
@Servie:业务逻辑层, 处理具体的业务逻辑.
@Repository:数据访问层,也称为持久层. 负责数据访问操作
@Configuration:配置层. 处理项目中的一些配置信息.
@Component:组件层
下面来测试一下 @Controller 和 @Service 的区别👇
java复制代码
@ResponseBody
@Controller
//@Service
public class UserController {
@RequestMapping("/hello")
public String hello(){
return "测试Controller和Service区别";
}
}
启动服务后我们发现是可以正常响应的👇
接下来把 @Controller 去掉,换成 @Service👇
java复制代码
@ResponseBody
//@Controller
@Service
public class UserController {
@RequestMapping("/hello")
public String hello(){
return "测试Controller和Service区别";
}
}
@NoArgsConstructor//无参构造函数
@AllArgsConstructor//全参构造函数
@Data
public class Student {
private String name;
private Integer age;
}
StudentComponent.java👇
java复制代码
@Component
public class StudentComponent {
public Student s1(){
return new Student("CoderYanger",21);
}
public Student s2(){
return new Student("Ayanger",12);
}
}
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean=context.getBean(Student.class);
System.out.println(bean);
//此时我们只能通过这个方式去拿到对象
StudentComponent bean1=context.getBean(StudentComponent.class);
Student student=bean1.s1();
}
}
我们希望把 s1 和 s2 也交给 Spring 去进行管理,就可以写成👇
java复制代码
@Component
public class StudentComponent {
@Bean
public Student s1(){
return new Student("CoderYanger",21);
}
@Bean
public Student s2(){
return new Student("Ayanger",12);
}
}
此时我们通过👇的方式去拿对象,不用 . 的方式
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//获取Spring上下文对象,直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
//从Spring上下文中获取对象
Student bean=context.getBean(Student.class);
//使用对象
System.out.println(bean);
}
}
启动服务后运行👇我们观察错误日志
从报错信息中我们发现:我们加上 @Bean 之后就交给 Spring 进行管理了,但是 Spring 管理的时候肯定会给每一个对象起一个名字,而这个名字就是我们的方法名~~
对不对呢??我们来测试一下👇
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean1=(Student)context.getBean("s1");
System.out.println(bean1);
Student bean2=(Student)context.getBean("s2");
System.out.println(bean2);
}
}
发现确实能够从 Spring 中拿出来👇
所以 @Bean 就表示把这个方法(s1、s2)定义的方法交给 Spring 进行管理了,我们就可以从容器中直接获取了~~
也就是说,@Bean 可以针对同一个类, 定义多个对象.
3.3.2方法注解要配合类注解使用
在 Spring 框架的设计中,方法注解@Bean 要配合类注解才能将对象正常的存储到 Spring 容器中~~
我们换成 @Service 发现也是可以的,就是交给 Spring 管理,让 Spring 扫描这里👇
java复制代码
//@Component
@Service
public class StudentComponent {
@Bean
public Student s1(){
return new Student("CoderYanger",21);
}
@Bean
public Student s2(){
return new Student("Ayanger",12);
}
}
3.3.3 重命名 Bean
可以通过设置 name 属性给 Bean 对象进行重命名操作,这一点在源码中也有体现👇
我们把 s1 的 Bean 重命名为 s3👇
java复制代码
@Component
public class StudentComponent {
@Bean("s3")
public Student s1(){
return new Student("CoderYanger",21);
}
@Bean
public Student s2(){
return new Student("Ayanger",12);
}
}
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean=(Student)context.getBean("s1");
System.out.println(bean);
}
}
再来运行,发现已经找不到 s1了👇
再来试试 s3👇
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean=(Student)context.getBean("s3");
System.out.println(bean);
}
}
此时我们发现就能找到了👇
再来看 Bean 的源码部分👇
其中 @AliasFor 的意思是别名
这里 value 属性的别名是 name,name 属性的别名是 value,所以它们两个是一摸一样的东西,后续写 name=...... 或者 value=......都是一样的~~
对于所有的注解,当我们没有去写属性名称的时候,默认为 value 属性,也就是👇
java复制代码
@Bean(value="s3")
public Student s1(){
return new Student("CoderYanger",21);
}
又由于 value 等同于 name,所以也可以写成👇
java复制代码
@Bean(name="s3")
public Student s1(){
return new Student("CoderYanger",21);
}
又由于在源码中 name 和 value 的类型都是 String\[\],是个数组,所以我们可以给赋多个值,比如👇
java复制代码
@Bean(name={"s3","s4"})
public Student s1(){
return new Student("CoderYanger",21);
}
测试一下,发现确实都能获取到👇
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean1=(Student)context.getBean("s3");
System.out.println(bean1);
Student bean2=(Student)context.getBean("s4");
System.out.println(bean2);
}
}
得到的是同一个对象~~
name 等同于 value,而 value 又可以省略,所以也可以写成👇
java复制代码
@Bean({"s3","s4"})
public Student s1(){
return new Student("CoderYanger",21);
}
那其他注解可不可以也这样??需要看的~~
比如 Component👇
它的类型是 String,而非 String\[\],所以它就不能给多个值~~
小结
五大注解
BeanName 默认为类名的小驼峰写法,如果类名前两位均为大写,BeanName 则为类名本身
@Bean
需要搭配五大注解使用,BeanName 默认为方法名
3.3.4 额外补充:传参
如果我们需要传参👇
java复制代码
@Bean
public Student s2(String name){
return new Student(name,12);
}
Spring 会帮我们管理这个对象,管理这个对象就要执行这个方法,但是执行这个方法的话,就需要 String name 这样的参数,但是这样的参数又没有,这时 Spring 就会从 Spring 容器当中查找 String 类型的对象,赋值给 name,如果没有的话就会报错了👇
java复制代码
@SpringBootApplication
//加上这个注解之后,不管这个类名叫什么,它都是一个启动类
public class SpringIocDemoApplication {
public static void main(String[] args) {
//直接使用父类接收
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
Student bean=(Student)context.getBean("s2");
System.out.println(bean);
}
}
@Bean
private String name(){
return "Ayanger";
}
@Bean
public Student s2(String name){
return new Student(name,12);
}
这样就会把 Ayanger 赋值给 name👇
当然,它的参数也有可能是一个对象👇
java复制代码
@Component
public class StudentComponent {
@Bean({"s3","s4"})
public Student s1(){
return new Student("CoderYanger",21);
}
@Bean
private String name(){
return "Ayanger";
}
@Bean
public Student s2(Student s3){
return new Student(s3.getName(),12);
}
}
那么此时 它就会在容器中根据 Student 这个类型去拿了👇
答疑:如果另一个类里面又有一个 name,应该注入哪个??
我们来测试一下👇
StudentComponent.java 中有个 Ayanger
java复制代码
@Component
public class StudentComponent {
@Bean({"s3","s4"})
public Student s1(){
return new Student("CoderYanger",21);
}
@Bean
private String name(){
return "Ayanger";
}
@Bean
public Student s2(String name){
return new Student(name,12);
}
}
UserConfig.java 中有个 Ayanger00
java复制代码
@Configuration//此时也是交给Spring管理了
public class UserConfig {
@Bean
private String name(){
return "Ayanger00";
}
public void print(){
System.out.println("do Configuration");
}
}
运行👇发现:@Bean 下不能是 私有的
这个是语法问题,那么我们把这两个方法的 private 改成 public,再来尝试👇
我们发现 Baen 的 name 定义注册不了,因为在路径 com/bit/ioc/component/StudentComponent.class 下已经被定义了,说白了就是 不能重复,如果你想重复的话需要加上一些配置,但是这些配置咱们还没有讲~~