目录
1. 修改其中一个类中 @Controller的 value 属性
一. 配置扫描路径
前面创建 Spring 项目和简单使用一文中说到(如何创建一个 Spring 项目并简单使用), 在获取 Spring 的上下文对象时, 是先去 配置文件中读取, 获得 Spring 容器. 因此, 在使用更简单的存 Bean 对象的注解时, 我们也需要进行配置文件
<?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">
<!-- 配置扫描路径, 指定要扫描那个路径底下的类注解, 不在该包底下或者在该包底下不加五大类注解都不能读取 -->
<!-- 在component-scan 下的子包下加了五大类注解一样可以加入到bean中 -->
<content:component-scan base-package="demo1.java"></content:component-scan>
</beans>
想要用注解的方式将 Bean 对象注册到 Spring 容器中 , 就需要配置 content: component-scan base-package="具体包路径", 只有配置包路径地下的所有类, 添加了注解才能被正确识别到注册到 Spring 中
二. 简单存储 Bean 的五大类注解
1. @Controller 注解
同样的, 要进行注册到 Spring 容器中, 需要先创建 Bean 对象(普通 Java 对象)
创建好了对象以后, 采用三步走获取 Bean 对象并使用
调用方法成功, 表示成功获取指定 Bean 对象
@controller 表示的是业务逻辑层
2. @Service 注解
同上面一样, 创建 Bean 对象并添加注解进行获取使用
调用方法成功
3. @Repository 注解
调用方法成功
4. @Component 注解
调用方法成功
@Component 组件存储
5. @Configuration 注解
调用方法成功
6. Bean 的命名规则
上面的五大类注解中, 都会发现一个问题: 那就是这里的获取 Bean 对象时, 使用的不在是 spring-config.xml 里的指定 Bean 标签的 id 和类 类型的组合获取 Bean 对象, 哪该如何获取类注解下注入的 Bean 对象呢?
下面看一组示例
调用方法验证
此时我们发现, 和上面五大类注解讲解的时候是一样的获取方法, 但是现在却报错了这是为什么? 仔细看不难发现, 之前的五大类讲解时, 都是类名首字母大写, 而这里是类名的首字母和第二个字母都大写, 因此, 此处我们就要去看看 Bean 的命名规则到底是如何讲解的了
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
以上为 BeanName 的命名源码, 从上面源码可以看出, 当 首字母和第二个字母都大写的时候, 直接放回当前类名
7. 为什么有这么多相同功能的类注解
细心的可以发现, 上面五大类注解都干了同一件事, 那就是将 Bean 对象注入到 Spring 容器中, 那为什么还需要搞这么多个类注解呢? 是否有这个疑问?
我们知道, 在开发中, 会需要大量注解不同的对象以供使用, 那么这些对象的用途都是什么呢? 既然它叫做类注解, 说明他是在类上作用的, 同时他最大的作用还有注解功能! 让程序员可以一眼就看出来这个类的用途, 高效开发!
8. 五大类注解之间有关系嘛?
既然功能都是差不多, 那么他们之间有什么关系嘛?
就这个问题, 去看他们的源码
1. @Controller 源码
2. @Service 源码
3. @Repository 源码
4. @Configuration 源码
5. @Component 源码
通过观察上述源码, 发现五大类注解中, 都包含有 @Target @Retention @Documented 注解 ,
同时 除了 @Component 注解外, 其余四个注解都包含了 @Component 注解
因此, 我们可以知道, 其他四个注解都是 @Component 注解的子类
三. @Bean 方法注解
前面学的类注解, 是添加在类上的, 而 Bean 方法注解则是添加到方法上的, 通常 @Bean注解是需要有返回值的, 表明通过 Bean 方法注解把某个返回的对象注入到 Spring 容器中. 这样就可以将自定义的类型在需要的时候注入到 Spring 容器中
此处创建一个 User 类, 里面包括一些 username, Id, age 等字段表明 User 的信息, 下面将这些信息注入到 Spring 中
接下来正常去获取 Spring 容器并获得Bean对象使用
明明和之前五大类注解的获取方式一样, bean命名一样, 却获取不到正确 Bean 对象这是为什么? 原因一共有以下两点:
1. @Bean 方法注解的命名规则不同
在@Bean 方法注解中, 不在是使用五大类注解的 Bean 命名规则: 首字母大写则命名时首字母小写; 首字母和第二个字母都大写, 则按原类名命名
@Bean 的命名规则默认为@Bean 方法注解加到那个方法上, bean的名称就是 方法该名称, 例如此处 @Bean 加到了 user1 方法上, 因此此处应该使用 user1 名
对于第一个问题, 为什么此处是 User.class ?
由于@Bean 注解的作用是将类中的某个方法的返回值注入到 Spring 容器中, 此处 bean 的命名用的是 user1 的方法名称, 返回值 是 User 类型, 因此此处是 User.class
对于第二个问题, 为什么此处是 User 返回值 ?
和上面一样, 用了 User.class 解析, 返回的必须是 User 类型接收
对于第三个问题, 为什么这里任然报错 ?
这就涉及到 @Bean 方法注解的第二个注意项, 详细看下文
2. @Bean 方法注解需要搭配类注解使用
调用方法验证
现在就正确拿到了 @Bean 方法注解 把 User 类型返回注入到 Spring 容器中的 Bean 对象了
那么, 只有我上面用的@Component 类注解可以搭配嘛? 其实不然, 五大类中的任意一个都可以.
那么为什么又需要搭配五大类注解使用呢 ?
大致原因如下: 出于性能方面的考虑, 加了五大类注解以后就只能放在扫描包路径底下, 不用去检测其他地方的 @Bean 注解.
哪又有人会问了, 为什么 @Bean 方法注解不能像类注解一样直接注入到 Spring 容器中呢?
正如上面所说, 由于已经规定了五大类注解在扫描包底下才起作用, 减少扫描开销, 因此 @Bean 便引入搭配类注解使用, 来提高性能
四. 五大类注解和 @Bean 方法注解多次注入
上面已经给 五大类注解和 @bean 方法注解说明白了, 那么现在有一个问题 如果多次注入会怎么样呢? 下面就来讨论一下多次注入的情况
1. 五大类注解多次注入
有没有这样的疑问, 在扫描包路径底下, 创建两个不同的包, 不同的包下创建相同类, 并注入到 Spring 中会怎么样呢?
这两个ControllerStudent 都是位于 spring-config.xml里的扫描包路径底下的, 但是两个类在不同包底下, 哪现在该如何去获取呢?
去尝试获取 demo1.demo2 包路径底下 Bean 对象并使用时发现, 无法获取唯一的指定 Bean 对象, 那这个时候该怎么去获取呢?
1.修改其中一个类中 @Controller的 value 属性
比如现在, 我给 demo1.demo2.test 包路径底下的 ControllerStudent 设置value 属性, 起一个新的Bean 名称
再去调用运行
这个时候发现, demo1. demo2 底下的 ControllerStudent 可以正确获取了, 同时还可以用新取的name去获取 demo1.demo2.test 底下的 COntrollerStudent
2. @Bean 方法多次注入
同样的, 在指定的扫描包路径底下有两个不同包下的相同类通过 @Bean 注入到 Spring 中
获取Bean对象并使用
可以看到并没有报错, 但是为什么是输出的 demo1.demo2.test 包下的 getUserName 呢?
原因在于, 如果在扫描路径下, 多次用 @Bean 注入返回相同对象, 则会根据包级覆盖, 包越大下的对象越先注入, 会被后面的覆盖
哪要怎样才能获取指定想要的呢 ?
和上面一样. 采取给 @Bean 方法注解添加 Value 属性
调用验证
这样就可以正确获取想要的了, 但是在项目中, 建议大家还是尽量不要多次注入相同对象或者频繁创建 Spring 容器以及销毁, 避免带来非预期的调用结果, 影响程序