你的位置:首页 > ASP.net教程

[ASP.net教程]Spring web应用最大的败笔


第一篇 介绍下IOC DI

Spring主要是业务层框架,现在已经发展成为一个完整JavaEE开发框架,它的主要特点是IoC DI和AOP等概念的融合,强项在面向切面AOP。推出之初因为Ioc/AOP等新设计理念值得框架设计者学习,现在已经成为Java世界主流框架,从其2.0引入auto-wired自动配对以后,开发效率大大提高,SpringMVC以简化和REST风格著称。Struts + Spring + Hibernate 号称SSH框架是JavaEE经典开发组合。Spring是于2003 年兴起的一个轻量级的Java开源框架框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。Rod Johnson 对Java EE正统框架臃肿、低效、脱离现实的种种现状提出了质疑,并积极寻求探索革新之道。Spring相比EJB,重新重视和定义了POJO.

平时,我们需要生成一个对象,使用new语法,如一个类为Apublic class A{  public void myMethod(){    System.out.println("hello");  }}如果我们在B中调用A,那么如下代码:public class B{  public void invoke(){    A a = new A();    a.myMethod();  }}

每次执行invoke方法时,都要生成一个A对象,如果A对象代码较长,这是费时的事
情。于是有如下写法:
public class B{
  A a = new A();
  public void invoke(){
    a.myMethod();
  }
}

将A对象变成B的类属性。 如果我们不想在B中实现A的实例,也就是不想立即new A(),而是想通过外界传入, 注意,如果你想知道为什么,这里涉及到设计模式以及解耦等因素,可详细参考 GoF 23 种设计模式。

如果想让A的实例从外界传入,有两种写法:
public class B{
  A a;
  public void setA(A a){
    this.a = a;
  }
  public A getA(){
    return a;
  }
  public void invoke(){
    a.myMethod();
  }
}
这种写法,A并没有被实例化,需要通过外界调用setA方法,将A的对象实例赋入B中. 或者通过B的构造函数传入,如下:
public class B{
  A a;
  public B(A a){
    this.a = a;
  }
  public void invoke(){
    a.myMethod();
  }
}
上述两种写法在编程中是经常发生的,B作为调用者,A是被调用者,A的实例化不在调用者B内部中完成,而是通过构造函数或setXXX方法赋值进来,这种方式我们称为依赖 性注射(IoC 模式),B 和A 的依赖联系是通过构造函数或setXXX 方法赋值进来,这样, 最大程度解耦了调用者B和被调用者A之间的耦合联系。Spring IOC三种注入方式(接口注入、setter注入、构造器注入)

Spring如何实现依赖注射:上文提到,A的实例化不在调用者B内部中完成,而是通过构造函数或setXXX 方法赋 值进来,Spring实际就是完成这个赋值的过程。 为了让Spring自动完成B代码中的A的实例化,需要通过配置文件告诉Spring有关A 的类的属性,这个配置是applicationContext.

在 applicationContext.<beans>  <bean id="b" class="springsimple.B"/></beans>这是最常用的JavaBeans的定义,id相当于对象名,当前文件应该是唯一,如果你有Jsp 编程经验,这非常类似于Jsp里调用JavaBeans语句:<jsp:userBeans id="b" class="springsimple.B"/>再在applicationContext.<beans>  <bean id="b" class="springsimple.B"/>  <bean id="a" class="springsimple.A"/></beans>这样我们告诉Spring我们有两个JavaBeans,现在解决关键问题,B代码中还调用了A, 那么如何让Spring将A的实例注射到B中?使用Spring配置的property语法。具体配置如下:<beans>  <bean id="b" class="springsimple.B">  <property name="a"><ref local="a" /></property> <!— 增加这一行--></bean>  <bean id="a" class="springsimple.A" /></beans>
增加一行说明:B 的属性a 指向了a,这样,Spring 会知道B 中属性a 的实例就是 springsimple.A,在B实例化时将会将B中的a 实现实例化,这是通过setA方法注射进入。 注意,property name="a"中的a 是setA字符中去掉set 后的字符串,这个字符串第一个必须是小写,例如,如果B中有setOneA方法,那么,配置文件应该是property name="oneA"。

在程序中调用 Spring 前面我们写了两个程序: B类和A类,并且写了配置文件applicationContext.1. 首先,将Spring配置到Web容器中。Spring框架是运行在Web容器中,所以,我们需要配置Web容器的配置文件web.  <web-app>
    <display-name>WebModule1</display-name>
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext.    </context-param>
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  </web-app>
除了配置/WEB-INF/applicationContext.

在 Web 层应用程序中使用Spring。前面已经配置成功后,就可以在Web 层的Servlet或Jsp中调用访问Spring了,如果你编制的是一个Servlet/Jsp 程序,那么在你的Servlet/Jsp 使用下面的代码通过Spring 创建B 的实例:

如果你编制的是一个Servlet/Jsp 程序,那么在你的Servlet/Jsp 使用下面的代码通过Spring 创建B 的实例:

ServletContext servletContext =this.getServletContext();WebApplicationContext appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);B b = (B) appContext.getBean("b");
上面基本是两句,首先获得WebApplicationContext实例,然后从WebApplicationContext
实例中获得B的实例。 如果你使用的是Web 层应用框架,如Struts或Tapestry,除了Spring说明提供特殊方便的调用方式,你需要从这个框架中获得当前Servlet容器ServletContext实例,再调用上面代码即可。

第二篇 回答几个概念

    

  先来看看Spring的框架结构。Spring是一个IOC和AOP的开发框架和平台。Spring的特点:轻量Spring 在大小和透明度上是轻量的,Spring基本核心版本大概只有1M,处理开销也非常小。IoC: Spring使用IOC实现松耦合,对象不是自己主动去寻找依赖而是依赖主动推给了自己。AOP : Spring支持面向方面编程,从业务逻辑中分离关注,能够实现聚焦开发。容器: Spring 容器包含和管理应用对象的生命周期。框架 : Spring提供最内核的功能,其余留给开发者自己开发。 

  Spring的核心容器模块:它提供基本功能的Spring框架。在此模块中的BeanFactory是任何基于Spring的应用程序的心脏。整个框架是建立在本模块的基础之上。该模块生成Spring容器。

  applicationContext说明,应用程序上下文模块使得Spring成为了一个框架。该模块扩展了BeanFactory的概念,国际化(I18N)消息,应用程序生命周期事件,和验证提供支持。此模块还提供了许多的企业服务,例如JNDI访问,EJB集成,远程和调度。它还提供了其他框架的支持。 

  Aop模块:该AOP模块用于Spring的应用程序开发方面。提供了大部分AOP联盟的支持,以确保Spring和其它AOP框架之间的互操作性。此模块还引入元数据编程。使用Spring的元数据支持我们的源代码,通过引入元注释,指导我们具体在哪里实现方面编程。

  JDBC和DAO抽象模块:使用这个模块,我们可以保持干净和简单的数据库的代码,并防止失败关闭数据库资源等问题。基于多个数据库服务器的出错信息之上建立的一个新的有意义的异常层。此外,该模块使用Spring的AOP模块实现事务管理。

  对象关系数据库映射整合模块:Spring支持使用一个对象/关系映射(ORM)工具,基于JDBC提供ORM模块。配合一些流行的ORM框架,包括Hibernate,JDO和iBATIS SQL映射。 Spring的事务管理也支持这些ORM框架以及JDBC。

  Web模块:该模块是建立在应用上下文模块之上,提供了一个基于Web应用的上下文。此模块还包含几个面向Web的任务,比如多个文件上传和将请求参数透明地绑定到你的业务对象。它也包含与Jakarta Struts的集成支持。

  SpringMVC:Spring提供了一个全功能的构建Web应用程序的MVC框架。虽然Spring可以很容易地集成到其他的MVC框架,如Struts,Spring的MVC框架使用IOC将控制器逻辑从业务对象中干净分离。它还允许您以声明方式绑定请求参数到你的业务对象。它还可以利用Spring的其他服务,如本地化消息和验证。

  BeanFactory:一个BeanFactory是工厂模式的实现,使用反转控制将从应用程序的配置和依赖性从应用程序代码中分离。 Spring配置文件:Spring的配置文件是一个

Bean的生命周期:Spring容器从

  一个简单的Spring应用程序包括:这些应用程序像任何Java应用程序。它们是由多个类组成,每个类执行应用程序内的一个特定的功能。这些类的配置是通过一个

第三篇 web应用的败笔

开发人员在使用Spring应用是非常擅长谈论依赖注入的好处。不幸的是,他们不是那么真的利用它的好处,如单一职责原则,分离关注原则。如果我们一起来看看大部分Spring的Web应用程序,常见的错误的设计如下:

  1.领域模型对象用来存储应用的数据(当作DTO使用),领域模型是贫血模型这样的反模式。
  2.服务层每个实体有一个服务。

问题是这样很普遍,错误在哪里呢?
Spring的web应用程序之所以这样是因为他们做事物的方式一直都是这样做的,老习惯难改,特别是如果他们是高级开发人员或软件架构师,这些人捍卫这样做的论据之一是:我们的应用程序遵循关注分离的原则,因为它已经被分为若干层,每个层有自己的特定职责。
  1. Web层负责处理用户输入,并返回正确的响应返回给用户。 web层与服务层通信。
  2.服务层作为一个事务边界。它也负责授权和包含我们的应用程序的业务逻辑。服务层管理的域模型对象,并与其他服务和存储库层进行通信。
  3.存储库/数据访问层负责与所使用的数据的存储进行通信。

分离关注(Soc)是分离计算机程序为不同的部分,每个部分有一个关注聚焦,一个典型的Spring Web应用在一定程度上遵循这一原则,但现实是,该应用程序有一个整体的服务层,它有太多的责任。更具体地,服务层有两个主要问题:

  1.在服务层发现业务逻辑。业务逻辑被分散在各个服务层。如果我们需要检查一个业务规则是如何实现的,我们必须先找到它。这可能并不容易。此外,如果相同的业务规则需要在多个服务类,问题是,规则需要从一个服务到另一个简单地复制。这将导致维护的噩梦。

  2.每个领域模型一个服务。这完全违反了单一职责原则,它被定义为如下:单一职责原则指出,每一个类都应该有一个责任,责任应该由类完全封装。其所有的服务应该狭义与责任相一致。(不应将原属于领域模型的行为方法等划放在服务中实现,对象不但有属性还有行为)。

服务类有很多依赖,以及大量的循环依赖。更像网络紧密耦合和单片服务。这使得很难理解,维护和重用。这听起来有点苛刻,但一个Spring的web应用的服务层往往是最容易出问题的部分。幸运的是,所有的希望都不会丢失。

1. 我们必须将我们的应用程序的业务逻辑从服务层迁移到领域模型类中。

举个例子:假设我是一个服务类,你是一个域模型对象。如果我让你从屋顶上跳下来,你会喜欢我这样的决定吗?(跳下来会摔伤,自己没有脑子或被洗脑,变成僵尸,只听从执行,不思考自己的安全,这就是贫血模型的问题)

将业务逻辑从服务层迁移到域模型类有下面三个优势:

(1)我们的代码将以逻辑方式切割,服务层只要关注应用逻辑,而我们的领域模型关注业务逻辑。
(2)业务逻辑只存在一个地方,容易发现修改。
(3)服务层的源代码是清洁的,不包含任何复制粘贴代码

2. 将每个实体服务切割为单一目标的更小的服务。

比如,有一个单一服务类,提供对人员和用户账户的CRUD操作,我们应该将它分为两个独立的服务类:
第一个是对人员的提供CRUD操作
第二个是提供与用户账户相关的操作。

好处:每个服务类中有一个逻辑组职责。每个服务类的依赖较少,这意味着他们不再是紧耦合的源头。他们是较小的和松耦合的组件。服务类更容易理解,维护和重用。

这两个简单的步骤将帮助我们使得我们的应用程序架构更干净,有助于同行开发商提高生产力和幸福。

第四篇 日志

日志是一种简单的不能再简单的存储抽象。它是一个只能增加的( append),完全按照时间排序的一系列记录。日志看起来如下:

 

只能给日志的末尾添加记录(append 类似队列),日志记录是从左到右读取的。每一条日志记录都有一个唯一的序列编号(一般我们使用时间戳)。 日志记录的排序是由"时间"决定,处于左边的记录比右边的要早些。记录编号可以看作是这条记录的"时间戳"。当然刚开始我们就把这种排序说成是按时间排序显得有点多余,不过,与任何一个具体的物理时钟相比,时间属性是非常便于使用的属性。在多个分布式系统中,时间会非常重要。 日志在存储空间完全耗尽的情况下,就不可能再给日志添加记录。稍后我们将会提到这个问题。 日志只是按照时间顺序存储记录的 一种数据表或者文件,可以表现为日志文件或日志数据库。日志重要特点是:它记录了在某个什么时间发生了什么事情。 而这对分布式数据系统而言才是问题的真正核心。不过,在我们更加深入的讨论之前,先澄清有些让人混淆的概念。每个编程人员都熟悉另一种日志:使用syslog或者log4j写入到本地文件里的、没有结构的、跟踪信息或错误信息。为了将两者区分开来,我们把这种日志称为"应用日志"。应用日志是我所说日志中的一种低级变种。两者最大的区别是:这些文本日志意味着主要用来方便人们阅读,而我所说的"日志"或者"数据日志"是为了方便程序访问。(实际上,如果你对它进行深入的思考,那么人们自己读取某个机器上的日志这种理念有些不顺应时代潮流。当涉及到许多服务和服务器的时候,这种方法很快就变成一个难于管理的。)

数据库日志:日志在数据库里的用法是:当数据库崩溃的时候用日志来同步各种数据结构和索引。为了保证操作的原子性和持久性,在对数据库进行维护,也就是所有各种数据结构做出更改之前,数据库会把即将修改的信息备份追加到日志里。日志记录了已经发生了什么,每个表或者索引都是其历史投影。由于日志是即刻持久化的,所以在宕机时可以用来作为恢复的可信数据源。

随着时间推移,日志用途从实现ACID发展为数据库之间复制数据的一种方法。发生在数据库上的操作动作顺序与远端备份数据库上的操作顺序通过日志保持完全同步。Oracle,MySQL 和PostgreSQL都是使用复制日志同步实现主从同步。Oracle还把日志产品化为一个通用的数据订阅机制,MySQL和PostgreSQL有类似的实现则,日志已经成为许多数据结构的关键组件。正是由于这样的起源,机器可识别的日志这个概念大部分时候还是都被局限在数据库内部。日志用做数据订阅的机制似乎是偶然出现,不过要把这种数据抽象用于支持所有类型的消息传输、数据流和实时数据处理也是可行的。

