前言
上一篇章总结了,Spring的创建与使用,通过创建Maven项目配置Spring的环境依赖,创建Spring框架的项目,然后通过在Resource目录下创建Spring-config.xml配置文件,添加<bean></bean>标签将我们需要的bean对象注入到容器中,然后通过ApplicationContext获取Spring上下文,使用getBean()方法获取bean对象.
最后提出了一个问题就是:当我们想注入到容器多个对象的时候,我们希望一个个的创建标签进行创建,因为这样的操作太繁琐而且代码冗余,所以针对上述问题,Spring提供了更加简单的注入对象的操作,就是通过使用注解来完成上述操作.
目录
前言
1. 存储Bean对象
1.1 配置扫描路径
1.2 添加类注解
1.2.1 类注解的使用
1.2.2 为什么会有这么多类注解
1.2.3 类注解之间的关系
1.3 Bean对象的命名规则
1.4 方法注解@Bean
2. 获取 Bean 对象(对象装配)
2.1 属性注入
2.2 构造方法注入
2.3 Setter注入
2.4 三种注入方式的优点与缺点
2.5 @Resource 另一种注入关键字
2.6 同⼀类型多个 @Bean 解决
1. 存储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.bit.service"></content:component-scan> </beans>
1.2 添加类注解
首先我们之前就接触过类注解
上述的@WebServlet就是一个类注解
在Spring中想要将对象进行存储到Spring中,有两种注解类型可进行实现:
1. 类注解:@Controller @Service @Repository @Component @Configuration
分别对应:控制器, 服务,仓库,组件,配置文件
2. 方法注解:@Bean
1.2.1 类注解的使用
这里使用@Controller注解进行演示,其他四个类注解的使用方式都是一样的.
@Controller // 将对象存储到 Spring 中
public class UserController {
public void sayHi(String name) {
System.out.println("Hi," + name);
}
}
此时我们先使⽤之前读取对象的⽅式来读取上⾯的 UserController 对象,如下代码所示:
public class Application {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =new ClassPathXmlApplicationContext("springconfig.xml");
// 2.得到 bean
UserController userController = context.getBean("userController", UserController.class);
// 3.调⽤ bean ⽅法
userController.sayHi("lisi");
}
}
1.2.2 为什么会有这么多类注解
既然功能是⼀样的,为什么需要这么多的类注解呢?
这和为什么每个省/市都有⾃⼰的⻋牌号是⼀样的?⽐如陕⻄的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,⼀样。为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类的⽤途.
- @Controller:表示的是业务逻辑层;
- @Servie:服务层;
- @Repository:持久层;
- @Configuration:配置层。
1.2.3 类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:
其实这些注解里面都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“子类”。
1.3 Bean对象的命名规则
当我们使用applicationContext进行获取上下文,然后通过上下文进行获取容器中的对象的时候,我们常用的就是传入两个参数:
参数一: Bean对象类名首字母小写
参数二: Bean对象的类对象.
我们平常使用的命名规则是大驼峰
如果我们⾸字⺟和第⼆个字⺟都是⼤写时,我们在使用上述的方法进行获取类对象的时候就不会获取成功.
这个时候,我们就要查询 Spring 关于 bean 存储时⽣成的命名规则了, 我们可以在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容:
它使⽤的是 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);
}
所以就是遇到首字母和第二个字母都是大写的时候,我们获取对象的时候,名字是不变的.
1.4 方法注解@Bean
类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的,如以下代码的实现:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
这里需要注意的是,使用方法注解一定要配合方法注解.(因为一个项目类已经是很多了,每个类的方法也很多,所以整个项目的方法就会特别多,所以我们就需要配和类注解进行使用)
方法注解,可以将返回的对象的名字进行修改,我们在使用方法的时候,可能会返回许多类型相同的对象,所以我们可以将这些返回的对象进行修改名字,以便在以后获取的时候更加的方便.代码如下:
@Component
public class Users {
@Bean(name = {"u1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
// 可以起多个名字
@Bean(name = {"u1", "us1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
// 省略name的写法
@Bean({"u1", "us1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
}
获取方法返回的对象
class App {
public static void main(String[] args) {
// 1.得到 spring 上下⽂
ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
User user = context.getBean("u1", User.class);
// 3.调⽤ bean ⽅法
System.out.println(user);
}
}
思考:使⽤ @Bean 注解并重命名,尝试使⽤原来的类名⾸字⺟⼩写是否能正确获取到对象?
答案:当给@Bean 设置了name属性之后,使用原方法名就获取不到对象了,只能使用别名进行获取.(起了小名,大名就不能用了(挺离谱的,但是要遵循规则))
2. 获取 Bean 对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:
- 1. 属性注入
- 2. 构造方法注入
- 3. Setter 注入
接下来,我们分别来看。
2.1 属性注入
属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中。Service 类的实现代码如下:
@Service
public class StudentService {
public void sayHi(){
System.out.println("hi");
}
}
将StudentService 对象注入到Controller中
@Controller
public class StudentController {
// 1.使用属性注入的方式获取Bean对象
@Autowired
private StudentService studentService; // 自动将标有Service注解studentService对象进行注入
public void sayHi(){
// 调用获取Bean对象Service的方法进行使用
studentService.sayHi();
}
}
测试属性注入
public static void main4(String[] args) {
// 1.测试属性注入
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
StudentController studentController = context.getBean("studentController",StudentController.class);
studentController.sayHi();
}
2.2 构造方法注入
@Controller
public class StudentController3 {
private StudentService studentService;
@Autowired
// 当有一个构造方法的时候可以将@Autowired省略
public StudentController3(StudentService studentService){
this.studentService = studentService;
}
public void sayHi(){
// 调用获取Bean对象Service的方法进行使用
studentService.sayHi();
}
}
注意事项:如果只有一个构造方法,那么可以将@Autowired进行省略.
但是如果类中有多个构造⽅法,那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法,否则程序会报错.
2.3 Setter注入
可以为注入的对象进行添加set和get方法,然后给set方法添加@AutoWired注解
@Controller
public class StudentController2 {
private StudentService studentService;
public void sayHi(){
// 调用获取Bean对象Service的方法进行使用
studentService.sayHi();
}
// 给私有成员变量设置Setter方法,并且加上@Autowired注解
@Autowired
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
}
2.4 三种注入方式的优点与缺点
优点 | 缺点 | |
属性注入 | 使用便捷 | 1.功能性:不能注入一个final修饰的对象 2.通用性:只适用于IOC容器中 3.设计原则: 容易违背单一设计原则(使用简单,犯错率大) |
构造方法注入 | 相对更符合单一设计原则 | 1. 不能注入不可变对象 2. 注入的对象随时可以被改变 |
setter方法注入 | 使用相对复杂 | 1.可注入不可变对象 2.注入的对象不会被修改 3.注入的对象可完全被初识化 4.通用性更好 |
2.5 @Resource 另一种注入关键字
在进⾏类注⼊时,除了可以使用@Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示:
@Controller
public class StudentController4 {
@Resource(name = "student1")
private Student student;
}
@Autowired 和 @Resource 的区别
- 出身不同:@Autowired 来自于Spring,而@Resource 来自于JDK 的注解;
- 使用时设置的参数不同:相比于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如name 设置,根据名称获取 Bean。
- @Autowired 可⽤于 Setter 注入、构造函数注⼊和属性注入,而@Resource 只能用于Setter 注入和属性注入,不能⽤于构造函数注入。
- 获取的Bean对象的顺序不同:autowired先根据名字在根据类型,Resource先根据类型再根据名字进行获取。
2.6 同⼀类型多个 @Bean 解决
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
@Bean
public User user2() {
User user = new User();
user.setId(2);
user.setName("MySQL");
return user;
}
}
@Controller
public class UserController4 {
// 注⼊
@Resource
private User user;
public User getUser() {
return user;
}
}
报错的原因是,⾮唯⼀的 Bean 对象。
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
- 使⽤ @Resource(name="user1") 定义。
- 使⽤ @Qualifier 注解定义名称。
① 使⽤ @Resource(name="XXX")
@Controller
public class UserController4 {
// 注⼊
@Resource(name = "user1")
private User user;
public User getUser() {
return user;
}
}
② 使⽤ @Qualifier
@Controller
public class UserController4 {
// 注⼊
@AutoWired
@Qualifier(value = "user2")
private User user;
public User getUser() {
return user;
}
}