一、概念
@Autowired默认按类型装配,通过@Qualifier注解指定bean的名称也可以做到按名称装配。
@Resource默认按名称装配,通常使用name属性制定bean的名称,当找不到与名称匹配的bean时,才会按类型装配。
@RequiredArgsConstructor 是Lombok中的一个注解,可以简化@Autowired注解,在类上使用这个注解的时候,需要注入的bean用final定义。
二、环境搭建
1.引入依赖
<!--lomobk-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
完整的pom文件如下所示
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>MySpringBoot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>MySpringBoot</name>
<description>MySpringBoot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lomobk-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.创建实体类
package com.example.myspringboot.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 测试实体
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Long id;
private String name;
}
3.创建服务层接口及两个实现类
package com.example.myspringboot.service;
import com.example.myspringboot.bean.Student;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 服务层
*/
public interface StudentService {
List<Student> getAll();
}
package com.example.myspringboot.service;
import com.example.myspringboot.bean.Student;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 服务层实现类1
*/
@Service
public class StudentServiceImpl1 implements StudentService {
@Override
public List<Student> getAll() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(1L, "aa"));
studentList.add(new Student(2L, "bb"));
return studentList;
}
}
package com.example.myspringboot.service;
import com.example.myspringboot.bean.Student;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 服务层实现类2
*/
@Service
public class StudentServiceImpl2 implements StudentService {
@Override
public List<Student> getAll() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(3L, "cc"));
studentList.add(new Student(4L, "dd"));
return studentList;
}
}
三、测试@Autowired
测试控制层示例代码如下:
package com.example.myspringboot.controller;
import com.example.myspringboot.bean.Student;
import com.example.myspringboot.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 测试控制器
*/
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/")
public List<Student> getAll() {
return studentService.getAll();
}
}
启动程序
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-07-03 11:29:10.271 ERROR 10976 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field studentService in com.example.myspringboot.controller.StudentController required a single bean, but 2 were found:
- studentServiceImpl1: defined in file [D:\javaLearn\MySpringBoot\target\classes\com\example\myspringboot\service\StudentServiceImpl1.class]
- studentServiceImpl2: defined in file [D:\javaLearn\MySpringBoot\target\classes\com\example\myspringboot\service\StudentServiceImpl2.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Process finished with exit code 1
我们看到了报错的信息,意思是服务接口有2个实现类,因为@Autowired是按类型装配的,现在就不知道该注入哪一个了。
解决方法1:
直接注入指定的类名
package com.example.myspringboot.controller;
import com.example.myspringboot.bean.Student;
import com.example.myspringboot.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 测试控制器
*/
@RestController
public class StudentController {
/**
* 指定注入StudentServiceImpl1
*/
@Autowired
private StudentService studentServiceImpl1;
@GetMapping("/")
public List<Student> getAll() {
return studentServiceImpl1.getAll();
}
}
在浏览器访问测试地址,测试成功
解决方法2:
添加@Qualifier注解的value属性指定注入的实现类
package com.example.myspringboot.controller;
import com.example.myspringboot.bean.Student;
import com.example.myspringboot.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 测试控制器
*/
@RestController
public class StudentController {
// @Qualifier指定注入StudentServiceImpl2
@Autowired
@Qualifier(value = "studentServiceImpl2")
private StudentService studentService;
@GetMapping("/")
public List<Student> getAll() {
return studentService.getAll();
}
}
继续在浏览器访问测试地址,测试成功
四、测试@Resource
我们在控制层使用@Resource注解并使用name属性指定要注入的实现类
package com.example.myspringboot.controller;
import com.example.myspringboot.bean.Student;
import com.example.myspringboot.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 测试控制器
*/
@RestController
public class StudentController {
@Resource(name = "studentServiceImpl1")
private StudentService studentService;
@GetMapping("/")
public List<Student> getAll() {
return studentService.getAll();
}
}
在浏览器访问测试地址,测试成功
五、测试@RequiredArgsConstructor
1.再创建一个服务层UserService
package com.example.myspringboot.service;
import org.springframework.stereotype.Service;
/**
* @author qx
* @date 2023/7/3
* @des 用户实现类
*/
@Service
public class UserService {
public void list() {
System.out.println("用户列表");
}
}
2.修改测试控制层
我们在类上加上@RequiredArgsConstructor注解,并在注入的bean使用final修饰
package com.example.myspringboot.controller;
import com.example.myspringboot.bean.Student;
import com.example.myspringboot.service.StudentService;
import com.example.myspringboot.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author qx
* @date 2023/7/3
* @des 测试控制器
*/
@RestController
@RequiredArgsConstructor
public class StudentController {
private final StudentService studentServiceImpl2;
private final UserService userService;
@GetMapping("/")
public List<Student> getAll() {
// 用户列表
userService.list();
return studentServiceImpl2.getAll();
}
}
启动浏览器测试,分别获取到了StudentService服务和UserService服务中功能。
我们就可以不使用属性注入(@Autowired
和@Resource
)的方式,直接通过构造器的方式来完成注入,不仅能够省略简化许多代码,也解决了属性注入可能存在的空指针问题。