请求与响应
上一章我们做了一个简单的程序,当我们在浏览器的地址栏中输入一个访问路径,就能访问我们部署在tomcat中的应用程序,后端接收到请求后,进行处理,再把处理完毕的结果传回给浏览器,浏览器响应对应的结果。
而浏览器在和数据库唧唧我我的时候,是基于HTTP协议的。 这个HTTP协议就规定了两个人唧唧我我的规范,或者说规则。
上一章我们写的controller程序,tomcat其实是不认的,这个程序他没有实现接口,也没有继承类。 但是有解决办法的。他不认我们写的controller即这样的:
@RestController
public class JoneyController{
@RequestMapping("/joney");
public String hiJoney(String name){
sout(name);
return "hi";
}
}
解决办法:他认的是Java的规范技术,即servlet,也叫servlet容器。
springboot的底层给我们提供了一个核心的程序,即DispatcherServlet。DispatcherServlet有一个复杂的集成体系。 servlet就是继承与他。
由浏览器发起的请求都会先到DispatcherServlet的手上过一遍,再从他开始,分到下面的子类。
在springboot中,我们成DispatcherServlet为核心控制器或者前端控制器。
下面介绍一下响应和请求对象:
请求对象:HttpServletRequest
响应对象:HttpServletResponse
其作用就和他们的名字一样。
网络架构
原谅我如此突兀给个名词大家,这个仅当做了解。
B/S架构和 C/S架构
BS架构就是浏览器,客户端架构,应用程序所有逻辑和数据存在服务器中,用户只需要浏览器就能获取服务。
CS架构就是用户要安装一个应用才能获取服务。即要下载客户端。
ApiPost
前面说了,我们主流开发都是前后端分开进行开发的。而我们开发的后端要测试一下接口行不行得通,到时候给前端对接时能不能顺利发送数据过去,该怎么办? 我们的前端还在开发,我们前后端期望的是,像建设桥梁一般,从河岸的两端同时建设,最后在河的中间进行没有差错的拼接。
这时候就要用到后端的接口测试工具了,我这里使用的是ApiPost。大家在官网下载最新版本就好了,他的操作是比较简单的。下面我带大家简单的走一遍使用流程。
1.
先建立一个controller包,放下我们的RequestController,里面写上如图所示的代码。
解释一下部分代码的含义:
@RequestMapping 指定controller的访问路径
开启运行springboot后,去到Apipost操作。
返回数据Ok。
控制台返回前端发过来的数据。
不单单是传两个参数,他还能传对象,时间,json格式的数据
还有一个比较特殊的,路径参数。
这个参数刚好是controller映射的路径的一部分,就叫做路径参数。
这个路径是动态更新的,浏览器传什么参数过来,我们就给他跳到哪个controller。
所以跟别的参数有点不同。
Controller Service Dao 解耦
目前为止,我们所有的代码都写在了controller方法中。当然,我们的代码逻辑都是简单的。
但是如果我们要开发一个比较复杂的功能,那就会出现很多问题,举个例子。
如图所示这部分代码,他能从网页中获取请求,来到list()方法中进行处理。
首先去Resource目录下拿了emp.xml文件。 存入empList列表中。
然后对列表中的数据进行处理进行,详细地,我们能看到,对数据中的gender进行判断,如果是1则给对象的性别属性赋值为男,为2则赋值为女。
最后把数据返回给浏览器。
这三步操作在这时候是比较清晰的。那现在我们可能就会问了。
就算业务再复杂,不也就取数据,做处理,返回数据吗?
这会出现几个问题。
- 代码复用型差
- 拓展性差
我们到公司中去工作。公司追求的是高效率。前面的代码中,每次都要去取数据,以及最后的数据返回,非常明显都是每次操作都要的。如果我们能把两个操作做到通用化,即每次用到,我们只要调用一个函数就完事了,这就能增加工作效率。现实是,我们已经这么做了。
再者,在后面如果需要增加功能,在原来杂糅的代码基础上进行修改,需要先看懂这部分的代码,才能进行修改,不知道大家有没有改过自己面向过程的代码? 改起来真是久久难以平息……
所以就有了把这些东西分开做的想法,这就叫做分层。
分层解耦
分层,就是把web程序分为三层。分别是Controller控制层、service逻辑业务层、Dao数据访问层。
controller控制层 就是我们接收浏览器的请求,和最后的return 处理好的响应数据。
Dao数据访问层 谁来要数据,就给他想要的数据。
service逻辑处理层 这一层需要拿到数据才能做逻辑处理嘛,所以要先去到Dao层拿数据,数据return给service层,进行完毕逻辑处理,就返回给controller。
分层之后就是解耦。
解释之前要先知道两个概念,一个是內聚,一个是耦合。
內聚:是针对同一层的某一个对象的。 比如service层的empService,就只进行emp的逻辑操作,而dept的操作放到deptService中去。这就叫做类的內聚程度比较高。
耦合:是用来衡量各个层或者各个模块之间的依赖关联程度的。依赖程度越高,耦合越高。
比如,service层要调用dao才能获取数据进行逻辑处理吧?不然可要巧妇难为无米之炊了。
那controller层要调用service层才能获得逻辑处理完的数据进行返回给浏览器把?这两个例子就是各层之间的耦合情况。
我们软件设计,怎么样才能说一个软件设计得好呢? 就是要高內聚,低耦合。就是要尽量自给自足地搞内循环为主体,外循环为辅的政策,防止别人卡脖子。
好了。现在我们层与层之间就是有耦合,有什么办法呢?
遇到问题 记得拨打金牌导师 JonyB的电话。
下面将教给大家。
代码这里,如果我的Service要增加点功能,我把EmpServiceB的代码复制一份,写一个EmpServiceD的Service层,那么在这里,我是不是也要该这个controller层啊? 这就是耦合的存在。 那怎么该才能做到: 我改了Service层的东西,其他东西也不用动呢?
可能会有人这么改: 我删掉粉红色的部分,让他只要是EmpService的子类就给我用。
可以吗?
当然是不行的,首先遇到的问题就是报错,因为使用了empService对象而不给他赋值,他指向的就是空指针。
下面是官方的解决思路
首先,我准备一个容器,你不是说你有好几个EmpService的子类吗?我都把他们的实例对象放到这个容器中去。
当controller需要用到EmpService的对象时,去这个罐子里面找,找到就赋值给在controller声明的对象empService,就可以使用了。
这样做的好处是,可以不用管EmpService的子类具体哪个能用。你给我什么我就用什么。
大家发现了吗? 这样的话,我们就要多管理一个东西了,就是这个罐子(上面说的容器),一个EmpService的子类都放进去是可以的,但是取的时候我该给哪个?
这时候有两个思路,要么在EmpService的子类中表明,EmpService的儿子里面只能放一个,要么就是放了一堆子类对象进去,只让一个能用。
当然,官方认为的难点跟我认为的难点有不小的差距……毕竟人家才是真正要做实现的那群人。
官方认为哪些实现是难点呢?
- EmpService的子类对象怎么放进去这个容器?
- 容器里面的对象怎么给到Controller? 怎么给到它要的那个对象?毕竟罐子里面这么多对象呢,你怎么知道他要的是哪个?
面对1.他们搞了一个概念,依赖反转(inversion of control)IOC,乍一看莫名其妙,但他的地位不容小(xiao,第三声)觑,是spring框架的第一大核心。
他的含义是指:对象创建的控制权由应用程序转移到了外部容器。
原来在程序中,我要用什么对象,new就行了。 现在用了IOC,就是把new的权限给了罐子(容器)。解释就是这么短。 反转之前,程序自身控制对象的创建,反转之后,容器控制。
面对2 又搞了一个概念,叫依赖注入(dependency injection) DI ,
他的含义是指:容器为应用程序提供运行时所需要依赖的资源。
比如controller运行时需要依赖EmpService资源,就可以让容器给他这个资源,这个过程就是依赖注入。
bean对象:就是容器里面的对象。
在容器的外面,这些对象还归我们管的时候,叫做实例对象。不归我们管之后,由容器来管,叫做Bean对象。看到了吗?其实都是一个东西。只是谁管他们的区别。
大家会有疑惑,我一直说的容器,或者这个罐子(我容器和罐子混起来说了,只是为了好理解,容器就是罐子,罐子就是容器),应该有个名字吧? 毕竟容器里面的东西都有名字了。没错,当然有名字。这个容器,官方的名字叫做IOC容器。
进行实际的解耦操作
有两个步骤:
①.Service层及Dao层的实现类,交给IOC容器管理。
②.为Controller及Service注入运行时,依赖的对象。
①的实现:
我想把EmpServiceA这个类,他的new对象管理权交给IOC容器管理,该怎么做?
加上@Component 注解。
②的实现:
我的Controller需要IOC容器提供一个EmpService的对象,该怎么做?
在声明变量上面加上@Autowired 注解
更进一步的解释
首先是IOC。
上面说了,我们给IOC容器权限的时候,用的是Component注释,但我们看到别人的项目里面,有时候也不是Compoent注释啊?
这是因为Component注释是IOC容器管理的基本注解,当我们给Service层注解时,可以用@Service,一般也是用的@Service。 当我们给Controller层注解时,一般用的是@Controller。
Dao层用的是@Repository。
这些额外的注解都是Component的衍生注解,就是说他们的实现代码其实是带了@Component的。
如果你的包放在了整个结构的外面,会出问题。这是因为@ComPonentScan找不到你的这个包,具体大家可以去查一下怎么解决。 这里不详细说怎么改的原因是,官方提倡包都放同一个地方,如果放错了,放到对的地方就好了,不必去改这个属性。
最后说一下DI。
DI是什么大家还记得吗?就是去IOC容器拿取Bean对象的过程。
这个拿取过程上面也提到过。有两种情况,一是一个类的子类放了一堆进容器中,我叫容器给的时候,他只给指定的一个我。二是一个类只放了一个子类,那就是给我那个类咯。
这里的实现也是这样的。