前言:
在 Spring 中想要更简单的存储和读取对象的核⼼是使用注解,也就是我们接下来要学习 Spring 中的相关注解,来存储和读取 Bean 对象。
1.存储 Bean 对象
之前我们存储 Bean 时,需要在 spring-config 中添加⼀行 bean 注册内容才行,如下图所示:
1.1 前置⼯作:配置扫描路径
注意:想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中
在 spring-config.xml 添加如下配置:
<?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.java.demo"></content:component-scan>
</beans>
注意:即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的。
1.2 添加注解存储 Bean 对象
public class app {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
User user = (User) context.getBean(
"user");
// 3.调⽤ bean ⽅法
user.sayHi("111");
}
}
想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
2. ⽅法注解:@Bean。
@Controller(控制器存储)
使⽤ @Controller 存储 bean 的代码如下所示:
@Controller
public class User {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
此时我们先使用之前读取对象的方式来读取上⾯的 User对象,如下代码所示:
public class app {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
User user = (User) context.getBean(
"user");
// 3.调⽤ bean ⽅法
user.sayHi("111");
}
}
@Service(服务存储)
使用@Service 存储 bean 的代码如下所示:
@Service
public class User {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
读取上面的 User对象:
public class app {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
User user = (User) context.getBean(
"user");
// 3.调⽤ bean ⽅法
user.sayHi("111");
}
}
@Component(组件存储)
使用 @Component 存储 bean 的代码如下所示:
@Component
public class User {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
读取上面的User类:
public class app {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
User user = (User) context.getBean(
"user");
// 3.调⽤ bean ⽅法
user.sayHi("111");
}
}
@Configuration(配置存储)
使用 @Configuration 存储 bean 的代码如下所示:
@Configuration
public class User {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
读取上面的User类:
public class app {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
User user = (User) context.getBean(
"user");
// 3.调⽤ bean ⽅法
user.sayHi("111");
}
}
思考:为什么需要这么多的类注解呢?
既然功能都是一样的,我们为什么需要这么多的注解呢?
这是为了程序员看到类注解之后,就能直接了解当前类的用途,比如:
@Controller:表示的是业务逻辑层;
@Servie:服务层;
@Repository:持久层;
@Configuration:配置层。
1.3类注解之间的关系
查看 @Controller / @Service等注解的源码我们可以发现:
其实这些注解里面都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“子类”
Bean 命名规则
通过上面示例,我们可以看出,通常我们 bean 使用的都是标准的大驼峰命名,而读取的时候首字母小写就可以获取到 bean 了,如下图所示
但是当我们首字母和第⼆个字母都是大写时,就不能正常读取 bean 了,会抛异常,如下图所示:
在idea里面搜索beanName
最后找到了bean的命名规则
它使用的是 JDK Introspector 中的 decapitalize 方法,源码如下:
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,那么Bean的首字母也为大写
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
类注解是添加到某个类上的,而方法注解是放到某个方法上的,如以下代码的实现:
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
public class app {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context.getBean("user1");
System.out.println(user.toString());
}
}
当我们尝试获取 bean 对象中的 user1 时却发现,根本获取不到,抛出异常如下:
我们接下来往下面看
方法注解要配合类注解使用
在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:
@Service
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
再次执行以上代码,就可以成功执行了
重命名 Bean
可以通过设置 name 属性给 Bean 对象进行重命名操作,如下代码所示:
@Service
public class Users {
@Bean(name = "u1")
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
然后我们使用u1就可以获取到User对象了,
public class app {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context.getBean("u1");
System.out.println(user.toString());
}
}
运行结果如下:
注意:使用 @Bean 注解并重命名, 原来的的名字(user1)就获取不到User类对象了
⼀个 bean 可以有多个名字:
@Bean(name = {"u1","u"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
2.获取 Bean 对象
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入
对象装配(对象注入)的实现方法以下 3 种:
1. 属性注入
2. 构造方法注入
3. Setter 注入
在实际的开发模式中,一般都是将 Service 类注入到 Controller 类中
2.1属性注入
属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中
Service 类的实现代码如下:
@Service
public class UserService {
/**
* 根据 ID 获取⽤户数据
*
* @param id
* @return
*/
public User getUser(Integer id) {
User user = new User();
user.setId(id);
user.setName("Java =" + id);
return user;
}
}
Controller 类的实现代码如下:
@Controller
public class UserController {
// 注⼊⽅法1:属性注⼊
@Autowired
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
获取 Controller 中的 getUser 方法:
public class app {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean(UserController.class);
System.out.println(userController.getUser(1).toString());
}
}
运行结果如下:
2.2 构造方法注入
构造方法注入是在类的构造方法中实现注入,如下代码所示:
@Controller
public class UserController2 {
// 注⼊⽅法2:构造⽅法注⼊
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
注意:如果只有⼀个构造方法,那么 @Autowired 注解可以省略:
但是如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法,否则程序会报错
2.3 Setter 注入
Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set 方法的时候需要加上 @Autowired 注 解,如下代码所示:
@Controller
public class UserController3 {
// 注⼊⽅法3:Setter注⼊
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
}
}
@Resource:另⼀种注入关键字
在进行类注入时,除了可以使用 @Autowired 关键字之外,我们还可以使用 @Resource 进行注入,如下代码所示:
@Controller
public class UserController {
// 注⼊
@Resource
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
}
}
@Autowired 和 @Resource 的区别
出身不同:@Autowired 来自于 Spring,而 @Resource 来⾃于 JDK 的注解;
使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。
@Autowired 可用于 Setter 注入、构造函数注入和属性注入,而 @Resource 只能用于 Setter 注 入和属性注入,不能用于构造函数注入。
3.三种注入方式的优缺点
属性注入的优点是简洁,使用方便;缺点是只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只 有在使用的时候才会出现 NPE(空指针异常)。
构造方法注入是 Spring 推荐的注入方式,它的缺点是如果有多个注入会显得比较臃肿,但出现这 种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点是通用性,在使用之前⼀定能把保证注入的类不为空。
Setter 方式是 Spring 前期版本推荐的注入方式,但通⽤性不如构造方法,所有 Spring 现版本已经推荐使用构造方法注入的方式来进行类注入了。
4.总结
1. 将对象存储到 Spring 中:
a. 使用类注解:@Controller、@Service、@Repository、@Configuration、@Component【它们之间的关系】
b. 使用方法注解:@Bean【注意事项:必须配合类注解⼀起使用】
2. Bean 的命名规则:
第二个字母都非大写,首字母小写来获取 Bean,如果首字母和第二个字母都是大写,那么直接使用原 Bean 名来获取 Bean。
3. 从 Spring 中获取对象:
a. 属性注入 b. Setter 注入 c. 构造函数注入(推荐)
4. 注⼊的关键字有:
a. @Autowired b. @Resource
5. @Autowired 和 @Resource 区别:
出身不同; 使用时设置参数不同 @Resource ⽀持更多的参 数,⽐如 name。
希望对大家有帮助!