1.分层解耦-三层架构
上节课案例中,全部都写在一个类empController中,而在设计和开发的时候需要尽量让每一个接口、类或者是方法它的职责更加单一,只管一块儿功能,即单一职责原则,才会使可读性更强、复杂度更低、拓展性更好以及利于后期维护,基于此才有了三层架构。
controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
service:业务逻辑层,处理具体的业务逻辑。
dao:数据访问层(Data Access Object持久层),负责数据访问操作,包括数据的增删改查。
过程:前端发起请求后,先会到达controller,controller接收到请求后要去调用service进行逻辑处理;逻辑处理的前提是先拿到数据,此时service层先去调用dao层,dao层再去操作文件当中的数据;数据拿到之后再将数据返回给service,service再进行逻辑处理并将结果返回给controller,controller再响应数据给前端。
(总之就是controller->service->dao->数据文件->dao->service->controller->前端)
面向接口的方式进行编程:(灵活切换各种实现)
dao:
创建包dao,编写接口EmpDao:
public interface EmpDao {
//获取员工列表数据
public List<Emp> listEmp();
}
在dao下创建包impl,编写类EmpDaoA:
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);
//引用工具类当中的一个方法parse方法解析xml文件,file代表要解析哪一份文件,targetClass表示解析的xml文件要往哪个对象里去封装
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
return empList;
}
}
service:
创建包service,编写接口EmpService:
public interface EmpService {
//获取员工列表
public List<Emp> listEmp();
}
在service下创建包impl,编写类EmpServiceA:
public class EmpServiceA implements EmpService {
//1.创建dao层对象,接收dao层的数据
private EmpDao empDao = new EmpDaoA();
@Override
public List<Emp> listEmp() {
//2.调用dao获取数据
List<Emp> empList = empDao.listEmp();
//3.对数据进行转换处理gender job
//基于stream流遍历一下,拿到里面的每一个元素
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;
}
}
controller:
编写EmpController:
@RestController
public class EmpController {
//1.创建service对象,接收service处理过的数据
private EmpServiceA empServiceA = new EmpServiceA();
@RequestMapping("/listEmp")
public Result list(){
//2.调用service获取数据
List<Emp> empList = empServiceA.listEmp();
//3.响应数据(统一结果)
return Result.success(empList);
//传递了响应回去的数据经过@Responsebody注解处理就会转换成json响应到客户端
}
}
2.分层解耦-分层解耦和IOC-DI
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合
如何解耦?控制反转和依赖注入。
Bean对象:IOC容器中创建、管理的对象。
控制反转(IOC):对象的创建控制权由程序自身转移到外部容器。
service层和dao层的实现类,交给IOC容器管理:
在EmpDao和EmpService中方法前添加@component注解,表示将当前类交给IOC容器管理,成为IOC容器中的bean。
依赖注入(DI):容器为应用程序提供运行时,所依赖的资源。
为Controller及service注入运行时依赖的对象:
1)在EmpController和EmpService的成员变量前添加@Autowired注解表示运行时,IOC容器会提供该类型的bean对象,并赋值给该变量,即依赖注入。
形象来说就是有一个池子,@Component将当前类添加进去,@Autowired表示使用池子里的类对象,这个池子就叫IOC容器,类对象叫做bean对象.
*如果要切换实现类,比如想要实现EmpServiceB,只需将A的@Component注释掉,在B中添加@Component注释即可。
3.分层解耦-IOC详解
bean的声明:
@Component包括三个衍生注解:@Controller(标注在控制器类上*在springboot集成web开发中,声明控制器bean只能用@Controller),@Service(标注在业务类上),@Repository(标注在数据访问类上,由于与mybatis整合,用的少);不属于三类时用@Component.
即上述EmpDaoA中可以改为@Repository,EmpServiceA中可以改为@Service,而EmpController中不必添加,因为@RestController中包含两个注解,@ResponseBody和@Controller.
*如果要修改bean对象的名字,在注解后添加(value = “xxx”)即可,默认是首字母小写的类名。
bean的组件扫描:
四大注解要想生效,还需要被组件扫描注解@ComponentScan(没有显示配置)扫描,默认扫描的范围是启动类(com.example.xxxx)所在包及其子包。
4.分层解耦-DI详解
bean注入:
@Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean就会报错
解决:
1)在开头加上注解@primary表示优先选择哪一个bean
2)在@Autowired前加上注解@Qualifier("bean的名字")表示选择哪一个bean
3)使用@Resource(“bean的名字”)不使用@Autowired——区别:@Autowired是spring框架提供的注解,而@Resource是jdk提供的注解;@Autowired默认时按照类型注入,而@Resource默认是按照名称注入。
5.要求:读取xml文件并在页面中显示出来。
1)常规方式,controlller控制器不分层
先编写Pojo包下的实体类Writer类,成员变量名要与xml文件中一样:
package com.example.writeremp.Pojo;
public class Writer {
private String author;
private String gender;
private String dynasty;
private String title;
private String style;
//get set方法
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getDynasty() {
return dynasty;
}
public void setDynasty(String dynasty) {
this.dynasty = dynasty;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
//有参构造
public Writer(String author, String gender, String dynasty, String title, String style) {
this.author = author;
this.gender = gender;
this.dynasty = dynasty;
this.title = title;
this.style = style;
}
//无参构造
public Writer() {
}
}
修改工具类XmlParseUtils,导入Result类:
package com.example.writeremp.Utils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
public class XmlParserUtils {
public static <T> List<T> parse(String file , Class<T> targetClass) {
ArrayList<T> list = new ArrayList<T>(); //封装解析出来的数据
try {
//1.获取一个解析器对象
SAXReader saxReader = new SAXReader();
//2.利用解析器把xml文件加载到内存中,并返回一个文档对象
Document document = saxReader.read(new File(file));
//3.获取到根标签
Element rootElement = document.getRootElement();
//4.通过根标签来获取 user 标签
List<Element> elements = rootElement.elements("writer");
//5.遍历集合,得到每一个 user 标签
for (Element element : elements) {
//获取 author 属性
String author = element.element("author").getText();
//获取 gender 属性
String gender = element.element("gender").getText();
//获取 dynasty 属性
String dynasty = element.element("dynasty").getText();
//获取 title 属性
String title = element.element("title").getText();
//获取 style 属性
String style = element.element("style").getText();
//组装数据
Constructor<T> constructor = targetClass.getDeclaredConstructor(String.class, String.class, String.class, String.class,String.class);
constructor.setAccessible(true);
T object = constructor.newInstance(author, gender, dynasty,title,style);
list.add(object);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
再编写writer.html,利用插值表达式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>诗人信息</title>
</head>
<link rel="stylesheet" href="element-ui/index.css">
<script src="./js/vue.js"></script>
<script src="./element-ui/index.js"></script>
<script src="./js/axios-0.18.0.js"></script>
<body>
<h1 align="center">诗人信息列表展示</h1>
<div id="app">
<table border="1" cellspacing="0" width="100%">
<tr align="center" >
<td>编号</td>
<td>姓名</td>
<td>性别</td>
<td>朝代</td>
<td>头衔</td>
<td>风格</td>
</tr>
<tr v-for="(writer,index) in tableData" align="center">
<td>{{index+1}}</td>
<td>{{writer.author}}</td>
<td>{{writer.gender}}</td>
<td>{{writer.dynasty}}</td>
<td>{{writer.title}}</td>
<td>{{writer.style}}</td>
</tr>
</table>
</div>
</body>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
new Vue({
el: "#app",
data() {
return {
tableData: []
}
},
mounted(){//vue的钩子方法发起异步请求请求数据
axios.get('/listWriter').then(result =>{
if(result.data.code){//回调函数中判断返回code的值
this.tableData = result.data.data;
}
});
},
methods: {
}
});
</script>
</html>
最后编写controller包下的WriterController:
package com.example.writeremp.Controller;
import com.example.writeremp.Pojo.Result;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class WriterController {
@RequestMapping("/listWriter")
public Result list(){
String file = this.getClass().getClassLoader().getResource("writer.xml").getFile();
System.out.println(file);
//1.引用工具类当中的一个方法parse方法解析xml文件,file代表要解析哪一份文件,targetClass表示解析的xml文件要往哪个对象里去封装
List<Writer> writerList = XmlParserUtils.parse(file, Writer.class);
//2.对数据进行转换处理gender job
//基于stream流遍历一下,拿到里面的每一个元素
writerList.stream().forEach(writer -> {
String gender = writer.getGender();
if("1".equals(gender)){
writer.setGender("男");
}else if("2".equals(gender)){
writer.setGender("女");
}
});
//3.响应数据(统一结果)
return Result.success(writerList);
//传递了响应回去的数据经过@Responsebody注解处理就会转换成json响应到客户端
}
}
在浏览器中输入/writer.html运行:
2)按照MVC的分层方式实现,常规java代码方式
创建两个包,Dao和Service;
在包下分别创建两个接口WriterDao和WriterService,编写抽象方法listWriter():
package com.example.writeremp.Dao;
import com.example.writeremp.Pojo.Writer;
import java.util.List;
public interface WriterDao {
public List<Writer> listWriter();
}
package com.example.writeremp.Service;
import java.util.List;
import com.example.writeremp.Pojo.Writer;
public interface WriterService {
public List<Writer> listWriter();
}
在Dao和Service包下分别再创建包impl,创建WriterDaoA和WriterServiceA实现类,并实现接口,重写接口中的抽象方法listWriter()来分别处理数据和逻辑(WriterServiceA中要先创建Dao对象,并使用对象的listWriter()方法接收dao处理后的数据):
package com.example.writeremp.Dao.impl;
import com.example.writeremp.Dao.WriterDao;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Utils.XmlParserUtils;
import java.util.List;
public class WriterDaoA implements WriterDao {
@Override
public List<Writer> listWriter() {
//数据层,处理数据
String file = this.getClass().getClassLoader().getResource("writer.xml").getFile();
System.out.println(file);
List<Writer> writerList = XmlParserUtils.parse(file, Writer.class);
return writerList;
}
}
package com.example.writeremp.Service.impl;
import com.example.writeremp.Dao.WriterDao;
import com.example.writeremp.Dao.impl.WriterDaoA;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Service.WriterService;
import java.util.List;
public class WriterServiceA implements WriterService {
//创建Dao对象
private WriterDao writerDao = new WriterDaoA();
@Override
public List<Writer> listWriter() {
//逻辑层,处理数据的逻辑
List<Writer> writerList = writerDao.listWriter();
writerList.stream().forEach(writer -> {
String gender = writer.getGender();
if("1".equals(gender)){
writer.setGender("男");
}else if("2".equals(gender)){
writer.setGender("女");
}
});
return writerList;
}
}
最后在WriterController中修改代码:
package com.example.writeremp.Controller;
import com.example.writeremp.Pojo.Result;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Service.impl.WriterServiceA;
import com.example.writeremp.Utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class WriterController {
//创建service对象
private WriterServiceA writerServiceA = new WriterServiceA();
@RequestMapping("/listWriter")
public Result list(){
/* String file = this.getClass().getClassLoader().getResource("writer.xml").getFile();
System.out.println(file);
//1.引用工具类当中的一个方法parse方法解析xml文件,file代表要解析哪一份文件,targetClass表示解析的xml文件要往哪个对象里去封装
List<Writer> writerList = XmlParserUtils.parse(file, Writer.class);
//2.对数据进行转换处理gender job
//基于stream流遍历一下,拿到里面的每一个元素
writerList.stream().forEach(writer -> {
String gender = writer.getGender();
if("1".equals(gender)){
writer.setGender("男");
}else if("2".equals(gender)){
writer.setGender("女");
}
});*/
//响应数据(统一结果)
List<Writer> writerList = writerServiceA.listWriter();
return Result.success(writerList);
}
}
实现:
3)采用控制反转和依赖注入的MVC方式实现。
首先在WriterDaoA中添加注解@Component表示将当前类交给IOC容器管理,成为IOC容器中的bean。
package com.example.writeremp.Dao.impl;
import com.example.writeremp.Dao.WriterDao;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Utils.XmlParserUtils;
import org.springframework.stereotype.Component;
import java.util.List;
@Component//将当前类交给IOC容器管理,成为IOC容器中的bean
public class WriterDaoA implements WriterDao {
@Override
public List<Writer> listWriter() {
//数据层,处理数据
String file = this.getClass().getClassLoader().getResource("writer.xml").getFile();
System.out.println(file);
List<Writer> writerList = XmlParserUtils.parse(file, Writer.class);
return writerList;
}
}
在WriterServiceA中添加@Component将当前类交给IOC容器管理同时,使用注解@Autowired读取IOC容器中的bean对象,并赋值给该变量,即依赖注入。
package com.example.writeremp.Service.impl;
import com.example.writeremp.Dao.WriterDao;
import com.example.writeremp.Dao.impl.WriterDaoA;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Service.WriterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class WriterServiceA implements WriterService {
//使用注解@Autowired读取IOC容器中的对象WriterDao
@Autowired
private WriterDao writerDao;
@Override
public List<Writer> listWriter() {
//逻辑层,处理数据的逻辑
List<Writer> writerList = writerDao.listWriter();
writerList.stream().forEach(writer -> {
String gender = writer.getGender();
if("1".equals(gender)){
writer.setGender("男");
}else if("2".equals(gender)){
writer.setGender("女");
}
});
return writerList;
}
}
最后在WriterController中也使用注解@Autowired读取IOC容器中的bean对象WriterService。
package com.example.writeremp.Controller;
import com.example.writeremp.Pojo.Result;
import com.example.writeremp.Pojo.Writer;
import com.example.writeremp.Service.WriterService;
import com.example.writeremp.Service.impl.WriterServiceA;
import com.example.writeremp.Utils.XmlParserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class WriterController {
//使用注解@Autowired读取IOC容器中的对象WriterService
@Autowired
private WriterService writerService;
@RequestMapping("/listWriter")
public Result list(){
/* String file = this.getClass().getClassLoader().getResource("writer.xml").getFile();
System.out.println(file);
//1.引用工具类当中的一个方法parse方法解析xml文件,file代表要解析哪一份文件,targetClass表示解析的xml文件要往哪个对象里去封装
List<Writer> writerList = XmlParserUtils.parse(file, Writer.class);
//2.对数据进行转换处理gender job
//基于stream流遍历一下,拿到里面的每一个元素
writerList.stream().forEach(writer -> {
String gender = writer.getGender();
if("1".equals(gender)){
writer.setGender("男");
}else if("2".equals(gender)){
writer.setGender("女");
}
});*/
//响应数据(统一结果)
List<Writer> writerList = writerService.listWriter();
return Result.success(writerList);
}
}
实现:
或者将WriterDaoA中的@Component注解改为@Repository,把WriterServiceA中的@Component注解改为@Service,WriterController中不必添加。