获得徽章 0
最新 热门
  • ## SpringMVC 容器 之前分析了过 Spring 的启动过程了,今天看下 SpringMVC 的启动。一样的,我们先看下 web.xml,SpringMVC 是以 Servlet 配置出现的 ``` <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-application.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> ``` 之前分析了 ContextLoaderListener,实例化 IoC 容器,并将此容器实例注册到 ServletContext 中。我们先看下 DispatcherServlet 的类图及继承关系: <div align="center"> <img src="https://github.com/Jimmy2Angel/jimmy2angel.github.io/blob/master/img/SpringMVC/DispatcherServletClass.png?raw=true"/> </div> SpringMVC 最核心的类就是 DispatcherServlet, 关于 Spring Context 的配置文件加载和创建是在 init() 方法中进行的,主要的调用顺序是 init-->initServletBean-->initWebApplicationContext 。 先来看一下 initWebApplicationContext 的实现:FrameworkServlet.java <div align="center"> <img src="https://github.com/Jimmy2Angel/jimmy2angel.github.io/blob/master/img/SpringMVC/initWebApplicationContext1.png?raw=true"/> <img src="https://github.com/Jimmy2Angel/jimmy2angel.github.io/blob/master/img/SpringMVC/initWebApplicationContext2.png?raw=true"/> </div> 先简单说下这些代码的功能: 514 行:从 ServletContext 中获取 rootContext 也就是 SpringIOC 容器 517 行:如果一个 context 的实例被注入了,直接使用 538 行:从 ServletContext 中获取 webApplicationContext 也就是 SpringMVC 容器 543 行:创建 SpringMVC 的容器,并将 rootContext 作为父容器 550 行:刷新上下文(执行组件的初始化),这个方法由子类 DispatchServlet 的方法实现 556 行:将 SpringMVC 容器作为属性设置进 ServletContext 这里多说一句,SpringMVC 容器在 ServletContext 中的属性名: ``` public String getServletContextAttributeName() { return SERVLET_CONTEXT_PREFIX + getServletName(); } public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; ``` 而 SpringIOC 容器在 ServletContext 中的属性名: ``` String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; ``` 前面的没什么好说的,我们看下 onRefresh() 方法,调用了 initStrategies() 方法: <div align="center"> <img src="https://github.com/Jimmy2Angel/jimmy2angel.github.io/blob/master/img/SpringMVC/DispatcherServlet-onRefresh.png?raw=true"/> </div> 执行 MVC 的相关组件的初始化,我们以 HandlerMappings 为例看来看下: <div align="center"> <img src="https://github.com/Jimmy2Angel/jimmy2angel.github.io/blob/master/img/SpringMVC/DispatcherServlet-initHandlerMappings.png?raw=true"/> </div> detectAllHandlerMappings 默认为 true,从当前的 SpringMVC 容器及其父容器中查找所有的 HandlerMappings,否则只从当前的 SpringMVC 容器中查找 HandlerMapping,如果没有找到 handlerMappings,设置默认的 handlerMapping,默认值设置在 DispatcherServlet 同级目录的 DispatcherServlet.properties 中。 ### 多说一句 上面的 findWebApplicationContext(),createWebApplicationContext(rootContext) 之类的方法点进去看看也很容易懂,我就不贴源码了,然后 createWebApplicationContext 中会层层调用直到 AbstractApplicationContext 的 refresh 方法来初始化 bean,这个方法在之前分析 Spring 启动的时候看过,这里也就不看了。 还是那句话,以我现在水平分析源码并不指望能看懂并理解每一句每一行,但是看不懂的方法你就点进去看看,万一里面里面的东西你看过呢是不是,就怕看不懂然后觉得这行代码不重要就不看了。 嗯?说完了?怎么感觉看完之前的 [Spring 容器那点事](https://jimmy2angel.github.io/2017/05/31/SpringIOC/),再看这个好像也没什么了。我们再来简单说下 Spring 容器和 SpringMVC 容器的 py(手动滑稽) 关系。 ## Spring 容器 和 SpringMVC 容器的关系 ContextLoaderListener 中创建 ApplicationContext(SpringIOC 容器)主要用于整个 Web 应用程序需要共享的一些组件 ,比如 DAO,数据库的 ConnectionFactory 等。而由 DispatcherServlet 创建的 ApplicationContext(SpringMVC 容器)主要用于和该 Servlet 相关的一些组件 ,比如 Controller、ViewResovler 等。 对于作用范围而言, 在 DispatcherServlet 中可以引用由 ContextLoaderListener 所创建的 ApplicationContext ,而反过来不行。 在 Spring 的具体实现上,这两个 ApplicationContext 都是通过 ServletContext 的 setAttribute 方法放到 ServletContext 中的。但是, ContextLoaderListener 会先于 DispatcherServlet 创建 ApplicationContext,DispatcherServlet 在创建 ApplicationContext 时会先找到由 ContextLoaderListener 所创建的 ApplicationContext,再将后者的 ApplicationContext 作为参数传给 DispatcherServlet 的 ApplicationContext 的 setParent() 方法, ``` wac.setParent(parent); ``` 其中, wac 即为由 DisptcherServlet 创建的 ApplicationContext,而 parent 则为 ContextLoaderListener 创建的 ApplicationContext 。此后,框架又会调用 ServletContext 的 setAttribute() 方法将 wac 加入到 ServletContext 中。 当 Spring 在执行 ApplicationContext 的 getBean 时, 如果在自己 context 中找不到对应的 bean,则会在父容器中去找 。这也解释了为什么我们可以在 DispatcherServlet 中获取到由 ContextLoaderListener 对应的 ApplicationContext 中的 bean。举个例子就是,你可以在 controller 层中注入 service 层的 bean。
  • 你可能看到标题会觉得这两个模式有啥关系啊,很好,这两个模式确实没有关系哈哈,但是我今天把这两个模式放在一起说还是有原因的。 ## 举个栗子 ### 装饰模式 Component:抽象构建 ConcreteComponent:具体构建类(被装饰类) Decorator:装饰类 ``` interface Component{ void method(); } class ConcreteComponent implements Component{ public void method(){ System.out.println("method"); } } class Decorator implements Component{ private Component component; public Decorator(Component component){ this.component = component; } public void method(){ System.out.println("PreDecorate"); component.method(); System.out.println("PostDecorate"); } } public class Test { public static void main(String args[]){ ConcreteComponent concreteComponent = new ConcreteComponent(); Decorator decorator = new Decorator(concreteComponent); decorator.method(); } } ``` ### 代理模式 ``` interface Subject{ void request(); } class RealSubject implements Subject{ public void request(){ System.out.println("request"); } } class Proxy implements Subject{ private RealSubject realSubject; public Proxy(){ this.realSubject = new RealSubject(); } public void request(){ System.out.println("PreProcess"); realSubject.request(); System.out.println("PostProcess"); } } public class ProxyDemo { public static void main(String args[]){ Proxy p = new Proxy(); p.request(); } } ``` ## 栗子分析 上面的栗子中,第一个是装饰模式,第二个代理模式,在接口的实现上面以至于整个代码上面几乎都没有多大的差别。以至于很多刚开始学习设计模式的同学看的有点晕,分不清两个的区别在哪。我们先来看下代码上的区别在哪: ### 第一处区别 在客户端代码(也就是此处的测试代码)中,装饰模式先创建一个具体构建类(被装饰类),然后作为参数通过构造器创建一个装饰类。而代理模式中,直接创建一个代理类,此处你看不见被代理类是什么,也就是说被代理类对于客户端是透明的。 ### 第二处区别 装饰类中,将客户端代码中传递过来了的被装饰类赋给装饰类持有的抽象构建引用 代理类中,持有的是被代理类引用,且在构造函数中创建一个被代理类实例。 ### 比较分析 这两个区别说的仅仅是第一个栗子中代码上的区别,但是至少可以看出来一点,被代理对象对于客户端来说是透明的。 ## 再举一栗 ### 装饰模式 ``` interface Arrangement { public void show(); } class Person implements Arrangement { private String name; public Person(String name){ this.name = name; } @Override public void show() { System.out.println(name + "的搭配:"); } } abstract class Dress implements Arrangement { private Arrangement arrangement; public void Dress(Arrangement arrangement){ this.arrangement = arrangement; } @Override public void show() { if (arrangement != null){ arrangement.show(); } } } class DKC extends Dress { @Override public void show() { super.show(); System.out.println("大裤衩"); } } class PX extends Dress { @Override public void show() { super.show(); System.out.println("皮鞋"); } } public class Test { public static void main(String[] args) { Person person = new Person("小明"); DKC dkc = new DKC(); PX px = new PX(); dkc.Dress(person); px.Dress(dkc); px.show(); } } ``` ### 代理模式 Girl:被追求的妹子 GiveGift:送礼物 Suitor:真实的追求者(由于比较害羞,找了一个代理帮自己追) Proxy:代理类 ``` class Girl { private String name; public Girl(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } interface GiveGift { public void GiveFlowers(); public void GiveDove(); public void GiveDolls(); } class Suitor implements GiveGift { private Girl girl; public Suitor(Girl girl){ this.girl = girl; } @Override public void GiveFlowers() { System.out.println(girl.getName()+",送你花!"); } @Override public void GiveDove() { System.out.println(girl.getName()+",送你巧克力!"); } @Override public void GiveDolls() { System.out.println(girl.getName()+",送你洋娃娃!"); } } public class Proxy implements GiveGift { private Suitor suitor; public Proxy(Girl girl){ this.suitor = new Suitor(girl); } @Override public void GiveFlowers() { suitor.GiveFlowers(); } @Override public void GiveDove() { suitor.GiveDove(); } @Override public void GiveDolls() { suitor.GiveDolls(); } public static void main(String[] args) { Girl girl = new Girl("marry"); Proxy proxy = new Proxy(girl); proxy.GiveDolls(); proxy.GiveDove(); proxy.GiveFlowers(); } } ``` 结合这个栗子再来说明一下两个模式分别是什么。 ### 装饰模式是什么 装饰模式可以说就是在已有功能的基础上添加更多的功能,把每个要添加的功能都放在一个单独的类中,并让这个类包装被装饰对象,然后在执行特定任务时,客户端代码就可以在运行时根据需要自由组合,按顺序地使用这些装饰功能。 ### 代理模式是什么 代理模式就是为一个对象提供一个代理并控制着对这个对象的访问。并且被代理对象对于客户端代码是透明的。就像最后这个栗子中,客户端代码并不知道真实的追求者是谁。代理类控制着真实追求着的访问,当然也可以添加一些功能什么的(就像装饰模式那样)。 ## 总结 现在理解了两个模式之后,就会发现两个模式的目的并没有什么关联。完全没必要纠结像第一个栗子中的代码相似问题。要说真实应用的话,JDK的IO包中的BufferedInputstream就是一个装饰类,Spring的AOP就用的是代理,不过是动态代理而已。 最后推荐学习设计模式的小伙伴们有兴趣可以看一下《大话设计模式》这本书,通过讲故事的形式把道理给讲明白了,个人还是觉得蛮好的。
  • 个人成就
    文章被点赞 76
    文章被阅读 5,656
    掘力值 277
    收藏集
    1
    关注标签
    16
    加入于