原创内容,版权所有,转载请注明出处。
这个系列的开篇传送门: https://www.imooc.com/article/80510
在看这一篇之前,对于接口的理解还不是很透彻的小伙伴,可以先看这一篇: https://www.imooc.com/article/19697
对于我这种对服务器一无所知的小白来说,找一本合适的书应该是第一个要解决的问题,我的目的是通过学习Spring,来开发提供移动端使用的服务器,为移动端提供Restful API。又看了看目录,感觉《Spring实战(第四版)》比较符合我的预期,那么好吧,就选这本书。
很多书都有个问题,在我看来,前几章的内容简直把想要学习的热情转变为望而生畏,根本不是什么循序渐进。。哎,可能我基础太差,一点都不友好,只能硬着头皮看下去了。
在学习Spring这个框架前,书上提到了两个似乎很重要的概念,**一个是依赖注入(DI),一个是面向切面(AOP),**这两个概念就像两击重拳把我打蒙。网上解释可以说是五花八门,让我更加凌乱。我静下来,花了几个小时,似乎明白了点什么,也不知道对不对。
####我认为的依赖注入(DI),通俗起来就一句大白话:我是一个类,我需要和另一个类一起才能工作,请把另一个类通过参数的形式传递给我,通过我的构造方法传递给我也可以,通过我的其他公共方法传递给我也可以。
嗯,应该就是这个解释。那么这个依赖注入到底能干什么?为什么非要用?让我用一个栗子来说一下。讲到这里,我有些饿了,好想吃烧烤。各位都很爱吃烧烤吧,我呢,最爱吃烤鸡皮,鸡皮放在烤盘上,吱吱作响,刷上酱油,烤得外焦里嫩,夹起来,沾一点烧烤辣椒粉,入口,太美妙了。于是我们就有两个类了,鸡皮类(ChickenSkin)和烤盘类(BarbecueBakingTray),接下来让我们来烤鸡皮吧。
####准备鸡皮
/**
ChickenSkin.java
鸡皮类,只有一个放在火上的方法。
*/
public class ChickenSkin {
public void onFire() {
System.out.println("鸡皮在吱吱作响...");
}
}
####准备烤盘
/**
* 烤盘类
* BarbecueBakingTray.java
*/
public class BarbecueBakingTray {
// 声明一下我要烤鸡皮
private ChickenSkin chickenSkin;
// 拿出烤盘
public BarbecueBakingTray() {
// 烤盘产生鸡皮 -- (你没看错,我也没打错字)
chickenSkin = new ChickenSkin();
}
// 开始烤
public void startBBQ() {
chickenSkin.onFire();
}
}
####烤盘和鸡皮都就绪了,让我们开始烧烤吧
public class Demo {
public static void main(String[] args) {
// 拿出烤盘
BarbecueBakingTray barbecueBakingTray = new BarbecueBakingTray();
// 开始烧烤
barbecueBakingTray.startBBQ();
}
}
然后你会在控制台上看见以下文字输出:
#####“鸡皮在吱吱作响…”
我通常会写出这样的代码,没问题嘛,感觉不错,该做的都做了,鸡皮也好了,可以开始吃了。可是吃着吃着,我感觉哪不对劲。我们来看一看BarbecueBakingTray这个类里的构造方法,发现鸡皮居然是从这个方法里产生出来了,这给我一种比较恶心的感觉,鸡皮就这样凭空出现在烤盘上,这不合逻辑啊,这不禁让我不敢去想这个鸡皮的出处。一阵恶心,我放下了鸡皮,愤怒地把烤盘里的鸡皮丢在了地上,不行,我需要吃一点烤蔬菜解解我的恶心感。于是我拿出了韭菜和土豆片,再一次准备烧烤。这一次,我不允许土豆和韭菜凭空出现在我的烤盘上了。
那么现在,烤盘不能自己产生食物了,我放什么就烧烤什么,烤盘依赖食物,嗯,这似乎就是依赖注入(DI)了。一个烤盘上,我既要烧烤土豆,又要烧烤韭菜,也许一会还想再吃一些鸡皮,这三个都是食物,都可以用来烧烤,我只有一个烤盘,想要烤得随心所欲,我改进了我的烧烤方法。
####我先描述了这些食物的共性
/**
* 鸡皮类
* @author seokho
* BarbecueFood.java
*/
public interface BarbecueFood {
// 所有食物都可以拿来烤
void onFire();
}
####然后我准备了三样食物,他们都符合这个共性 (提醒一下,这几个类写在一起,但是他们都在不同的包里)
/**
* 鸡皮类
* @author seokho
* ChickenSkin.java
*/
public class ChickenSkin implements BarbecueFood {
@Override
public void onFire() {
// TODO Auto-generated method stub
System.out.println("鸡皮在吱吱作响...");
}
}
/**
* 韭菜类
* @author seokho
* Chives.java
*/
public class Chives implements BarbecueFood {
@Override
public void onFire() {
// TODO Auto-generated method stub
System.out.println("韭菜噼啪作响...");
}
}
/**
* 土豆类
* @author seokho
* Potato.java
*/
public class Potato implements BarbecueFood {
@Override
public void onFire() {
// TODO Auto-generated method stub
System.out.println("土豆正在变糊...");
}
}
####然后我准备好了我的烧烤盘
/**
* 烤盘类
* @author seokho
*
*/
public class BarbecueBakingTray {
private BarbecueFood barbecueFood;
// 放入可以烧烤的食物
public void putFoodOn(BarbecueFood barbecueFood) {
this.barbecueFood = barbecueFood;
}
// 开始烧烤
public void startBBQ() {
barbecueFood.onFire();
}
}
####一切就绪,我们从新开始烧烤吧
public class Demo {
public static void main(String[] args) {
// 准备鸡皮
BarbecueFood chickenSkin = new ChickenSkin();
// 准备韭菜
BarbecueFood chives = new Chives();
// 准备土豆
BarbecueFood potato = new Potato();
// 准备烤盘
BarbecueBakingTray barbecueBakingTray = new BarbecueBakingTray();
// 开始烤鸡皮
barbecueBakingTray.putFoodOn(chickenSkin);
barbecueBakingTray.startBBQ();
// 开始烤韭菜
barbecueBakingTray.putFoodOn(chives);
barbecueBakingTray.startBBQ();
// 开始烤土豆
barbecueBakingTray.putFoodOn(potato);
barbecueBakingTray.startBBQ();
}
}
然后你会在你的控制台看见以下文字输出:
#####鸡皮在吱吱作响…
#####韭菜噼啪作响…
#####土豆正在变糊…
好了,同一个烤盘,可以烤不同的食物。在烤盘类的 putFoodOn 方法里,传入的参数是一个接口,而实现了这个接口的类都可以作为参数进行传递,这就是依赖注入, 实现了烧烤食物共性的鸡皮类(ChickenSkin),韭菜类(Chives),土豆类(Potato),都可以通过烤盘类(BarbecueBakingTray)的putFoodOn方法注入到烤盘里。 好处嘛,我觉得是可以自由替换传入的参数而不需要更改这个类,就好像我可以放入任何可以烧烤的食物,而不需要更换烧烤盘,最大的好处就是解耦。
####依赖注入这个概念大概理解了,我需要再次记住,依赖注入(DI),通俗起来就一句大白话:我是一个类,我需要和另一个类一起才能工作,请把另一个类通过参数的形式传递给我,通过我的构造方法传递给我也可以,通过我的其他公共方法传递给我也可以。
另一个重要概念面向切面(AOP),这里先不给出代码,因为我还没有学到那里,但是请允许我再一次举个栗子,来说明这个概念。
吃烧烤,也是要有讲究的,最重要的环节,我认为就是为食物刷酱油和放香料,这样烤出来的食物色泽金黄,美味可口。但是我发现一个问题,每次烤鸡皮,烤土豆,烤韭菜的时候,我都要刷酱油和放香料,实在有点累人,或许我应该叫上我的朋友铁蛋,把这个任务交给他,于是铁蛋一听说有免费烧烤吃,屁颠屁颠就来了,我就告诉铁蛋,只要你看见食物快要熟了,就刷酱油一次,放孜然一小把,花椒面一小把。铁蛋点点头,于是我就只管烤,铁蛋非常有默契的为我们的食物进行味道上的润色,合作非常愉快。
在这里,铁蛋的角色就相当于事务管理,在我执行烧烤的过程中,一旦满足食物快要熟了的条件,或者其他条件,铁蛋就会为食物加调料,这就好比,程序无论在任何地方,一旦出错,就会生成错误报告。
我们不可能为每一个类都添加一个错误报告的生成方法,或者引用错误报告生成类,这不合逻辑。我们需要让错误报告类自己能够收集错误,这就是面向切面。
为什么说不合理呢?比如说我在烧烤土豆,要加调料,我把铁蛋叫来,铁蛋刷好了调料,就回家了。几分钟后,我开始烧烤鸡皮,又把铁蛋叫来,铁蛋刷好了调料,回家了。这样下去,铁蛋会累死。所以我让铁蛋伴随我整个烧烤过程,告诉了他什么时候该怎么做,让他自己处理为食物添加调料的工作。以上就是我对面向切面(AOP)的理解。
好了,这个系列的笔记我会按照我的学习进度进行实时更新,下一篇见。