上一篇的请求和响应案例中,我们会发现案例中:解析XML数据,获取数据的代码,处理数据的逻辑的代码,给页面响应的代码全部都堆积在一起了,全部都写在controller方法中了。
整个工程代码的复用性比较差,而且代码难以维护,那么就需要分层开发。
一个类或一个方法,就只做一件事情,只管一块功能,这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利用后期的维护。
接收前端发送的请求,对请求进行处理,并响应数据
@RestController
public class EmpController {
//面向接口EmpService创建EmpService类型的业务层对象
private EmpService empService = new EmpServiceA();
@RequestMapping("/listEmp")
public Result list(){
//1. 调用service层对象, 获取数据
List<Emp> empList = empService.listEmp();
//3. 响应数据
return Result.success(empList);
}
}
处理具体的业务逻辑
//业务逻辑接口(制定业务标准)
public interface EmpService {
//获取员工列表
public List<Emp> listEmp();
}
//业务逻辑实现类(按照业务标准实现)
public class EmpServiceA implements EmpService {
//面向接口EmpDao创建EmpDao类型的dao层对象
private EmpDao empDao = new EmpDaoA();
@Override
public List<Emp> listEmp() {
//1. 调用dao对象, 获取数据
List<Emp> empList = empDao.listEmp();
//2. 对数据进行转换处理 - gender, job
//empList集合的数据来自dao层,因此需要在service中调用dao
empList.stream().forEach(emp -> {
//处理 gender 1: 男, 2: 女
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}else if("2".equals(gender)){
emp.setGender("女");
}
//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}else if("2".equals(job)){
emp.setJob("班主任");
}else if("3".equals(job)){
emp.setJob("就业指导");
}
});
return empList;
}
}
负责数据的访问操作,包含数据的增、删、改、查
//数据访问层接口(制定标准)
public interface EmpDao {
//获取员工列表数据
public List<Emp> listEmp();
}
//数据访问实现类
public class EmpDaoA implements EmpDao {
@Override
public List<Emp> listEmp() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合,目的是使程序模块的可重用性、移植性大大增强。。
高内聚指的是:一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 “高内聚”。
程序中高内聚的体现:EmpServiceA类中只编写了和员工相关的逻辑处理代码
低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。
程序中耦合代码的体现:把业务类变为EmpServiceB时,需要修改controller层中的代码
之前我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦合了,当service层的实现变了之后, 我们还需要修改controller层的代码。
那应该怎么解耦呢?
Inversion Of Control,简称IOC。就是将bean对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象,这种思想称为控制反转。
对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器
Dependency Injection,简称DI。IOC容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
程序运行时需要某个资源,此时容器就为其提供这个资源。
例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象
由IOC容器中创建和管理的对象
完成Controller层、Service层、Dao层的代码解耦
解耦思路:
第1步:在Controller层、Service层的实现类中删除new对象的代码。
第2步:在Controller层、Service层的实现类中添加Spring提供的注解:@Autowired ,程序运行时,IOC容器自动为Controller程序注入依赖的Service层对象,自动为Service程序注入依赖的Dao层对象。
第3步:在Service层、dao层的实现类中添加Spring提供的注解:@Component ,将Service层及Dao层的实现类交给IOC容器管理
完整代码
删除Controller层、Service层中new对象的代码,使用Spring提供的注解:@Component和 @Autowired,除了@Component注解,也可以使用@Component的衍生注解,可以更好的标识web应用程序开发当中,bean对象到底归属于哪一层。
Controller层
第一种写法,使用 @RestController注解
@RestController //@RestController = @Controller + @ResponseBody
public class EmpController {
@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
private EmpService empService ;
@RequestMapping("/listEmp")
public Result list(){
//1. 调用service, 获取数据
List<Emp> empList = empService.listEmp();
//3. 响应数据
return Result.success(empList);
}
}
第二种写法,使用 @Controller注解
@Controller
public class EmpController {
@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
private EmpService empService ;
@RequestMapping("/listEmp")
public Result list(){
//1. 调用service, 获取数据
List<Emp> empList = empService.listEmp();
//3. 响应数据
return Result.success(empList);
}
}
Service层
第一种写法,使用 @Component注解
@Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class EmpServiceA implements EmpService {
@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
private EmpDao empDao ;
@Override
public List<Emp> listEmp() {
//1. 调用dao, 获取数据
List<Emp> empList = empDao.listEmp();
//2. 对数据进行转换处理 - gender, job
empList.stream().forEach(emp -> {
//处理 gender 1: 男, 2: 女
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}else if("2".equals(gender)){
emp.setGender("女");
}
//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}else if("2".equals(job)){
emp.setJob("班主任");
}else if("3".equals(job)){
emp.setJob("就业指导");
}
});
return empList;
}
}
第二种写法,使用 @Service注解
@Service
public class EmpServiceA implements EmpService {
@Autowired //运行时,从IOC容器中获取该类型对象,赋值给该变量
private EmpDao empDao ;
@Override
public List<Emp> listEmp() {
//1. 调用dao, 获取数据
List<Emp> empList = empDao.listEmp();
//2. 对数据进行转换处理 - gender, job
empList.stream().forEach(emp -> {
//处理 gender 1: 男, 2: 女
String gender = emp.getGender();
if("1".equals(gender)){
emp.setGender("男");
}else if("2".equals(gender)){
emp.setGender("女");
}
//处理job - 1: 讲师, 2: 班主任 , 3: 就业指导
String job = emp.getJob();
if("1".equals(job)){
emp.setJob("讲师");
}else if("2".equals(job)){
emp.setJob("班主任");
}else if("3".equals(job)){
emp.setJob("就业指导");
}
});
return empList;
}
}
Dao层
第一种写法,使用 @Component注解
@Component //将当前对象交给IOC容器管理,成为IOC容器的bean
public class EmpDaoA implements EmpDao {
@Override
public List<Emp> listEmp() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}
第二种写法,使用 @Repository注解
@Repository
public class EmpDaoA implements EmpDao {
@Override
public List<Emp> listEmp() {
//1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}