Spring 更简单的读取和存储对象
存储 Bean 对象
通过注解,来实现更简单的存储和读取对象。但必须要配置扫描路径,就是在 配置文件当作添加 content:component-scan:
<?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">
<!--写入要存储的路径-->
<content:component-scan base-package="com.beans"></content:component-scan>
</beans>
意思就是,当前要存储的对象是在 com.beans 包下的对象。
使用五大类注解实现
五个注解如下:
- Controller 控制器
- Service 服务
- Repository 仓库
- Configuration 配置
- Component 组件
Controller
就像之前存储的话,是要在 配置文件 当中来实现注入,现在直接通过 Controller 就可以了:
@Controller
public class blog {
public void sayHi() {
System.out.println("hello Controller");
}
}
这样就完成了对象的注入,使用原始的读取方式读取对象:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
Blog blog = context.getBean("blog", Blog.class);
//使用 bean
blog.sayHi();
}
}
运行结果如下:
但要注意的是,对象所在的包,只能是在 配置文件 的包下面。
Service
使用 Service 注解:
@Service
public class UserService {
public void sayHi() {
System.out.println("hello Service");
}
}
代码:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserService userService = context.getBean(UserService.class);
//使用 bean
userService.sayHi();
}
}
运行结果如下:
Repository
使用 Repository 注解:
@Repository
public class UserRepository {
public void sayHi() {
System.out.println("hello Repository");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserRepository userRepository = context.getBean(UserRepository.class);
//使用 bean
userRepository.sayHi();
}
}
运行结果如下:
Configuration
使用 Configuration 注解,要注意的是,千万不要把 Configuration 写成 Configurable:
@Configuration
public class UserConfiguration {
public void sayHi() {
System.out.println("hello Configuration");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
//使用 bean
userConfiguration.sayHi();
}
}
运行结果如下:
Component
通过 Component 注解来注入对象:
@Component
public class UserComponent {
public void sayHi() {
System.out.println("hello Component");
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserComponent userComponent = context.getBean(UserComponent.class);
//使用 bean
userComponent.sayHi();
}
}
运行结果如下:
为什么需要五大类注解
明明每个注解都能实现类的注入,那为什么还需要五个呢?因为这就涉及到软件工程方面的知识了,一个软件至少会分为一下四层:
- Configuration 就是配置层:关于当前项目中的所有配置,都会写在这个文件夹里。
- Controller 就是控制层:就是前端参数校验,主要就是校验前端数据的合理性和正确性。
- Service 就是服务层,负责数据的组装和接口调用。更直白的说,服务器九四 “牵线搭桥” 让程序去调用对应的功能/接口/服务。
- Repository 叫做仓库,但其实是 数据持久层,也叫做 Dao 层,就是对数据进行持久化操作(对数据库进行增删改查操作)。
一个软件至少需要这四层,那么这五个类注解,可以更直观的让程序员知道当前类的用途。可以让代码的可读性更高。
五大类中间的关系
查看 Configuration 的源码,发现父类是 Component :
查看 Controller 的源码,发现仍然是依靠 Component 来实现的:
查看 Repository 源码,还是依靠 Component 来实现的:
查看 Service 的源码,发现还是一样:
也就是说,这些注解的作用是一样的,但是对于开发人员的不一样的。Component 是所有类的父类。
Spring 使用五大类注解,生成 bean name:通过查看 AnnotationBeanNameGenerator.class 文件:
当然,字节码文件看起来是很累的,通过 gitee 里面可以找到源文件。在全站搜索 spring frame 相关项目就可以找到了:
然后就是源码了:
在 IDEA 里面查看 class 文件,找到名字相关的方法:
然后发现又调用了这个代码:
然后再次进入:
然后找到文件位置:
再往上找,发现是 JDK 里面的:
所以,只要看这段代码就知道为什么了:
也就是只有第一个字母是大写的,类名才是小写,当前两个字母都是大写的时候,返回的就是原类名了。
使用 Bean 注解来实现
听过 Bean 来注入,就是 Bean 方法注解。Bean 注解,是针对方法实现的,只能添加在 方法上面:
-
先创建 User 对象:
public class User { private int id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
然后添加方法
@Component public class UserBeans { @Bean public User user1() { User user = new User(); user.setId(1); user.setName("张三"); return user; } }
这样就完成了对象的注入。
-
获取对象
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
User user = context.getBean("user1", User.class);
//使用 bean
System.out.println(user);
}
}
运行结果如下:
获取 Bean 对象
获取 bean 对象也叫做对象装配,是把对象从容器中取出来放到某个类中,有时候也叫对象注入。对象装配的实现方法有三种:
属性注入
通过 @Autowired 注入:
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi() {
userService.sayHi();
}
}
就是把 UserService 类注入,然后就可以使用类了:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserController userController = context.getBean(UserController.class);
//使用 bean
userController.sayHi();
}
}
运行结果如下:
构造方法注入
通过一个构造方法来实现属性注入:
@Controller
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
运行结果如下:
如果类当中,只有一个构造方法,那么 @Autowird 可以省略。
Setter 注入
通过 Setter 方法注入:
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
运行结果如下:
属性注入 和 构造方法注入 和 Setter 注入的区别
- 属性注入特点是写法简单,但是通用性不好,只能运行在 IoC 容器下,如果是 非 IoC 容器的话,就会出现问题。
- 构造方法注入,通用性更好。因为构造方法的优先级是最高的。确保使用注入对象之前,对象一定初始化过了。当构造方法注入参数太多时,就需要检查自己的代码是否符合单一设计原则的规范了。构造方法注入,也是 Spring 推荐的注入方法。
- Setter 注入是早期 Spring 版本推荐的写法,但是通用性没有构造方法注入通用,因为只有一个参数。
@Resource 和 @Autowired 的区别
-
相同点:都可以完成对象的注入。
-
不同点:
a)出身不同,@Resource 来自于 JDK,@Autowired 是由 Spring 框架提供的。b)@Autowired 支持属性注入,构造方法注入 和 Setter 注入,而 @Resource 不支持构造方法注入。
c)支持的参数不同:@Resource 支持更多的参数设置,比如 name、type 设置。而 @Autowired 只支持 required 参数设置。
重命名 Bean
比如我们有多个 User 对象,那么获取 Bean 的时候。就要通过方法名获取了,但是如果重命名对象。也可以得到,就是在 Bean 后面加个括号,里面重命名就好了:
@Component
public class UserBeans {
@Bean(name = "userInfo")
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
代码如下:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
User user = context.getBean("userInfo", User.class);
//使用 bean
System.out.println(user);
}
}
运行结果如下:
如果是重命名多个名字的时候,就用大括号把名字括起来:
@Component
public class UserBeans {
@Bean(name = {"userInfo", "user1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
@Bean 的命名规则: 当没有设置 name 属性的时候,那么 bean 默认的名称就是方法名。当设置了 name 属性之后,只能通过重命名的那么属性对应的值类获取。也就是重命名之后,再使用方法名就获取不到 bean 对象了。
把 Bean 对象注入到其他类
我们把 User 对象(有多个对象)注入到 Controller 当中,看看能不能实现对象的使用:
@Controller
public class UserController {
@Autowired
private User user;
public void sayHi() {
System.out.println("User-> " + user);
}
}
然后通过 UserController 来使用对象:
public class Test1 {
public static void main(String[] args) {
//得到上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//得到 bean
UserController userController = context.getBean(UserController.class);
//使用 bean
userController.sayHi();
}
}
运行结果如下:
报错提示说,User 对象被注入了两个。一个是 userInfo,一个是 user2。因为 Autowired 在注入的时候,是先根据类型来查找,然后就会去找 User 对象,找 User 类型的对象在 Spring 当中有没有。然后就找到了两个,然后就报错。
一个对象注入多次的问题
就是上面这种情况,在扫描的时候,发现了多个对象。解决方案如下:
-
精确的描述 bean 的名称,就是将注入的名称写对。比如说我们注入 user1 :
@Controller public class UserController { @Autowired private User user1; public void sayHi() { System.out.println("User-> " + user1); } }
运行结果如下:
-
使用 Resource 注解,Resource 是来自 JDK 的,Resource 有 name 属性,就是类似于重命名:
@Controller public class UserController { @Resource(name = "user1") private User user; public void sayHi() { System.out.println("User-> " + user); } }
运行结果如下:
-
通过 Autowired 搭配 Qualifier :
@Controller public class UserController { @Autowired @Qualifier("user1") private User user; public void sayHi() { System.out.println("User-> " + user); } }
运行结果如下: