IoC 2‍|学习笔记

简介: 快速学习IoC 2‍

开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术IoC 2‍】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/76/detail/15770


IoC 2‍


内容介绍

一、@Bean 的用法

二、spring 的依赖注入

三、WEB 后端-SPRING SECURITY SAMPLES


一、@Bean 的用法

‍‍@Bean 是一个方法级别上的注解,‍‍主要用在 @Configuration 注解的类里,也可以用在 @Component 注解的类里。添加的 bean 的 id 为方法名 ‍。

如下图,‍‍所以在 Configuration 这个类里面可以去写 bean,‍‍就在表示刚才所说的含义。

图片5.png

‍‍‍‍‍‍除了用‍‍构造器之外,‍‍还可以用‍‍ set 方法。如果用 setUtimateAnswer,‍‍‍‍配置文件里面它的写法就会‍‍改变。‍‍不是‍‍构造器,写的是‍‍ property,最后作用还是一样。‍‍‍

图片6.png

‍总的来说,用户要做的事情就是有很多东西不想再硬编码到代码里头去,将通过配置文件的方式把这些知识给它设置进去,我在代码里可能不要去跟更多东西直接产生依赖,所以说用户靠这种配置文件或者annotation 的方式,去告诉这个 spring 来进行托管,然后相应的配置了一些属性或者配进去,那刚说了如果一些参数确实要经常变化,或者你在不同的地方,‍‍你想复用这个代码,但是‍‍在不同的地方‍‍关于一些‍‍参数它设置的值有差异,‍‍那就要把它变成从外部‍注入进去,不能在编码里硬编码写死,所以这是所展示的例子。

图片7.png

造器可以做注入,也可以用 set 方法,提供一些 set方法,然后用那种属性的方式注入,两种方式都比较好,都可以用。Spring 这个团队倡导用 setter 这种注入的方式,理由是如果你有很多东西都要通过这种注入的方式去设置,像刚刚看到的 yearhotmetter,有很多的完全放在构造器里面,这个构造器的参数就非常多,构造器就不好维护,所以用 setter 方法,但是用 setter 这个方法,在有些人看来是不好的,因为用 setter 就意味着对象能改,对象能改这件事情在很多的编程人眼里是不好的事情,意思就是说函数从数学上来的,y=f(x) 本质上来说,无论给 f 传递一个什么样的 x 值,x 值本身不发生变化。从这个道理来讲,面向对象的编程里面不应该有 set 方法,只有构造器,改变值时不是通过假设 x·setYear(50),而应该是 newBean(50)或 x=x·getyear+50 已有对象不会进行修改,只能产生新的对象,应该在构造器里面进行依赖注入设置,而不是用 set。归根结底不应有 set 方法,每次改写时要创建新对象。

图片8.png


二、Spring 的依赖注入

接下来在图片所示代码里如何运用依赖注入。首先看到反复刷一个分层结构的概念,依赖注入在这个系统里面如何体现?

正常逻辑请求来的时候最先到达 controller,controller 在处理之后转发给 service,然后 service 是做的接口和实线分离,(有些人习惯于把接口的包放到 service 包里面,就里面还有一个包放线具体如何组织都可以)。Service 到 dao 这一层然后 dao 这一层也是分了接口实线的分离。

接着再到 repository,最后操作到了实体 entity 层。现在可以看到在每一层里面都有接口,那所有的代码全部针对着接口编程的话,那就意味着你的代码里从来没出现过这种实线类的名字

图片9.png

代码如下,从最底层的 person 实体类来说

图片30.png

Repository 中扩展的 JpaRepository 定义了许多方法,所以写一个接口,接口 spring 拿到后就会去实现它,如果什么都没加,就会把这些方法针对着<Person,Integer>这些具体的类和主镜的类型去实现出相应的方法。

在 java 里面的泛型和在 C++ 里面谈到的模板类类似,只是细节上存在一些差异。

那如果在里面写了一些方法,然后 Queoy去定义它的查找逻辑,那就会把这一部分真的方法也给实现了,因为只是标注在一个方法上,它具体的实现类是根据所写的 Queoy 去实现。

这个实现类不是控制 spring 帮助去实现的,只写了一个接口,没有提供任何的实现,用 panocationon 标注一下,具体是靠 spring 实现的,所以记住这个接口。

图片11.png

然后 dao 这一层,现在做的比较简单,所以可以定义一些向上暴露的接口如 findQne,但是也就仅此而已。这个接口就定义成如图,最后返回一个人。

图片12.png

然后现在就是出现了刚才讲到的注入的新事项,dao 的实现类如何去实现?

Repository、service、controller 都是 component 的一种,还有用 spring 会进行托管,它里面可以看到了一个成员类型,成员变量 PersonRepository,那就是刚才定义的一个接口类型。

并没有去强调实现类,只是有一个这样的借口,然后这个接口只是有一个变量,声明一下类型。@Autowired,Spring 读到之后帮助生成 PersonRepository 的对象赋给它,生成的逻辑就是与 dao 里的接口名字最接近的,Spring 帮找到了生成的类。然后在底部 PersonRepository 上调用方法已提供的 getOne 方法。@Override 的书写不影响代码,是有助于编译器去发现所书写的一些问题。另外可以看到红箭头这里完成了一次注入,PersonRepository 这个对象本身不是直接创建的,是靠 spring 创建后注入进来的,可以去使用它。Dao 实现类里面如果未来将 JpaRepository 换为 My......实现类,代码不会动,直接红箭头处注入新换的实现类。

图片13.png

到了 service 这一层,是在使用 dao 上的东西,同样做接口与实现分离,有一个接口 PersonService,有一个实现 PersonServicelmpl。这个实现类里可以看到,有一个对象 PersonDao,Spring 同样会看到这个接口类型,@Autowired 就意味着希望 spring帮自动找到它的实现类,然后创建对象注入。如果这个实现类要想替换一个 dao 的实现类,代码本身不需要动,因为从来没有说跟哪个实是绑定的,只要拿掉被替换的实现类,换一个新的进去,或者改写里面代码,此操作对这一层不产生任何影响

图片14.png有了 service 之后,再来看 controller。首先前面用的是 @RestController同样也是 component 的一种,只针对 service 的接口编程,还没有说是 PersonServicelmpl,只在 service 上去调用。

回顾之前的结构,controller 只针对 service 接口编程,独立于 service 具体的实现类,如果要更新 service 具体的实现类对它是不产生任何影响。Service 的实现类里是针对 dao 接口编程,dao 的实现类是针对 repository 接口编程,所以每一层都做到了接口和实现的分离。

然后所有的都是在对着接口编程,他们的对象都是靠注入进来,这就是图片所示的三个红箭头的来历。

图片15.png

通过上述例子,简单直观展示如何去用 spring 依赖注入。编写后台时要把握两个原则,第一个是分层,第二个就是要接口和实现分离写任何一个代码的时候,都应该考虑的一种实践方案

分层架构应用在很多的系统里,分层的好处是代码比较容易管理,另外就是看到代码之后,如 controller 只能看到 serviceservice 之下东西一概不关心,或者不用调用Service 同样的道理只能看到 dao,dao 之下的东西不用管

换言之每一层向上屏蔽掉了底下所有的复杂性,每一层都是如此。另外在分层的架构里面,每一层屏蔽掉它之下的所有层的这种差异,然后再看每一层只向上暴露接口给上面去用,只向下调用它下面这一层的接,并不会越过上下层去提供接口,意味每一层只跟上下两层交互,所以这一层如果接口都不动,可以随意替换,如果要动也只影响上面一层或者下面一层,不会影响更多的层。但分层的结构意味着系统当中执行效率不会很高,必须按这种分层架构去调用。另外分层架构不能完全避免有一个请求修改不去修,不会影响到更多的层,只会影响本层或上下一层。

比如:在 person 里面想修改内容,数据库里多了一个字段 column,entity 可能会发生变化,repository 对象变动要重新操作,dao 层针对新加的一层会增加 find 或其他动作。

以此类推这种操作可能还会影响到其他层,未必只影响到一层,这是带来的潜在的问题

为什么会反反复复强调分层架构因为只要是写软件,不但是写 web 软件写任何一个软件都会用到一个招数就是解决不了问题就加一层,加一层解决不了再加一层,任何一个软件都是如此。所谓加一层的目的就是要在中间做转换,做一些处理,举例学院分成三个不同的方向,有系统软件,操作系统软本身就是个分层,系统软件里为什么要在一个 Linux 的机器上面去跑一个 docer 虚拟机里面,里面跑一个 windows,因为希望用到 windows 操作系统,但是现在的机器是一个 Linux 机器为了满足这个要求就在操作系统和用户的应用之间插入了像容器这样的一层关系,或者虚拟器,靠它去模拟出来一个 windows 的环境,用户使用它的时候,Windows 之 docer 就屏蔽掉了底下的差异底下其实是一个 Linux,所以任何一个软件在编程的时候只要解决不了就可以多加一层,用这一层再做一些相应的转换就可以解决。

我们这个运用一样,如果觉得有问题,想做一些处理,就可以在中间再加一层。比如看到很多网上讲 repository 就是 dao,为什么还有 dao,原因是因为数据一部分来自于关系型数据库,还有一部分来自于 db。没

办法把它俩直接汇聚到一起,又希望最终在给 service 看到的时候是一个完整的 person,而不是分离开的两个对象,所以就加一层。这一层来自于关系型数据库和非关系型数据库的数据给他汇聚到一起,构成一个完整的 person。

然后在上层 service 看到的就是一个完整 person。Person 到底是来自于哪个数据?或者到底来自于几个数据按键,就可以到这一层完全给封掉?所以说只要是我们这个专业写代码的时候一定记住当你解决不了的时候加一层,靠这一层来帮你解决问题。而接口和实现分离为什么很重要?

我们也在反复强调大一时候如果学程序设计,就是为什么要定义接口,因为接口稳定。两个同学之间合作来开发一个系统,为什么大家要先谈我们把接口设计好。就是因为接口设计好之后,它就稳定可以不动,然后我们各自去提供它的实现,我们的代码就能合到一起。所以我们一定要做接口,然后把它暴露出去给别人去用。接口是优先的,我们是针对接口编程。接口和实现去做分离,它的意义就在于接口是稳定的,实现是不稳定的,那么实现在未来就会不断地发生修改,我要把实现发生修改的这个影响降到最低,我只能通过接口和实现分离来得到。

就像我们现在看到的后面代码,所有的代码全部是针对接口编程,不针对实现类编程,未来到底实现类是哪一个注入进来,然后就把接口和实现分离开,这样的代码就容易维护。

一样的道理,这个东西不是在 wive 里面才有,用其他的系统是一样的。所有的这种根本的东西必须要通过多门课程,然后学科里面一些比较精髓的思想从头贯穿到尾逐渐的掌握。所以这两个思想是到目前为止,在我们这个后台的开发里面体现的最多的。写代码整个的过程还体现了一个很重要的原则就是不断的抽象,然后逐步细化。所以一开始打出一个框架,然后不断的往里面塞东西,越来越细化,快速原型、持续迭代、不断求精,这是我们讲软件开发的一个过程。然后这里面讲的分层,接口和实现分离是编程的程序设计的一个原则。框架不断地发生变化,之后可能会发现有新版本出来,可能有些结构会发生变化,但是对你来说并不会很难,这些背后体现的意思是我们要去反复去体会掌握设计出想要的东西,所以在验收大作业的时候不仅要看写的代码功能,还要看代码是不是遵循一些原则,只有代码维护了才会对这个系统不断演化保持一种可能性,如果代码写的不好,你就很难去处理。

举例:安卓是个什么样的系统?安卓是一个宏内核。宏内核有什么好处?

