控制器是 MVC 框架的核心组件,应用程序控制器的方法处理 HTTP 请求。这里首当其冲需要回答的一个问题就是控制器的实例应该是单例 (Singleton)还是就每个请求生成新的实例。
不同的框架对此有不同的处理方式,SpringMVC 的控制器采用单例;Struts 的 Action 则是多实例;PlayFramework v1.x 的方式比较极端,控制器方法必须是静态的,因此不需要生成实例。
ActFramework 生成控制器实例的方式与众不同,没有统一的单例或者多例的限制,而是根据应用程序控制器代码来决定是否采用共享单例,还是就每个请求生成新的控制器实例。
public class HelloController {
@GetAction("/hello")
public String sayHello() {
return "Hello world!";
}
}
上面的控制器没有任何字段,因此对于任何发往 /hello
的请求,只会有一个 HelloController
的实例来响应。
public class LoggedInHelloController {
@LoginUser User me;
@GetAction("/hi")
public String sayHi() {
return "Hi from " + me.getFullName();
}
}
LoggedInHelloController
控制器有一个字段 User me
,因此 ActFramework 认定这个控制器是有状态的,所以会对每个请求生成新的控制器实例。
@Entity("user")
public class User {...}
@Stateless
public class UserDao extends EbeanDao<User> {...}
@UrlContext("/users")
public class UserController {
@Inject
private UserDao userDao;
@PostAction
public User create(User user) {return userDao.save(user);}
@GetAction
public Iterable<User> list() {return userDao.findAll();}
...
}
上面的 UserDao
类被标注为 @Stateless
,因此虽然 UserController
类中有 UserDao userDao
的字段,ActFramework 依然认定 UserController
是无状态的,所以所有请求响应会共享一个 UserController
实例
在类上标注 @Stateless
的方法非常简便好用,但当控制器中需要注入来自三方库的无状态对象,应用程序开发人员没有办法改变其代码,因此只能在使用的地方标注 @Stateless
:
public class SuperHelloController {
@Stateless
@Inject
private HelloHelper helper;
@GetAction("/superHello")
public String superHello() {
return helper.hello();
}
}
在 SuperHelloController
中有一个 HelloHelper helper
字段,假设 HelloHelper
类来自三方库,而我们确信这个类和请求无关,因此标注该字段为 @Stateless
,这样 ActFramework 会认定 SuperHelloController
为无状态的,所有请求共享一个 SuperHelloController
实例。
ActFramework 依据控制器的字段状态来判定是否对控制器做单例,或者多实例处理
@Stateless
或字段类型上有 @Stateless
标注,则处理为单例@Stateless
的字段,则处理为多实例