分布式系统日志:日志解决了两个问题:操作动作的顺序化和数据的分发,这两个问题在分布式数据系统里显得尤为重要。保持一致的操作动作的顺序是分布式系统设计的核心问题之一。以日志为中心实现分布式系统是受到了一个简单经验常识的启发,它称为状态机复制原理:如果两个相同的确定的处理过程从同一状态开始,以相同的顺序输入相同的(数据或事件),那么这两个处理过程必然会产生相同的输出(结果),并且在最后相同的状态结束。这也许有点难以理解,让我们更加深入的探讨,弄懂它的真正含义。确定性意味着处理过程是与时间无关,而且任何其他"外部的"输入不会影响到其处理结果。例如,如果一个程序的输出会受到线程执行的具体顺序影响,或者受到getTimeOfDay调用、或者其他一些非重复性事件的影响,那么这样的程序一般被认为是非确定性的。处理状态是处理过程保存在计算机上的任何数据(状态),在处理过程结束后,这些状态数据要么保存在内存里,要么保存在磁盘上。“以相同的顺序获得相同输入”这个地方应当引起注意的是:这里就是引入日志的地方。这儿有一个重要的常识:如果给两段确定性代码相同的日志输入,那么它们就会生成相同的输出。分布式计算在这方面的应用格外明显。你可以把用多台机器一起执行同一件事情缩减为为这些进程输入分布式一致性的日志数据。这里使用日志的目的是把所有非确定性的东西排除在输入流之外,这样来确保每个复制都能够同步地处理输入。当你理解了以后,状态机复制原理就不再复杂或者说不再深奥了:它或多或少的意味着"确定性的处理过程就是确定性的"。不管怎样,我都认为它是分布式系统设计里较常用的设计工具之一。这种方式的一个美妙之处就在于作为日志的索引的时间戳就像时钟状态的一个副本。你可以用一个单独的数字描述每一个副本,这就是经过处理的日志的时间戳。时间戳与日志一一对应着整个副本的状态。由于写进日志的内容不同,也就会有许多在系统中应用这个原则的不同方式。举个例子,我们可以记录一个服务的请求,或者也可以记录服务从请求到响应的状态变化,或者它执行命令的转换。理论上来说,我们甚至可以为每一个要执行的机器指令或者调用的方法名和参数实现一系列副本记录。只要两个处理过程用相同的方式处理这些输入,这些处理过程就会保持副本的一致性。一千个人眼中有一千种日志的用法。数据库工作者通常区分物理日志和逻辑日志。物理日志就是记录每一行被改变的内容。逻辑日志记录的不是改变的行,而是那些引起行的内容被改变的SQL语句(insert,update和delete语句)。分布式系统通常可以宽泛划分为两种方法来进行数据处理和复制。"状态机器模型"通常使用一个active-active模型,在这个模型中我们保存了请求和该请求的复制处理。我们可以对"状态机器模型"进行细微的更改,称之为"预备份primary-backup模型",也就是选出一个副本做为leader,并允许它按照请求到达的时间来进行处理请求,并将该请求导致状态的改变输出到日志。其他的副本按照leader状态改变的顺序而应用执行这些改变,这样他们之间就能达到同步,并能够在leader失败的时候接替leader的工作。

第五篇 HTTP 2.0说明

  今年春节的时候,我们发现之前访问www.baidu.com会跳转到http://www.baidu.com。但是从这天起,访问都跳转到了https://www.baidu.com。http是基于http1.0协议的。https是基于http2.0协议的。使用了https之后,我们看看发生了些什么变化。

  百度实现全站https这一技术的实现,有效避免网络信息劫持。百度这次的全站https就是一种加密协议,即使在传输的过程中被截取,也是加密的形式,所以被截取的信息都是无用的数据。

 

百度的这次全站https以后发展下来,是不是百度要对收录的网站进行更高的安全级别。当使用http的时候,你访问网站的所有数据都是明文的,只要中间有人抓到数据包就能知道你要搜索的东西。换成https的,至少别人抓到你的数据包也不知道你搜索的是啥。(https起到了保护隐私的作用)
  那么在性能方面:https多几次握手,网络耗时变长,用户从http跳转到https还要一点时间。机器性能:机器性能,https要多做一次RSA校验。CDN:全国所有节点要支持https才行,另外,如果面对DDOS,https的解决方案也会复杂得多。

  对周边系统的影响:页面里所有嵌入的资源都要改成https的,这些资源可能会来自不同的部门甚至不同的公司,包括:图片、js、form表单等等,否则浏览器就会报警。手机百度这类使用了百度搜索服务的客户端产品,也可能要修改。解决第三方网站看不到refer的问题。所有的开发、测试环境都要做https的升级。
  使用https的收益: 最恶心的是泄露用户数据,经常在网上看到说「我用百度搜了一个黄金,马上就有人联系我了」「我在百度上搜了一种病,马上医院就来电话了」。其实在搜索HTTPS很久之前,百度就做了搜索结果url加密。用户搜索安全,被运营商强插广告,甚至在正常结果前面插一条广告。