宏内核的好处是速速度比较快,但是现在变得非常的“臃肿”,安卓装到一个手机里时代码太多,所以要解决这个问题要开发 miurokernd,谷歌新开发的 fushia,miurokernd 只是一个小的内核装上去,其他所有东西全部像插件一样插上来。宏内核是没有办法以这种方式来控制它,整个就是一大块,里面有很多模块,他们互相之间直接就产生了依赖。微内核是它们互相之间都产生依赖,它们都跟中间的这个交互,靠它来做消息的转发。好处就是只要不需要就可以不装,系统能跑起来就可以。宏内核之间依赖性强,不容易演化,但是速度快直接进行交互。

微内核容易演化,但效率会低。所以在微内核的基础上还要速度快,两个指标都要达到有点矛盾,真正能够做到就很厉害。代码之间的耦合度一定要低,接口与实现分离就是在降低耦合度,每一层都跟它下一层的接口进行交互,降低对下一层实现的耦合依赖,所以系统将来在做演化的时候,才有可能不断的演化,系统越来越丰富,并且保证一定的性能。这些需要大家在编码中,在实际过程当中去理解。有关 IoC 如果你要想用的更好,要去看一下 spring 官方文档会更复杂,内容会非常的多。

 

三、WEB 后端-SPRING SECURITY SAMPLES

Spring 里面有很多的工具,有一个叫 spring security,大家在做用户验证的时候,比如说他是一个管理员还是一个普通用户,可以用它来做,这样就意味着你需要和 react 进行集成。老师写了一个例子放到网上,可以去 covas 里下载,但发现其中有个问题还不知如何解决,讲到那里再做解释。

首先,spring security 是什么?spring security 就是想控制前后端的交互的时候的一个权限问题。

这个体系比较复杂,所以以举例的方式来讲解,用户未必必须采用,如果有其他的方案比如说最简单的把用户名、密码存到数据库里,让用户传入用户密码到后台比对就知道它是一个普通用户还是一个管理员,然后对它进行控制。

今天所讲的是利用 spring security 来做,不详细讲题型,举官方提供的例子并加以解释。第一,所有的配置信息放到一个类里要加 configuration,那么 spring就会去加载,知道如何来帮你把相关的东西托管,那这里要做安全管理 Config 文件是集成自 WebMvcConfigurer。

下面 addViewControllers 对于前端的请求,如果相对于当前的工程,是访问 “/home” 看到哪个页面,如果直接是 “/” 看到哪个页面,“/hello”看到 “hello” 页面,“/login” 看到 “login” 页面。类似于做了一个路由。

图片16.png

第二,要做一个有关安全方面的控制,要实现 WebSecurityConfigurerAdapter 类。然后把 WebSecurity 给 Enable 一下。这是一个配置文件,所以要写 @Configuration 没必要写到一个类里,有关跨越的写到跨越里,有关安全控制写到安全控制里,有关路由写到路由里,刚开始自定义的 bean 可以写到 bean 的 Configuration 文件里。如图,这个类里有个 configure 方法要去覆盖,当匹配到 “/”“/home” 对于这种请求要做认证,跳转到 “/login” 页面进行验证。红色括号中的详细细节要进行约束。

图片17.png

认证的时候怎么认证?底下覆盖 userDetailsService 方法,这个方法认证的时候需要用户名是 “user”,密码是 “password”,如果用户的用户名密码跟匹配,就给他一个角色 “USER”,当然这俩东西从哪儿来,如果写的复杂的话,可以说它来自于数据库,是通过 springJPA 抓取回来得到它的值,返回 InMemoryUserDetailsManager(user) 结果,键是要把这个组装出来User 这个信息就是包含了用户名、密码、角色信息传递给它。

图片18.png

第三,应用前面有个页面这个页面有一个超链接,点击超链接发出一个请求 “hello”,然后 controller 再处理吧。第二个 “hello” 页面有个登出按钮登出按钮一旦点了,它就会对着 /logout 的位置发一个请求出去。底下是 login 页面当前登录回来的那个结果是错误还是要登出,内容会不一样。之下有文本输入用户和密码 type=“text” type=“password” 密码为星号或黑点。点击 submit 会产生 login 的动作请求出去

图片19.png

效果如图,超链接发出 hello 到了 hello 页面,进入之前输入用户名和密码,输入不对会跳到左下角的页面,如果点击 sign out 会跳转到 You have been logged out 页面。

图片20.png

三个页面代码如下

首先 home 页面的超链接,点击超链接发出 hello 的动作

图片21.png

第二,跳到 hello 页面上,然后看对 hello 的处理。这些页面做安全控制,跳到 login 页面。

图片22.png

图片23.png

路由如下

图片24.png

不会用静态页面,主要用 React+Spring Security 这种方式来跑,所以试着写了一个第四节课写 react 路由的例子,上面有 home、about、users 三个页面,三个链接设置的路由就是让他们跳到什么地方去。

图片25.png

动作在做了一个相应处理,在 Info.is 这个页面里加入内容,要向后台http://loccalhost:8080/发东西,前端属性里点的是 home、about、users 中的哪个这个信息要了解到,也就是 props.menu 里的信息要拿到。把用户名和密码放到 headers 里,用 basic 的方式认证要有用户名和密码,将用户名和密码放进去转换成 base64 的形式(base64是一种编码机制)设置到头里。

然后 fatch 一下对着 url get。按道理用户名和密码就应该发到了后端,那后端在处理时如果用户名和密码不对是不允许登录的。响应回来后可以看到有可能是对的也有可能是错的,如果是对的就把内容取出来显示到底部的 welcome 这个位置。

图片26.png

后端如何写?如图,上方是 SpringBoot 的代码,然后下方配置文件里把用户名和密码写进去,SpringSecurity 的用户名是 root,密码是123。

图片27.png

然后写 GreetingController,如果是 about 就返回“This is a Spring security sample”,如果是users 就返回 “I am a user”,如是 / 就返回“Let start!”。然后允许跨域访问,因为前后两个工程。

图片28.png

后面有关 Configuration 安全的配置,按照网上的例子写了一堆,然后按照之前的代码跑了一下,但有个问题是如果之前的用户名和密码写错是不起作用的。还没有找到正确的解决方法,先展示一下代码跑起来的页面。

图片29.png

相关文章
|
3月前
|
XML Java 数据库
Ioc原理
Ioc原理
47 0
|
4月前
|
设计模式 Java 容器
控制反转 (IoC)
【8月更文挑战第24天】
44 0
|
JavaScript uml 容器
Ioc——控制反转
Ioc——控制反转
213 0
Ioc——控制反转
|
XML Java 关系型数据库
IoC 1|学习笔记
快速学习IoC 1
100 0
IoC 1|学习笔记
|
运维 Java vr&ar
控制反转|学习笔记
快速学习控制反转
104 0
控制反转|学习笔记
|
Java API C++
IOC理解
成功就是简单道理的深刻理解与灵活运用 前不久,阿里大牛虾总在群里抛出一个问题:“从深层次讲解一下如何理解IOC,IOC和DI是一回事吗?” 这个问题真是让人平静而又不平静 平静源于此问题就像问中国人怎么使用筷子,天天使用筷子,难道还不会使用筷子? 但又不平静,你能写出一份详细的说明书,让一个不会使用筷子的人按此说明成功地使用上筷子吗?
357 0
IOC理解
|
Oracle 架构师 Java
什么是IOC
什么是IOC
279 0
什么是IOC
|
XML 存储 Java
|
XML JavaScript Java
阅读Spring源码:IOC控制反转前的处理
阅读Spring源码:IOC控制反转前的处理
132 0
阅读Spring源码:IOC控制反转前的处理
|
Java 容器 Spring
Spring入门知识 (一) IOC 控制反转
Spring入门知识 (一) IOC 控制反转
96 0
Spring入门知识 (一) IOC 控制反转