MVC

    Controller是JFinal核心类之一,该类作为MVC模式中的控制器。基于JFinal的Web应用的控制器需要继承该类。Controller是定义Action方法的地点,是组织Action的一种方式,一个Controller可以包含多个Action。Controller是线程安全的。

    JbootController是扩展了JFinal Controller,在Jboot应用中,所有的控制器都应该继承至JbootController。

    JbootController新增的普通方法:

    新增关于FlashMessage的方法:

    方法调用 描述
    setFlashAttr() 设置 FlashMessage 的 key 和 value
    setFlashMap() 把整个 map的key和value 设置到 FlashMessage
    getFlashAttr() 获取 已经设置进去的FlashMessage 信息
    getFlashAttrs() 获取 所有已经设置进去的 FlashMessage 信息

    FlashMessage 是一种特殊的 attribute,用法和 setAttr 一样,唯一不同的是 setAttr 是用于当前页面渲染,而
    setFlashAttr 是用于对 redirect 之后的页面进行渲染。

    新增关于JWT的方法:

    JWT简介: Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

    JWT的相关配置

    配置属性 描述
    jboot.web.jwt.httpHeaderName 配置JWT的http头的key,默认为JWT
    jboot.web.jwt.secret 配置JWT的密钥
    jboot.web.jwt.validityPeriod 配置JWT的过期时间,默认不过期

    @RquestMapping

    RquestMapping是请求映射,也就是通过注解,可以让某个请求映射到指定的控制器Controller里去。

    使用@RquestMapping

    使用非常简单。只需要在Controller类添加上@RquestMapping注解即可。

    例如:

    我们在HelloController控制器上,添加了(“/“)配置,也就是让当访问 的时候让HelloController控制的index()这个方法(action)来处理。

    [注意]:

    • 访问http://127.0.0.1等同于http://127.0.0.1/
    • @RquestMapping 可以使用在任何的 Controller,并 不需要 这个Controller继承至JbootController。

    在 Controller 之中定义的 public 方法称为 Action。Action 是请求的最小单位。Action 方法 必须在 Controller 中定义,且必须是 public 可见性。

    每个Action对应一个URL地址的映射:

    1. public class HelloController extends Controller {
    2. public void index() {
    3. renderText("此方法是一个action");
    4. }
    5. public String test() {
    6. return "index.html";
    7. }
    8. public String save(User user) {
    9. user.save();
    10. render("index.html");
    11. }
    12. }

    以上代码中定义了三个 Action,分别是 HelloController.index()HelloController.test()HelloController.save(User user)

    Action 可以有返回值,返回值可在拦截器中通过 invocation.getReturnValue() 获取到,以便进行 render 控制。

    注意: 带参数的Action必须在pom.xml文件里添加如下配置:

    1. <plugin>
    2. <groupId>org.apache.maven.plugins</groupId>
    3. <artifactId>maven-compiler-plugin</artifactId>
    4. <configuration>
    5. <source>1.8</source>
    6. <target>1.8</target>
    7. <encoding>UTF-8</encoding>
    8. <!--必须添加compilerArgument配置,才能使用JController方法带参数的功能-->
    9. <compilerArgument>-parameters</compilerArgument>
    10. </configuration>
    11. </plugin>

    如果 action 形参是一个 model 或者 bean,原先通过 getBean(User.class, “”) 获取 时第二个参数为空字符串或 null,那么与之等价的形参注入只需要用一下(“”)注解即可:

    1. public void save(@Para(“”)User user) {
    2. user.save();
    3. render("index.html");
    4. }

    getPara 系列方法

    Controller 供了 getPara 系列方法用来从请求中获取参数。getPara 系列方法分为两种类型。 第一种类型为第一个形参为 String 的 getPara 系列方法。该系列方法是对 HttpServletRequest.getParameter(String name) 的 封 装 , 这 类 方 法 都 是 转 调 了 HttpServletRequest.getParameter(String name)。第二种类型为第一个形参为 int 或无形参的 getPara 系列方法。该系列方法是去获取 urlPara 中所带的参数值。getParaMap 与 getParaNames 分别对应 HttpServletRequest 的 getParameterMap 与 getParameterNames。

    getModel 用来接收页面表单域传递过来的 model 对象,表单域名称以”modelName.attrName”方式命名,getModel 使用的 attrName 必须与数据表字段名完全一样。getBean 方法用于支持传统 Java Bean,包括支持使用 jfnal 生成器生成了 getter、setter 方法的 Model,页面表单传参时使用与 setter 方法相一致的 attrName,而非数据表字段名。 getModel 与 getBean 区别在于前者使用数表字段名而后者使用与 setter 方法一致的属性名进行数据注入。建议优先使用 getBean 方法。

    以下是一个简单的示例:

    上面代码中,表单域采用了”blog.title”、”blog.content”作为表单域的 name 属性,”blog”是类 文件名称”Blog”的首字母变小写,”title”是 blog 数据库表的 title 字段,如果希望表单域使用任 意的 modelName ,只需要在 getModel 时多添加一个参数来指定,例如: getModel(Blog.class, ”otherName”)。

    render

    渲染器,负责把内容输出到浏览器,在Controller中,提供了如下一些列render方法。

    指令 描述
    render(”test.html”) 渲染名为 test.html 的视图,该视图的全路径为”/path/test.html”
    render(”/other_path/test.html”) 渲染名为 test.html 的视图,该视图的全路径为”/other_path/test.html”,即当参数以”/”开头时将采用绝对路径。
    renderTemplate(”test.html”) 渲染名为 test.html 的视图,且视图类型为 JFinalTemplate。
    renderFreeMarker(”test.html”) 渲 染 名 为 test.html 的视图 , 且 视图类型为FreeMarker。
    renderJsp(”test.jsp”) 渲染名为 test.jsp 的视图,且视图类型为 Jsp。
    renderVelocity(“test.html”) 渲染名为 test.html 的视图,且视图类型为 Velocity。
    renderJson() 将所有通过 Controller.setAttr(String, Object)设置的变量转换成 json 数据并渲染。
    renderJson(“users”, userList) 以”users”为根,仅将 userList 中的数据转换成 json数据并渲染。
    renderJson(user) 将 user 对象转换成 json 数据并渲染。
    renderJson(“{\”age\”:18}” ) 直接渲染 json 字符串。
    renderJson(new String[]{“user”, “blog”}) 仅将 setAttr(“user”, user)与 setAttr(“blog”, blog)设置的属性转换成 json 并渲染。使用 setAttr 设置的其它属性并不转换为 json。
    renderFile(“test.zip”); 渲染名为 test.zip 的文件,一般用于文件下载
    renderText(“Hello Jboot”) 渲染纯文本内容”Hello Jboot”。
    renderHtml(“Hello Html”) 渲染 Html 内容”Hello Html”。
    renderError (404 , “test.html”) 渲染名为 test.html 的文件,且状态为 404。
    renderError (500 , “test.html”) 渲染名为 test.html 的文件,且状态为 500。
    renderNull() 不渲染,即不向客户端返回数据。
    render(new MyRender()) 使用自定义渲染器 MyRender 来渲染。

    使用session非常简单,直接在Controller里调用getSessionAttr(key)setSessionAttr(key,value) 就可以。

    分布式session

    在Jboot的设计中,分布式的session是依赖分布式缓存的,jboot中,分布式缓存提供了3种方式:

    1. ehcache
    2. redis
    3. ehredis: 基于ehcache和redis实现的二级缓存框架。

    所以,在使用jboot的分布式session之前,需要在jboot.properties配置上jboot分布式的缓存。

    例如:

    1. jboot.cache.type=redis
    2. jboot.cache.redis.host = 127.0.0.1
    3. jboot.cache.redis.password = 123456
    4. jboot.cache.redis.database = 1

    配置好缓存后,直接在Controller里调用getSessionAttr(key)setSessionAttr(key,value) 即可。

    注意: session都是走缓存,如果jboot配置的缓存是ehcache(或者 ehredis),请注意在ehcache.xml上添加名为 SESSION 的缓存节点。

    限流和流量控制

    在Jboot中,默认提供了4个注解进行流量管控。4个注解代表着四个不同的流量管控方案,他们分别是:

    例如:

    1. @RequestMapping("/limitation")
    2. public class LimitationDemo extends JbootController {
    3. public static void main(String[] args) {
    4. Jboot.setBootArg("jboot.limitation.webPath","/limitation/view");
    5. Jboot.run(args);
    6. }
    7. public void index() {
    8. }
    9. /**
    10. * 所有的请求,每1秒钟只能访问一次
    11. */
    12. @EnableRequestLimit(rate = 1)
    13. public void request() {
    14. renderText("request() render ok");
    15. }
    16. * 所有的请求,并发量为1个
    17. */
    18. @EnableConcurrencyLimit(rate = 1)
    19. public void con() {
    20. try {
    21. Thread.sleep(2000);
    22. } catch (InterruptedException e) {
    23. e.printStackTrace();
    24. }
    25. renderText("con() render ok");
    26. }
    27. /**
    28. * 所有的请求,每1秒钟只能访问一次
    29. * 被限制的请求,自动跳转到 /limitation/request2
    30. */
    31. @EnableRequestLimit(rate = 1, renderType = LimitRenderType.REDIRECT, renderContent = "/limitation/request2")
    32. public void request1() {
    33. renderText("request1() render ok");
    34. }
    35. public void request2() {
    36. renderText("request2() render ok");
    37. }
    38. /**
    39. * 每个用户,每5秒钟只能访问一次
    40. */
    41. @EnablePerUserLimit(rate = 0.2)
    42. public void user() {
    43. renderText("user() render ok");
    44. }
    45. /**
    46. * 每个用户,每5秒钟只能访问一次
    47. * 被限制的请求,渲染文本内容 "被限制啦"
    48. */
    49. @EnablePerUserLimit(rate = 0.2, renderType = LimitRenderType.TEXT, renderContent = "被限制啦")
    50. public void user1() {
    51. renderText("user1() render ok");
    52. }
    53. /**
    54. * 每个IP地址,每5秒钟只能访问一次
    55. */
    56. @EnablePerIpLimit(rate = 0.2)
    57. public void ip() {
    58. renderText("ip() render ok");
    59. }

    以上代码和注释已经很清楚的描述了每个注解的意义,但是,针对已经上线的项目,使用@EnableXXXLimit进行流量控制并一定是有效的,很多时候我们需要针对突发流量进行限制和管控,因此,除了以上注解意外,Jboot提供了在线流量管理功能。

    使用Jboot在线流量管理,首先配置上流量管理的URL地址,例如:

    1. jboot.limitation.webPath = /jboot/limitation

    配置好,启动项目,访问 http://127.0.0.1:8080/jboot/limitation 我们可以看到如下内容:

    限流API

      • 接口:/jboot/limitation/set
      • 参数:

        | 参数 | 描述 |
        | ——————- | ——-|
        | type | 限流类型:支持有 ip,user,request,concurrency,分别代表:单个IP每秒钟限流、单个用户每秒钟限流、每秒钟允许请求的数量,总体并发量设置 |
        | path |要对那个路径进行设置,例如 /user/aabb|
        | rate |设置的数值是多少|

    1. 关闭限流管控

      • 接口:/jboot/limitation/close
      • 参数:

        | 参数 | 描述 |
        | ——————- | ——-|
        | type | 限流类型:支持有 ip,user,request,concurrency,分别代表:单个IP每秒钟限流、单个用户每秒钟限流、每秒钟允许请求的数量,总体并发量设置 |
        | path |要对那个路径进行设置,例如 /user/aabb|

    2. 开启限流管控

      • 接口:
      • 参数:

        | 参数 | 描述 |
        | ——————- | ——-|
        | type | 限流类型:支持有 ip,user,request,concurrency,分别代表:单个IP每秒钟限流、单个用户每秒钟限流、每秒钟允许请求的数量,总体并发量设置 |
        | path |要对那个路径进行设置,例如 /user/aabb|

    注意:

    1. 通过限流API进行限流,所有的设置都会保存在内存里,因此如果重启服务器后,通过限流API进行限流的所有设置将会失效。
    2. 接口的前缀 /jboot/limitation是通过jboot.properties的jboot.limitation.webPath = /jboot/limitation进行设置的。

    限流API安全设置

    由于限流功能对系统至关重要,为了防止恶意用户猜出限流API对系统进行恶意操作,因此Jboot提供了限流API的权限设置功能,需要通过 通过jboot.properties的jboot.limitation.webAuthorizer = com.xxx.MyAuthorizer进行设置,其中MyAuthorizer需要实现io.jboot.web.limitation.web.Authorizer接口。

    例如:

    1. public class MyAuthorizer implements Authorizer {
    2. @Override
    3. public boolean onAuthorize(Controller controller) {
    4. return true;
    5. }
    6. }

    当限流API被请求的时候,会通过 MyAuthorizer 进行权限认证,只有MyAuthorizer通过(onAuthorize返回true)的时候,请求API才会生效。

    在使用websocket之前,需要在jboot.properties文件上配置启动websocket,例如:

    1. jboot.web.websocketEnable = true
    2. jboot.web.websocketBufferPoolSize = 100

    jboot.web.websocketBufferPoolSize 在没有配置的情况下,默认值是100

    当做好以上配置后,就可以开始编写websocket的相关代码了。

    html代码:

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Insert title here</title>
    6. </head>
    7. <body>
    8. 服务器返回的信息:
    9. <input type="text" id="show"/>
    10. 浏览器发送的信息:
    11. <input type="text" id="msg"/>
    12. <input type="button" value="send" id="send" onclick="send()"/>
    13. <script>
    14. var ws = null ;
    15. var target="ws://localhost:8080/websocket/test";
    16. if ('WebSocket' in window) {
    17. ws = new WebSocket(target);
    18. } else if ('MozWebSocket' in window) {
    19. ws = new MozWebSocket(target);
    20. } else {
    21. alert('WebSocket is not supported by this browser.');
    22. }
    23. ws.onopen = function(obj){
    24. console.info('open') ;
    25. console.info(obj) ;
    26. } ;
    27. ws.onclose = function (obj) {
    28. console.info('close') ;
    29. console.info(obj) ;
    30. } ;
    31. ws.onmessage = function(obj){
    32. console.info(obj) ;
    33. document.getElementById('show').value=obj.data;
    34. } ;
    35. function send(){
    36. ws.send(document.getElementById('msg').value);
    37. }
    38. </script>

    java代码: