Java的三层架构是一种常见的软件架构设计模式,用于将应用程序的不同功能和责任分配到不同的层级,以提高可维护性、可扩展性和可重用性。
它可以包括以下三个层级:
1.表现层:也称为用户界面层,负责与用户进行交互,并将用户的请求发送到其他层进行处理。
2.业务逻辑层:也称为服务层或控制层,包含应用程序的核心业务逻辑。它接收来自表现层的请求,处理数据逻辑、业务规则和算法,并协调多个数据访问的操作。在Java中,可以使用POJO(Plain Old Java Object)或Spring框架的服务层来实现业务逻辑层。
3.数据访问层:也称为持久层或数据层,负责处理数据的持久化和访问。它与底层数据库进行交互,执行数据的读取、写入和更新操作。在Java中,可以使用JDBC(Java Database Connectivity)或ORM(Object-Relational Mapping)框架(如Hibernate)来实现数据访问层。
使用三层架构的优点
明确的职责划分、降低模块之间的耦合度、提高代码的可重用性和可测试性。此外,它还为不同层级的开发人员提供了分工合作的便利,并促进了代码的维护和扩展。
下面我们来进一步了解一下三层架构在代码中的样子
大致过程为:
浏览器发送请求,控制端(Controller)接收请求处理,并做出响应发送给服务端(Service),服务端进行数据的逻辑处理后发送给数据层(Dao),数据层在数据库中查找数据后返回服务层,服务层接收数据后发送给控制层,控制层再返回给浏览器,供客户端阅读。
各层代码如下:
//Controller
@RestController
public class UserController {
private UserServiceA userService = new UserServiceA();
@RequestMapping("/listUser")
public Result listUser() throws Exception {
// 调用service, 获取数据
List<User> userList = userService.listUser();
// 响应数据
return Result.success(userList);
}
}
@RestController:标记类注解,结合了@Controller 和 @ResponseBody注解的功能
@Controller:用于标记一个类,表示这个类是一个控制器,用于处理用户的请求和响应
@ResponseBody:用于标记一个方法,表示该方法返回的结果将被直接写入HTTP响应体中,而不是被解析为视图名称
@RestController:使用 @RestController 注解标记的类,可以简化返回 JSON 或 XML 数据的操作,因为它会自动将方法返回的对象转换为相应的格式,并写入响应体中。这样,我们就不需要在每个方法上再添加 @ResponseBody 注解了。
//Service
public class UserServiceA {
private UserDaoA userDao = new UserDaoA();
public List<User> listUser() throws Exception {
//调用 dao 层, 查询数据
List<User> userList = userDao.listUser();
//2. 对数据进行逻辑处理 - 对地址 address 中的 province 属性后, 添加上 "省/市"
for (User user : userList) {
Address address = user.getAddress();
address.setProvince(address.getProvince()+" 省/市");
address.setCity(address.getCity()+" 市/区");
user.setAddress(address);
}
return userList;
}
}
//Dao
public class UserDaoA {
public List<User> listUser() throws Exception {
//1. 从文件中查询数据
String file = UserController.class.getClassLoader().getResource("user.xml").getFile();
List<User> userList = XmlParserUtils.parse(file);
System.out.println(userList);
return userList;
}
}
1.软件设计的原则是: 高内聚低耦合
2.内聚即为: 软件中各个模块的内部功能联系
高内聚的理解: 模块元素中的各个元素之间联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高
3.耦合即为 : 各个软件之间的依赖、关联程度
低耦合的理解:软件中各个层、模块之间的依赖关系程度越低越好
4.高内聚低耦合的目的:使程序模块的可重用性、移植性大大增强
耦合的解释
当我们在开发时进行加载XML数据,当我们在使用三层架构对原始的代码进行了编写时,controller调用service,service调用dao。在controller中调用service,我们需要在controller中new一个service对象,那么此时controller的代码就耦合了service。
在service中调用dao,我们就需要在service中new一个到对象,那么此时service的代码就耦合了dao。
代码如下
具体代码的实现
//UserService接口
public interface UserService {
public List<User> listUser();
}
//UserServiceImpl
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoA();
public List<User> listUser() {
//调用 dao 层, 查询数据
List<User> userList = userDao.listUser();
//2. 对数据进行逻辑处理
for (User user : userList) {
Address address = user.getAddress();
address.setProvince(address.getProvince()+" 省/市A");
address.setCity(address.getCity()+" 区/县A");
user.setAddress(address);
}
return userList;
}
}
这里调用dao层的作用是什么呢?
为了更好的实现解耦
当我们将创建的实例对象,都存储在一个容器中,如果需要这个对象,就不用再去单独new一个对象了,直接在运行时通过容器提供就可以了。
这里就涉及到了两个过程:
(1)控制反转 (IOC):将对象交给容器管理的过程
(2)依赖注入(DI):应用程序运行时,容器为其提供运行时所需的资源
(3)bean:IOC容器中创建、管理的对象
@Component
public class UserServiceA implements UserService {
@Autowired
private UserDao userDao ;
public List<User> listUser() {
//调用 dao 层, 查询数据
List<User> userList = userDao.listUser();
//2. 对数据进行逻辑处理
for (User user : userList) {
Address address = user.getAddress();
address.setProvince(address.getProvince()+" 省/市A");
address.setCity(address.getCity()+" 区/县A");
user.setAddress(address);
}
return userList;
}
}
在成员变量上加上 @Autowird 注解,表示在程序运行时,Springboot会自动的从IOC容器中找到UserService类型的bean对象,然后赋值给该变量
代替了查找数据的代码:
@Component
public class UserDaoA implements UserDao {
public List<User> listUser() {
//1. 从文件中查询数据
String file = UserController.class.getClassLoader().getResource("user.xml").getFile();
List<User> userList = XmlParserUtils.parse(file);
System.out.println(userList);
return userList;
}
}
@Component 的注解的衍生
可以使用value对其进行赋值
依赖注入
使用 @Autowird ,默认是按照类型进行,如果存在 多个相同类型的bean,就会报错
有以下解决方法
1) @Primary 注解
2)@Qualifier 注解
通过@Autowired,配合@Qualifier 来指定我们当前要注入哪一个bean对象。在@Qualifier的value属性中,指定注入的bean的名称
3)通过@Resource注解,并指定其name属性,通过name指定要注入的bean的名称。这种方式呢,是按照bean的名称进行注入。
@Autowird与@Resource的区别