美文网首页
Spring应用中组件的作用

Spring应用中组件的作用

作者: sansansan233 | 来源:发表于2020-10-11 14:11 被阅读0次


很多文章和书籍都介绍了spring框架以及spring框架的使用,讲的很多的都是spring框架里的IOC容器,DI自动注入,以及切面编程。在最初的使用过程中,始终对于这些概念模模糊糊,感觉好像用到了,又不知道是在哪里。使用spring的xml配置的时候,可能感觉更清晰一些,而spring boot使用注解的配置更加感觉满眼都是黑魔法,因此想基于web编程,简单说明一下为什么要使用IOC容器,DI自动注入以及切面编程。

网络应用,可以简单来看从最初的时候,我们怎么一步步需要各种各样的协议,框架,语言,描述方法等等。在最初的时候,并没有Http协议等等,只有基于流的TCP协议。这时候我们编写网络应用的时候,可以看作如下流程:

    1、服务端打开套接字,监听某个端口(服务端:new ServerSocket(),配置ServerSocket,bind,listen,最终阻塞在accept上)

    2、客户端发起链接(客户端:new Socket(),connect(serverHost,serverPort),然后客户端写入内容

    3、服务端accept返回client的写入通道(linux系统对应client文件描述符),从里面读取出客户端的内容,并分析,计算,得出结果,最终写回。

一个简单的网络应用,我们可以在第三步读取客户端的内容,并不对其进行处理,写回一个”hello,world“就好了。这时候网络应用并没有什么特别的作用。或者将”hello,world“替换成其他文本:”欢迎访问本网站“等等。

后来出现了静态的内容,客户端写入一个文章标题,一个文件名,服务端将上文中的“hello,world”,替换成服务端本地的文件就好了。这时候,客户端就在黑框框里显示了一连串的文字。

但问题也随之出现,显示效果很差,于是,希望服务端能够标明,文件的格式,比如有标题,有字号等等,客户端可以随之将对应内容显示到对应地方,于是,大家加入了Html的各种标签,比如<head><body><title>表示显示的位置,<h1><h2><b>等表示显示的字体。由于服务端将这些标签写入代码中的字符串,也很别扭,比如 print("<head><title>hello,world</title></head>"),于是就将静态文件,以.html文件显示,后来又出现了css来将样式更加美化等等。

这时候网络web服务就可以显示一些单页的公告等等。于是在想是否可以出现一些更多的文本,比如一本书,可以带上索引,而不用一个一个文件的请求,只用点击一下就好了,于是html里面有了<a>标签,进行链接跳转,每点击一下,客户端自动请求<a>标签里的链接表明的地址,这样,静态的内容文件就略微动了起来。

但因为网络等等的原因,可能会出现各种各样的错误。于是,就出现了Http协议,表明每次的请求的状态,如最常见的,200,404,400等等。前端网页也可以添加一些动作,比如隐藏文本,鼠标滑过显示文本等等,就有了简单的JS。本文的重点不在前端,可以前端到现在暂时告一段落。

但此时网络应用只是展示一些内容。并不能根据不同的用户,提供不同的服务。比如我们现在有一个电话簿。那么任何人只要知道我们的网站地址,都可以看到电话簿,看到的也都是相同的电话簿。这个就有点儿像某个机构的联系方式页面:科室1:tel 123,科室2:tel 456,等等。对于一些私密的个人电话,仅允许个人,就不好在这里展示。于是我们就有了用户登录的需求,甚至更进一步,可以做出个人电话簿,某某某的电话簿---联系人1:123,联系人2:456 。

对于这样的一个电话簿的应用来说,就需要用户进行登录。

1、用户输入应用地址,服务端读取静态文件返回,客户端展示登陆页面

2、前端填写了用户的账户密码,点击登录按钮,客户端发起对服务端的请求

3、服务端根据请求的内容,解析出用户的账户密码,查询数据库,返回客户端结果

4、客户端根据结果进行展示。

从这里可以看出,大家登陆页面都是一样的,只是之后的内容不一样。对于不需要修改的地方,不会因为用户不同而展示不同的东西,我们称之为静态,所以,在服务端就是静态服务内容,根据用户不同展示不同的内容的,可以简单称为动态内容,动态应用。以后可以根据这样的进行划分。(可以查询更详细的定义,这里仅仅简单说明)。简单来说,重点就是第三步,动态应用生成动态内容。这时候我们的spring就起作用了。

简单的拆分第三步,可以看作这样:userInfo=Decode(String clientRequest),生成一个POJO的UserInfo对象,login(userInfo.UserName,userInfo.UserPassword),在login中,建立对数据库的链接,发送查询语句如:“select * from usertable where username=XXX”,然后拿到结果生成一个UserInfo userInfoInDb,然后判断密码是否正确。甚至对于上文中所说的电话簿来说,两步合一步,如果密码正确,再次查询电话簿数据库,根据userId,将对应联系人和电话信息都给查询出来。最后序列化成为一个json或xml,返回客户端,直接可以展示出登陆用户的联系人信息。

这时候就可以看到,我们需要对数据库建立两次链接,销毁两次链接。对于一个复杂的应用,太多地方需要查询数据库,每次都要进行连接,销毁链接,不仅代码编写的时候非常的麻烦,容易出错。而且修改的地方只有sql语句内容,如上文那个select语句。非常的繁琐,一个不熟悉数据库链接的程序员搞起来也非常费劲,不如专人写好一个接口,封装成一个对象,使用的时候调用就好了。这样,就出现了数据库连接对象,由于用的地方多,有时候关联性不大,可以并发操作,于是就使用池化来进行操作。

这时候,一个数据库连接对象池,在我们的应用中,只需要一个,所有的查询任务都可以丢给这个链接池。但简单来讲,我们可以在这里新建一个,在那里新建一个,甚至不知道程序走到这儿的时候是否新建了一个,并且这个对象的创立可能也需要一定固定的步骤。此时,我们就可以使用常用的设计模式中的--工厂模式,抽象工厂,单例模式等等,来对我们的代码进行改造。

当然,这只是一个方面,仅仅是数据库查询,后面的时候会有各种各样的应用,使用各种各样的方面的对象,也要频繁的创建等等,就需要有一个总领性的东西,容纳他们,我们每次使用的时候就从里面拿取就好了,这时候,我们就可以使用到了Spring的IOC。一个小的ioc容器我们就可以简单地看成是一个map,我们可以从里面,根据某种key获取我们想要的对象。比如根据名字,根据对象,等等等等。

这时候,我们的对象还都是简单的一个个对象,并不彼此发生关联,我们需要自己编写代码,将他们联系起来,最简单的比如说常用的mvc模式。

service.setJdbcTemate(ioc.getByName("jdbctemapte"));

controler.setService(ioc.getByName("service"));

还是需要一堆胶水代码,将一个对象,注入到另一个对象里面。每次都要编写这么一堆,非常的费劲,甚至一些专业的地方,还需要专门分析。于是,spring给出了下一个非常好用的工具,就是自动注入,我们可以告诉框架,哪个地方需要什么对象,需要根据什么方法(名字?类型?等)从容器中获取出对象,把它放入目的字段?这便是spring的自动注入,大神们帮我们准备好了这样的工具。我们只需要告诉工具,要怎么查找对象,比如xml配置里面,<bean>标签中某个字段的配置,甚至不用告诉工具,工具已经默认好了,比如@Autowired注解,按照类型注入,按照类名注入。

这样来说,我们的应用中各种各样的对象,已经按照某个方式已经组织起来了。各个使用的工具已经填好了他们所依赖的其他工具。可以等待用户的输入然后进行运行了。那么,又为什么要使用切面编程呢?

比如现在我们需要加入日志的功能。在某些方法上,打印一个“已经进入某方法:时间”,“已经退出某方法:时间”。很简单,只用加入一个日志对象,进入方法的时候调用一下,log.info或者log.debug等等,但很多地方都需要添加呢?这岂不是跑断腿了。

于是,我们可以考虑,反正所有的对象都已经是spring ioc容器生成的。那何不在容器生成对象的时候动动手脚,正好java提供了反射的功能。于是可以把调用的方法,看成一个点,在进入之前,我们先拐一下(横刀夺爱,咔嚓切一刀),拐到我们的日志上,在调用之后,再拐回调用本来的方法,结束后,再拐到我们日志上。最后,再返回。那么,在生成对象的时候,需要spring框架保留下bean的信息以及这两次日志的信息。就可以使用代理模式,结合JDK代理或者cglib代理来做一个代理对象的代理方法,代理方法内就可以{log.info("entry method");method.invoke(target,args);log.info("out method");}。来实现。这就是切面的意义。

所以,综上所述,三大块各有各的应用。一个简单的spring boot+spring mvc的实现流程就可以理解为如下:

    1、点击启动应用,应用进入SpringApplication.run(Application.class,args),进入spring框架处理过程中

    2、Spring可以通过Application.class的Class对象,获取到包的位置,Class的注解信息,如果我们没有填写扫描的包的名字(scan注解),那么就会根据默认的包位置进行扫描,获取文件名即类名。所以我们经常把Application放在最外层,其他的controller等等放在各个包下面,也有这一方面的原因。

    3、获取到了Class信息,就可以通过ClassLoader来进行类的加载,和生成类的对象。由于我们配置了,或者引入了切面的配置,所以,spring会根据我们配置的切面的方法(正则表达式,SPEL表达式),找到对应的方法,在生成的时候,生成类的代理对象,使用的时候就可以根据代理来进行调用。

    4、当对象生成结束后,会根据我们配置好的注入地点,注入方式。将我们所期望的对应的字段填好。这样,spring的工作基本就完成了。

可以简单的看成下面进入到spring mvc的工作时候的流程:

    1、启动,根据spring上述工作完成对象的生成和对象之间的关联

    2、监听某个端口等待用户请求的进入

    3、用户请求进入,从ioc容器中获取解析工具,根据协议解析用户的请求内容

    4、根据用户的请求内容,从ioc容器中获取用户期望调用的方法,以及调用对象(如所写的@RestController,@GettingMap,里的路径,和标配的http方法,getpost等)

    5、通过调用代理对象的方法,此时进入到配置的切面,根据配置进行调用(环绕,前置,后置,异常。)

    6、进入target对象的方法进行处理(我们所写的controller个各种方法等等),进行数据库查询呀,逻辑调用等等。然后返回

    7、又进入到了切面里,进行后置操作,如json的序列化等等工序。

    8、最终生成一堆字符串,返回给客户端。

总之,本文可能写的比较杂乱,也没有对应的配图,有对应的流程图会显示的更加详细。只想简单梳理一下一部分组件的定位。

相关文章

网友评论

      本文标题:Spring应用中组件的作用

      本文链接:https://www.haomeiwen.com/subject/nnwbxttx.html