三层架构
如下图,创建Dao的接口以及该接口的实现类,Service也一样
Dao
// Dao接口
public interface UserDao {public List<String> findAll();
}// Dao接口的实现
public class UserDaoImpl implements UserDao {// 加载用户数据@Overridepublic List<String> findAll() {InputStream in = this.getClass().getClassLoader().getResourceAsStream("user.txt");ArrayList<String> lines = IoUtil.readLines(in, StandardCharsets.UTF_8, new ArrayList<>());return lines;}
}
Service
// UserService 接口
public interface UserService {public List<User> findAll();
}// UserService 接口实现类
public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl(); // usage@Overridepublic List<User> findAll() {
// 1. 调用dao,获取数据List<String> lines = userDao.findAll();// 2. 解析用户信息,转换为User对象 -> list集合List<User> userList = lines.stream().map(line -> {String[] parts = line.split( ",");Integer id = Integer.parseInt(parts[0]);String username = parts[1];String password = parts[2];String name = parts[3];Integer age = Integer.parseInt(parts[4]);LocalDateTime updateTime = LocalDateTime.parse(parts[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return new User(id, username, password, name, age, updateTime);}).toList();return userList;}
}
Controller
import java.util.List;@RestController
public class UserController {private UserService userService = new UserServiceImpl(); // 1 usage@GetMapping("/book")public List<User> list(){// 1. 调用service,获取数据List<User> userList = userService.findAll();// 2. 返回数据(json)return userList;}
}
分层解耦
实现分层解耦的思路是什么?
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
Bean对象: IOC容器中创建、管理的对象,称之为Bean。
-
将项目中的类交给IOC容器管理(IOC,控制反转)
-
应用程序运行时需要什么对象,直接依赖容器为其提供(DI,依赖注入)
IOC容器管理对象
注解 | 说明 | 位置 |
---|---|---|
@Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
@Controller | @Component 的衍生注解(内部封装了@Component ) | 标注在控制层类上 |
@Service | @Component 的衍生注解(内部封装了@Component ) | 标注在业务层类上 |
@Repository | @Component 的衍生注解(内部封装了@Component ) | 标注在数据访问层类上(由于与mybatis整合,用的少) |
@Repository("UserDao") // 定义Bean名称,但通常不会定义
依赖注入
属性注入(适用于快速开发)
@RestController
public class UserController { @Autowired private UserService userService; //......
}
- 优点:代码简洁,方便快速开发。
- 缺点:隐藏了类之间的依赖关系,可能会破坏类的封装性。
构造函数注入(对规范性要求高)
@RestController
public class UserController { private final UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; }
}
- 优点:能够清晰地看到类的依赖关系,提高了代码的安全性。
- 缺点:代码繁琐,如果构造参数过多,可能会导致构造函数臃肿。
- 注意:如果只有一个构造函数,
@Autowired
注解可以省略。
setter注入
@RestController
public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; }
}
- 优点:保持了类的封装性,依赖关系更清晰。
- 缺点:需要额外编写setter方法,增加了代码量。
多个类型相同的Bean注入
- @Autowired注解,默认是按照类型进行注入的。
- 如果存在多个相同类型的bean,将会报出如下错误:
Field userService in com.itheima.controller.UserController required a single bean, but 2 were found: - userServiceImpl1: defined in file [D:\idea_workspace_web_ai\web-project1\springboot-web-quickstart\target\classes\com\itheima\service\impl\UserServiceImpl.class] - userServiceImpl2: defined in file [D:\idea_workspace_web_ai\web-project1\springboot-web-quickstart\target\classes\com\itheima\service\impl\UserServiceImpl2.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
以下为一些解决方法
- 方案一:@Primary(
@Primary
注解,你可以清晰地定义在存在多个候选 Bean 时,哪一个应该是默认的注入对象,从而避免潜在的冲突和不确定性):就是说在使用@Autowired时注入的是标了@Primary的Bean码@Primary @Service public class UserServiceImpl implements UserService {@Overridepublic List<User> list(){//省略......} }
-
方案二:@Qualifier
@RestController public class UserController {@Autowired@Qualifier("userServiceImpl1")private UserService userService; }
-
方案三:@Resource
@RestController public class UserController {@Resource(name = "userServiceImpl1")private UserService userService; }