MVC中的service controller 有状态,无状态Bean线程安全

 

 

概述

mvc框架由model,view,controller组成,执行流程一般是:在controller访问model获取数据,通过view渲染页面。

mvc模式是web开发中的基础模式,采用的是分层设计,各层之间职责分明。然而事与愿违,当我们日积月累的基于mvc模式开发之后,会逐渐的感受到层与层之间存在粘连和职责模棱两可的地方,这就是service层出现的重要原因。

问题是什么?

问题的本质是:业务逻辑粘连了C层和M层,应该从C层&M层解耦出来,成为独立的Service层。 
在C层直接实现业务逻辑,缺点: 
1. 不同的controller之间,无法共享通用的业务逻辑,比如:折扣计算。

  1. 业务逻辑升级,需直接在原代码上做修改兼容,导致controller代码不断膨胀复杂。

service层的作用:

service是业务层,是使用一个或多个模型执行操作的方法。 
1. 封装通用的业务逻辑,操作。 
如一些数据的检验,可以通用处理。 
2. 与数据层的交互。 
3. 其他请求:如远程服务获取数据,如第三方api等。

补充:

  1. Model和Service验证方面 
    Model校验:某些字段不能为空等数据层的校验 
    Service验证:逻辑层的校验,避免一些不合理的出现,尤其是调用多个模型

 

DAO层、ENTITY层、SERVICE层、CONTROLLER层

1、DAO层: 持久层  主要与数据库进行交互

   DAO层主要是做数据持久层的工作,主要与数据库进行交互。DAO层首先会创建DAO接口,然后会在配置文件中定义该接口的实现类,
   接着就可以在模块中就可以调用DAO 的接口进行数据业务的而处理,并且不用关注此接口的具体实现类是哪一个类。DAO 层的数据源和数据库连接的参数数都是在配置文件中进行配置的。
   
2、Entity层(domain层) 实体层   数据库在项目中的类

3、Service层(biz):业务层  控制业务

   Service层主要负责业务模块的逻辑应用设计。和DAO层一样都是先设计接口,再创建要实现的类,然后在配置文件中进行配置其实现的关联。接下来就可以在service层调用接口进行业务逻辑应用的处理。
   封装Service层的业务逻辑有利于业务逻辑的独立性和重复利用性。

4、Controller层:(action层) 控制层  控制业务逻辑

   Controller层负责具体的业务模块流程的控制,controller层主要调用Service层里面的接口控制具体的业务流程,控制的配置也需要在配置文件中进行。

5、View层 此层与控制层结合比较紧密,需要二者结合起来协同工发。View层主要负责前台jsp页面的表示,

Conroller层和Service层的区别是:Controlle层负责具体的业务模块流程的控制;Service层负责业务模块的逻辑应用设计;
   
总结:在具体的项目中,其流程为:Controller层调用Service层的方法,Service层调用Dao层中的方法,其中调用的参数是使用Entity层进行传递的。
 

 

 

Spring MVC默认是单例模式,Controller、Service、Dao都是单例所以在使用不当存在一定的安全隐患。Controller单例模式的好处在与: 
1. 提高性能,不用每次创建Controller实例,减少了对象创建和垃圾收集的时间 
2. 没多例的必要 
由于只有一个Controller的实例,当多个线程同时调用它的时候,它的成员变量就不是线程安全的。 
当然在大多数情况下,我们根本不需要Controller考虑线程安全的问题,除非在类中声明了成员变量。因此Spring MVC的Contrller在编码时,尽量避免使用实例变量。如果一定要使用实例变量,则可以改用以下方式: 
1. Controller中声明 scope=”prototype”,即设置为多例模式 
2.在Controller中使用ThreadLocal变量,如:private ThreadLocal<Integer> count = new ThreadLocal<Integer>();

 

 

使用Spring MVC有一段时间了,之前一直使用Struts2,在struts2中action都是原型(prototype)的, 说是因为线程安全问题,对于Spring MVC中bean默认都是(singleton)单例的,那么用@Controller注解标签注入的Controller类是单例实现的?

测试结果发现spring3中的controller默认是单例的,若是某个controller中有一个私有的变量i,所有请求到同一个controller时,使用的i变量是共用的,即若是某个请求中修改了这个变量a,则,在别的请求中能够读到这个修改的内容。 若是在@Controller之前增加@Scope(“prototype”),就可以改变单例模式为多例模式

以下是测试步骤,代码与结果.

1. 如果是单例类型类的,那么在Controller类中的类变量应该是共享的,如果不共享,就说明Controller类不是单例。以下是测试代码:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

@Controller

public class ExampleAction {

    private int singletonInt=1;

     @RequestMapping(value = "/test")

     @ResponseBody

     public String singleton(HttpServletRequest request,

             HttpServletResponse response) throws Exception {

         String data=request.getParameter("data");

         if(data!=null&&data.length()>0){

             try{

              int paramInt= Integer.parseInt(data);

             singletonInt = singletonInt + paramInt;

             }

             catch(Exception ex){

                 singletonInt+=10;

             }

         }else{

             singletonInt+=1000;

         }

         return String.valueOf(singletonInt);

    }

}

分别三次请求: http://localhost:8080/example/test.do?data=15

得到的返回结果如下。

第一次: singletonInt=15

第二次: singletonInt=30

第三次: singletonInt=45

从以上结果可以得知,singletonInt的状态是共享的,因此Controller是单例的。

2. 如果Controller类是单例,那么多个线程请求同一个Controller类中的同一个方法,线程是否会堵塞

 

1

2

3

4

5

6

7

8

9

10

11

12

13

@RequestMapping(value = "/sleepdata")

@ResponseBody

public String switcher(HttpServletRequest request

     , HttpServletResponse response)

           throws Exception {

  String sleep = request.getParameter("sleep");

  if (sleep.equals("on")) {

      Thread.currentThread().sleep(100000);

       return "sleep on";

   else {

       return sleep;

  }

验证方法:分别发送两个请求,

第一个请求:http://localhost:8080/coreplat/sleepdata.do?sleep=on

第二个请求:http://localhost:8080/coreplat/sleepdata.do?sleep=test

验证结果:第一个请求发出去以后,本地服务器等待100s,然后返回结果”sleep on”,在本地服务器等待的者100s当中,发送第二个请求,直接返回结果”test”。说明之间的线程是不互相影响的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值