文章目录
1. 使用 Bean 标签
在 Spring 的配置文件中添加 Bean 标签, 可以把指定类的实例以指定的名称 (id) 存储到容器中, 将来可以使用这个名称获取到该实例.
1.1 创建 Bean 类
public class Student {
public void sayHello(){
System.out.println("hello Student!");
}
}
1.2 将 Bean 类注册到容器中
通过这个 bean 标签就将 Student 类的实例存储到容器中了, spring 容器中会以 map 的结构组织这些对象, key 就是我们设置的 id, value 就是 bean 对象.
1.3 获取并使用 bean 对象
public class APP {
public static void main(String[] args) {
// 1.获取 spring 上下文.对于容器的一系列操作都是通过这个上下文对象完成的.创建该对象的时候需要指定配置信息.
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2.调用 getBean 方法获取容器中的 Bean 对象.
Student student = context.getBean("student", Student.class);
// 3.使用 bean 对象.
student.sayHello();
}
}
代码运行后, 成功获取到对象并打印出了信息.
注意事项:
a.Bean 对象的名称, 也就是我们在配置文件中添加 bean 标签时指定的 id.
b.getBean方法有很多重载方法, 它支持:
-
通过名称 + 类型获取对象.
-
只通过类型获取, 如下所示:
Student student = context.getBean(Student.class);
但是存在隐患的就是如果该类型的对象在容器中存在多个, 则会抛出以下异常:
-
只通过 id 获取, 如下所示:
Student student = (Student) context.getBean("student");
但此时返回的 bean 对象类型就是 object, 还需要进行类型的强转!
2. 使用注解
使用上述方式, 每次向容器中存储一个 bean 对象就需要在配置文件中去添加一个 bean 标签, 这显然是很麻烦的, 使用注解可以简化这个流程.
2.1 前置工作, 配置扫描路径.
在使用类注解实现自动往容器中添加对象之前, 需要先在配置文件中设置扫描路径, 这个工作是必须的!!! 因为如果我们不设置扫描路径的话, spring 它是不会去扫描当前项目中的类并读取类上添加的注解.
只需要在配置文件中添加一个 content:component-scan 标签, 标签中添加一个 base-package 属性, 属性的值就是扫描路径.
2.2 添加类注解存储 Bean 对象
想要将对象存储在 spring 容器中, 有两种注解类型可以实现.
- 类注解: @Controller、@Service、@Repository、@Component、@Configuration.
- 方法注解: @Bean
2.2.1 @Controller
使用 @Controller 注解存储 bean 的代码示例:
//将该类的实例存储到 spring 容器中去
@Controller
public class UserController {
public void printHello(){
System.out.println("from com.demo.controller.UserController: hello!");
}
}
然后我们在启动类的 main 方法中尝试获取该对象并调用 printHello 方法:
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean("userController",UserController.class);
userController.printHello();
}
}
运行结果:
2.2.2 @Service
代码示例:
@Service
public class UserService {
public void printHello(){
System.out.println("form package com.demo.service.userService:hello!");
}
}
测试代码:
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = context.getBean("userService",UserService.class);
userService.printHello();
}
}
2.2.3 @Repository
代码示例:
@Repository
public class UserRepository {
public void printHello(){
System.out.println("from package com.demo.repository.UserRepository:hello!");
}
}
测试代码:
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserRepository userRepository = context.getBean("userRepository",UserRepository.class);
userRepository.printHello();
}
}
其余的两个类注解在使用和功能上同上诉三个注解是一致的, 就不过多举例了.
2.3 为什么需要那么多注解
通过上诉示例我们发现, 这几个类注解功能是一致的, 那就是将该类的实例存储到容器中, 那么为什么会有这么多注解呢?
spring 提供这么多注解主要是用于直观的标识某一个类的用途, 让程序员看到这个注解就能大概猜到这个类主要是干嘛的, 起到类似于注释的效果.
比如:
- @Controller 控制层. 参数验证和拦截相关的功能在这层实现.
- @Service 服务层. 主要做服务调度.
- @Repository 持久层. 直接操作数据库.
- @Configuration 配置层.
- @Component 存放一些不方便划分进上诉分层的工具组件.
调用流程如下:
2.4 使用类注解时 Bean 对象的命名规则.
上诉示例中, 我们使用类名首字母小写作为 Bean 对象的名称, 但不总是这样的, 还存在特例. 如下面这个示例:
@Component
public class UComponent {
public void printHello(){
System.out.println("from package com.demo.component.UComponent:hello!");
}
}
当我们尝试使用类名首字母小写, 也就是 “uComponent” 去获取 bean 对象时:
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UComponent uComponent = context.getBean("uComponent",UComponent.class);
uComponent.printHello();
}
}
抛出了异常, 异常原因就是在 spring 容器中不存在一个名称为 uComponent 的对象.
2.4.1 默认的命名规则
如果在使用类注解时, 没有传递任何的参数, 那么 spring 会调用如下方法生成一个默认的 bean 对象名称.
该方法最终调用的是 JDK Introspector 中的 decapitalize ⽅法, 源码如下:
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);
}
所以默认的命名规则如下:
判断类名的首字母和第二个字母是否都是大写. 如果是, 则 bean 对象的名称就是类名.
如果不是, 则使用类名首字母小写作为 bean 对象的名称.
2.4.2 重命名
当然 spring 也支持我们在使用类注解时, 显示指定 bean 对象的名称.
观察各个注解源码我们发现, 每个注解都提供了一个 String 类型的 value 属性, 它就是用于设置 bean 对象名称的.
对于上述示例, 我们显示指定 bean 对象的名称为 “uComponent”.
@Component("uComponent")
public class UComponent {
public void printHello(){
System.out.println("from package com.demo.component.UComponent:hello!");
}
}
再使用 “uComponent” 这个名称获取 bean 对象的时候, 就不会报错了.
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UComponent uComponent = context.getBean("uComponent",UComponent.class);
uComponent.printHello();
}
}
运行程序, 成功获取到了对象并调用了它的方法:
2.5 方法注解 @Bean
方法注解用于添加到一个方法上, 该方法的返回值会被存储到 spring 容器中.
使用 @Bean 注解应当注意的是:
1.通过 @Bean 注解存储到容器中的对象, 默认名称为方法名. 当然可以在使用该注解的时候, 显示指定对象的名称, 并且 spring 允许给一个对象添加多个名称.
@Bean 注解的源码如下, 其中 value 和 name 可以看作是一个属性, 类型是 “String[]”.
2.@Bean 注解得搭配 Bean 标签或者是类注解一起使用, 即定义该方法的类, 一定得在 spring 容器中存在相应的 Bean 对象. 只有这样 spring 才会扫描该类, 去读取方法上添加的 @Bean 注解, 然后通过存储在容器中的该类的对象来调用此方法, 并将方法的返回值作为一个 Bean 对象存储到容器中.
简单代码示例:
@Controller
public class UserController {
@Bean({"userService","service","user"})
public UserService getUserService(){
return new UserService();
}
}
public class APP {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 分别通过 userService, service, user 获取对象
UserService userService1 = context.getBean("userService",UserService.class);
UserService userService2 = context.getBean("service",UserService.class);
UserService userService3 = context.getBean("user",UserService.class);
userService1.printHello();
userService2.printHello();
userService3.printHello();
}
}
程序的运行结果: