浅谈 Flask 框架

一、框架对比

Django Flask
Admin —— Model 原生无
Model 原生无
Form 原生无
Session 有 —— 颠覆认知操作
  • Django —— 教科书式框架

    • 优势:组件全,功能全,教科书
    • 劣势:占用资源,创建复杂度高
  • Flask —— 以简单为基准开发,一切从简,能省则省
    • 优势:轻,块
    • 劣势:先天不足,第三方组件稳定性较差

二、Flask入门

  1. 下载安装

    • 下载:pip install Flask
    • 注意:不要使用工具中的插件创建 Flask 项目
  2. 三行代码启动Flask项目
    from flask import Flask
    app = Flask(__name__)
    app.run()

三、Flask的请求和响应

3.1 Flask中的响应Response

  1. 与django对比

    Django Flask 返回
    HttpResponse("hello") "hello" 字符串
    render(request,"html",{"k":v}) render_template("html",k=v) html页面
    redirect("/") redirect("/") 重定向
    JsonResponse({"k":v}) jsonify({"k":v}) 标准的json字符串
    • 以上是web框架的Response三剑客
  2. 返回字符串 —— string
    from flask import Flask
    app = Flask(__name__)
    @app.route("/")
    def home():
        return "hello World I am Flask"
    app.run()
  3. 返回html页面 —— string
    from flask import Flask, render_template
    app = Flask(__name__)
    @app.route("/index")
    def index():
        return render_template("index.html")
    app.run()
  4. 返回重定向 —— string
    from flask import Flask, redirect
    app = Flask(__name__)
    @app.route("/login")
    def login():
        return redirect("/index")
    app.run()
  5. 返回文件内容 —— Response instance
    • send_file():读取并返回文件内容,自动识别文件类型,Content-type中添加类型,即,Content-type:文本类型
    • 浏览器特性:
      • 可识别的Content-type,会自动渲染
      • 不可识别的Content-type,会自动下载
    from flask import Flask, send_file
    app = Flask(__name__)
    @app.route("/get_file")
    def get_file():
        # 返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型
        return send_file("2.exe")
    app.run()
  6. 返回标准格式的json字符串 —— string
    • jsonify():返回标准格式的json字符串,本质:先序列化json的字典,Content-type中加入Application/json,Flask1.1.1 直接返回字典格式,相当于自动执行jsonify
    from flask import Flask, send_file
    app = Flask(__name__)
    @app.route("/get_json")
    def get_json():
        d = {"k":"v"}
        # 返回标准Json格式字符串 API接口
        return jsonify(d)
    app.run()

3.2 Flask中的请求Request

  1. 与django对比

    Django Flask Flask中的含义
    request.method request.method 获取请求方式
    request.GET request.args 获取URL中的数据
    request.POST request.form 获取FormData中的数据 ,也就是所谓的Form标签
    request.FILES request.files 获取request中的文件,返回FileStorage中,存在save(保存文件)和filename(原始文件名)
      request.json 请求中Content-Type:application/json,请求体中的数据被序列化到 request.json中,以字典的形式存放
      request.data 请求中Content-Type中不包含Form或FormData,保留请求体中的原始数据,bytes类型
    request.path_info request.path 请求路径 路由地址
    request.get_full_patch() request.url 访问请求的完整路径包括 url参数
    request.get_host() request.host 主机位 127.0.0.1:5000
    request.COOKTES request.cookies 字典获取浏览器请求时带上的Cookie
    • 注意:request.values,获取url和FormData中的数据,敏感地带(url和FormData中存在相同的key时会出错)
  2. 示例
    import os
    from flask import Flask, request, render_template
    # request 请求上下文管理
    app = Flask(__name__)
    ?
    @app.route("/login",methods=["GET","POST"])
    def login():
        # print(request.args.to_dict())
        # print(request.host)
        # print(request.path)
        # print(request.url)
        # print(request.cookies)
        # 优先判断 请求方式
        # 如果是 GET 请求 返回登录页面
        if request.method == "GET":
            return render_template("login.html")
        # 如果是 POST 请求 获取用户名密码 校验
        else: # 405 请求方式不被允许
            my_file = request.files.get("my_file")
            filename = my_file.filename # 获取原始文件名
            file_path = os.path.join("avatar",filename)
            my_file.save(file_path)
            print(request.form.to_dict()) # form - FormData
            if request.form.get("username") == "YWB":
                return "Login OK!"
        return "Login 不OK!"
    ?
    if __name__ == ‘__main__‘:
        app.run()

四、 Jinja2:template语言

  • {{ }} 引用或执行
  • {% %} 逻辑引用
    STUDENT = {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘}
    STUDENT_LIST = [
        {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘},
        {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘},
        {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘}
    ]
    STUDENT_DICT = {
        1: {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘},
        2: {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘},
        3: {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘},
    }
    from flask import Flask, render_template, Markup
    app = Flask(__name__)
    ?
    @app.template_global()
    def ab(a,b):
        return a+b
    ?
    @app.route("/")
    def home():
        return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT)
    ?
    @app.template_global()
    def my_input(na,ty):
        s = f"<input type=‘{ty}‘ value=‘{na}‘>"
        return Markup(s)
    ?
    if __name__ == ‘__main__‘:
        app.run()

    <p>{{ ab(50,60) }}</p>
    
    {{ stu }}
    <table border="1px">
        <tr>
            <td>name</td>
            <td>age</td>
            <td>gender</td>
        </tr>
        <tr>
            <td>{{ stu.name }}</td>
            <td>{{ stu["age"] }}</td>
            <td>{{ stu.get("gender") }}</td>
        </tr>
    </table>
    
    {{ stu_l }}
    <table border="1px">
        <tr>
            <td>name</td>
            <td>age</td>
            <td>gender</td>
        </tr>
        {% for student in stu_l %}
        <tr>
            <td>{{ student.name }}</td>
            <td>{{ student["age"] }}</td>
            <td>
                {% if  student.get("gender") != "男" and  student.get("gender") != "女" %}
                女
                {% else %}
                {{ student.get("gender") }}
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </table>
    
    {{ stu_d }}
    <table border="1px">
        <tr>
            <td>id</td>
            <td>name</td>
            <td>age</td>
            <td>gender</td>
        </tr>
        {% for skey,svalue in stu_d.items() %}
        <tr>
            <td>{{ skey }}</td>
            <td>{{ svalue.get("name") }}</td>
            <td>{{ svalue.get("age") }}</td>
            <td>
                11111
            </td>
        </tr>
        {% endfor %}
    </table>

    stu.html

五、Flask的知识点

5.1 Flask中的session

  • 基于请求上下文管理机制

    STUDENT = {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘}
    STUDENT_LIST = [
        {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘},
        {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘},
        {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘}
    ]
    STUDENT_DICT = {
        1: {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘},
        2: {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘},
        3: {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘},
    }
    from flask import Flask, render_template, request, session, redirect
    app = Flask(__name__)
    # app.config["DEBUG"] = True
    app.debug = True
    app.secret_key = "[email protected]#$%^(*&^%$#@#$%&*(^$WQ*(^EWET*^EWEU"
    
    def war(func): # func = home
        def inner(*args,**kwargs):
            # 校验session
            if session.get("user"):
                ret = func(*args,**kwargs) # func = home
                return ret
            else:
                return redirect("/login")
        return inner
    
    @app.route("/")
    @war           # war(home) -> inner == == home
    # 先要经过一次校验 才能执行或不执行视图函数
    def home():
        # 校验用户登录 session
        #如果session。get(“user”) 存在
        # 如果不存在 返回Login
        return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT)
    
    @app.route("/a")
    def homea():
        # 校验用户登录状态?
        # 校验session中有没有 user Key
        if session.get("user"):
            inp = Markup("<input type=‘submit‘ value=‘后端给返回的按钮‘>")
            return render_template("my_a.html", btn=inp)
        # 校验失败
        # 跳转login
        else:
            return redirect("/login")
    
    @app.route("/login",methods=["GET","POST"])
    def login():
        # 当请求方式为 GET 的时候
        if request.method == "GET":
            # eyJ1c2VyIjoiYWxleGFuZGVyLmRzYi5saSJ9.XUfYMw.o8oZX2GMC-kjZPWBkyRuUeSMp4M
            #  交由客户端保管机制
            # {"user":"alexander.dsb.li"}
          # Flask理念 - 一切从简 为服务器减轻压力
            return render_template("login.html")
        else:
            user_info = request.form.to_dict()
            session["user"] = user_info.get("username")
            print(session.get("user"))
            return "login OK!"
    
    if __name__ == ‘__main__‘:
        app.run("0.0.0.0",9527)

5.2 Flask中的路由

  1. @app.route()

    • 参数:

      • rule:路由地址,例,"/login"
      • methods:允许进入视图函数的请求方式
      • endpoint:mapping ,路由地址和endpoint之间的mapping,路由地址和视图函数之间还有一个mapping,注意,endpoint在同一个app中不能出现重复,默认值是视图函数名
      • defaults:默认路由参数
      • strict_slashes:是否严格遵循路由匹配规则
      • redirect_to:永久重定向 ,308和301
    from flask import Flask,request,url_for
    # url_for用于对endpoint设置的名称的反向获取url
    app = Flask(__name__)
    
    @app.route("/a",methods=["GET","PoSt","options","abc"],endpoint="end_homea" ,defaults={"count":20},strict_slashes=True,redirect_to="/")
    def homea(count):
        # print(url_for("end_homea"))
        # 如果在蓝图里面,要使用url_for("蓝图标识".endpoint)
        # 如文件名为app01,则url_for("app01.end_homea")
        count = request.args.get("count",count)
        return f"200OK!{count}"
  2. 动态参数路由
    • 可以分页,获取文件,解决分类,解决正则路由问题
    from flask import Flask,request,url_for
    # url_for用于对endpoint设置的名称的反向获取url
    app = Flask(__name__)
    
    @app.route("/get_img/<filename>")
    def get_img(filename):
        filepath = os.path.join("img", filename)
        return send_file(filepath)
    
    # 访问地址 : http://127.0.0.1:5000/info/1
    @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info")
    def student_info(nid):
        print(url_for("r_info",nid=2))  # /info/2,反向url时也要加上参数
        return f"Hello Old boy {nid}"   # Python3.6的新特性 f"{变量名}"

5.3 Flask初始化配置

  • 常用配置:

    • template_folder:模板文件存放路径
    • static_folder:静态文件存放目录,默认值是static
    • static_url_path:静态文件访问路径,默认值是/ + static_folder
    from flask import Flask
    app = Flask(__name__,template_folder="templatess",
                static_folder="static",static_url_path="/futeng")
    # static == 你的家
    # /static == 回家路
    # /futeng == 富腾路

5.4 Flask实例配置

  • Flask实例配置,即app的配置,可以在app.default_config中查看

    # 在setting.py中,写配置对应的类
    import hashlib,time
    class DebugConfig(object):
        DEBUG = True
        SECRET_KEY = "#$%^&*($#$%^&*%$#$%^&*^%$#$%"
        PERMANENT_SESSION_LIFETIME = 3600
        SESSION_COOKIE_NAME = "I am Not Session"
    
    class TestConfig(object):
        TESTING = True
        SECRET_KEY = hashlib.md5(f"{time.time()}#$%^&*($#$%^&*%$#$%^&*^%$#$%{time.time()}".encode("utf8")).hexdigest()
        PERMANENT_SESSION_LIFETIME = 360000
        SESSION_COOKIE_NAME = "#$%^&*($#$%^&*%$#$%^&*^%$#$%"
    
    # 在可执行文件中,调用
    from flask import Flask
    from setting import DebugConfig,TestConfig
    
    app = Flask(__name__)
    app.config.from_object(DebugConfig)
    # 或 app.config.from_object(TestConfig)

5.5 Flask的蓝图

  • 蓝图:Blueprint,不能被run的Flask实例 不存在Config

    # 在可执行文件中,调用
    from flask import Flask
    from users import bp
    from car_users import car_bp
    
    app = Flask(__name__)
    app.register_blueprint(bp)
    app.register_blueprint(car_bp)

    from flask import Blueprint
    bp = Blueprint("app01",__name__)
    
    @bp.route("/add_user")
    def add_user():
        return "添加用户"
    
    @bp.route("/find_user")
    def find_user():
        return "查看用户"
    
    @bp.route("/drop_user")
    def drop_user():
        return "删除用户"
    
    @bp.route("/up_user")
    def up_user():
        return "修改用户"

    users.py

    from flask import Blueprint
    car_bp = Blueprint("app02",__name__,url_prefix="/car")
    # 参数url_prefix是为了区分users.py和car_users.py中的相同路由
    # 使用url_prefix后,访问时,要在原来路由前加上url_prefix设置的路径
    
    @car_bp.route("/add_user")
    def add_user():
        return "car添加用户"
    
    @car_bp.route("/find_user")
    def find_user():
        return "car查看用户"
    
    @car_bp.route("/drop_user")
    def drop_user():
        return "car删除用户"
    
    @car_bp.route("/up_user")
    def up_user():
        print("I am vf")
        return "car修改用户"

    car_users.py

5.6 Flask特殊装饰器

  • @app.before_request:在请求进入视图函数之前 做出处理
  • @app.after_request:在响应返回客户端之前,结束视图函数之后
    @app.before_request
    def be1():
        print("I am Be1")
        # return "第一个请求你就过不去"  # 5种
    
    @app.before_request
    def be2():
        print("I am Be2")
        # return "第二个请求过不去了" # 5种
    
    @app.after_request
    def af1(res):
        print("I am af1")
        return res
    
    @app.after_request
    def af2(res):
        print("I am af2")
        return res
    • be+af 请求生命周期:

      • 正常:be1 -> be2 -> vf -> af2 -> af1
      • 异常:be1 -> af2 -> af1
      • 注意:只要有响应返回 , af全部执行
  • @app.errorhandler:重定义错误页面
    @app.errorhandler(404)        # 只能是 4xx,5xx
    def error404(error_message):
        print(error_message)
        # return redirect("https://www.autohome.com.cn/111232130/#pvareaid=33112794r5454") # 5种类型

5.7 Flask的CBV

  • CBV:继承 MethodView 让当前的 class 可以成为视图类

    from flask import Flask
    app = Flask(__name__)
    
    class Login(views.MethodView):
        def get(self,*args,**kwargs):
            return "I am Get"
    
        def post(self,*args,**kwargs):
            pass
    
    app.add_url_rule("/login",
                     endpoint="my_login",    # 可以不设置endpoint
                     view_func=Login.as_view(name="loginlogin")   # view_func.__name__="loginlogin"
                    )

六、Flask组件:Flask-Session

  1. 下载:pip install Flask-Session
  2. 使用:
    from flask import Flask
    from flask_session import Session
    from redis import Redis
    
    app = Flask(__name__)
    app.config["SESSION_TYPE"] = "redis"
    app.config["SESSION_REDIS"] = Redis(host="192.168.12.87",port=6379,db=10)
    # app.session_interface # Flask 利用 session_interface 选择 Session 的存放位置 和 存放机制
    # app.default_config
    
    Session(app)    

七、Flask总结

  1. 下载:pip install Flask
  2. 启动:
    from flask import Flask
    app = Flask(__name__)
    app.run("0.0.0.0",9527)        # 监听地址 监听端口
  3. response
    # Flask 三剑客
    return ""    # 返回字符串 HTTPResponse
    return render_template("index.html")    # 返回模板 依赖MarkupSafe 默认存放路径:templates
    return redirect("/login")    # 响应头中加入 - location:重定向
    # Flask 特殊Response
    return send_file(文件名称或文件路径+文件名称)    # 打开并返回文件内容 自动识别文件类型 响应头 Content-Type:文件类型
    return jsonify({k:v})    # 返回标准格式的json字符串 响应头中 Content-Type:application/json
    # Flask 1.1.1中直接返回dict 本质上是在运行jsonify
  4. request
    from flask import request
    request.form  # 获取FormData中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError
    request.args  # 获取URL中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError
    request.json  # 请求头中 Content-Type:application/json 存放字典
    request.data  # 请求头中 Content-Type没有Form FormData 存放bytes类型
    
    request.method  # 请求方式
    request.cookies  # 浏览器的cookie键值对
    request.headers  # 请求头
    request.path  # 路由地址 请求地址
    request.url  # 请求全部地址
    request.host  # 主机位
  5. session 交由客户端保管机制 保管一串儿加密字符串
    from flask import session
    app.config["SECRET_KEY"] = "@#$%^&*(()*&^%$#*&^&%^&*)"
    session["key"] = "value"
  6. 路由配置 route
    @app.route(
        rule,    # 路由地址
        endpoint,    # mapping 名称对应路由地址 url_for 反向推算路由地址 url_for(endpoint)
        methods,    # 允许进入视图函数的 请求方式
        redirect_to,    # 永久重定向 301 308
        strict_slashes,    # 是否严格遵循路由匹配规则
        defalut,    # 默认参数
    )
    
    @app.add_url_rule(
        rule,
        view_func,    # 视图函数
    )
    
    # 动态参数路由
    @app.route("/get_file/<filename>")
    def get_file(filename):
        pass
  7. 初始化配置
    Flask(__name__,
          template_folder,    # 模板存放文件
          static_folder,    # 静态文件存放目录
          static_url_path,    # 静态文件访问路径
         )
  8. config
    class DebugConfig(object):
        DEBUG = True
        ...
    
    app.config.from_object(DebugConfig)
  9. 蓝图 Blueprint 当成不能被 run 的 Flask 实例
    from flask import Blueprint
    bp = Blueprint("bp",__name__,
                  url_perfix,    # url前缀
                  )
    @bp.route("/mybp",endpont="666")
    @bp.add_url_rule
    @bp.before_request
    @bp.after_request
    @bp.errorhandler(http错误码)
    
    url_for("蓝图标识".endpoint)
    url_for("bp.666")
    
    app.register_blueprint(bp)
  10. 特殊装饰器
    @app.before_request    # 在请求进入视图函数之前,做出处理
    @app.after_request    # 在结束视图函数之后,响应客户端之前
    @app.errorhandler(http错误码)    # 重定向错误信息
    def error404(errorMessage):
        pass
  11. CBV
    from flask import views
    class Login(views.MethodView):
        def get(self,*args,**kwargs):
            pass
    app.add_url_rule("/login",endpoint=None,view_func=Login.as_view(name="loginlogin"))
    # name是当前视图函数名,必须唯一,因为他是endpoint

原文地址:https://www.cnblogs.com/zengyi1995/p/11322989.html

时间: 2024-10-13 15:09:46

浅谈 Flask 框架的相关文章

浅谈数据库框架,见笑,请多指正

浅谈数据库框架,见笑,请多指正 http://weibo.com/p/1001603724746155003486 一友说"插件式存储又割裂了SQL引擎的完整逻辑...总体而言在现有框架下MySQL的优化器没有多大改进的价值". 我们且做个技术分析: 1 插件式框架,可以静态/动态加载组件,方便在同类不同属家的模块间切换,这种设计是良好的. 很多软件的设计都采用了"微内核+插件"这样的方式构筑了强大的应用.如Ecplise生态圈. 2 数据库范围内, MySQL的属

2014-07-29 浅谈MVC框架中Razor与ASPX视图引擎

今天是在吾索实习的第15天.随着准备工作的完善,我们小组将逐步开始手机端BBS的开发,而且我们将计划使用MVC框架进行该系统的开发.虽然我们对MVC框架并不是非常熟悉,或许这会降低我们开发该系统的效率,但是我们可以通过边学边做的方式来实现其开发的.这不仅便于我们日后对系统的管理与维护,而且还给我们带来一个学习的动力与实践的地方. 但我们在创建一个基于MVC框架的项目时,就遇到一些问题了.那就是MVC的视图引擎是有两种的,一种是Razor,会以cshtml后缀的文件作为视图文件:另一种是ASPX,

Flask解读 --- 浅谈Flask基本工作流程_1

网站写了蛮长时间了,一直想具体分析分析Flask的运行机制,但是源码看得断断续续,不过最近状态不错,进度上来了点,这里先新建一个类别,专门来说说Flask和源码有关系的内容, 这篇准备粗略说一下应用Flask框架的时候,从HTTP请求开始到响应的流程 前置技能   ---   WSGI 在具体读源码之前,这里先需要说一个概念,什么是WSGI. WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,

浅谈angular框架

最近新接触了一个js框架angular,这个框架有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入,以上这些全部都是属于angular特性,虽然说它的功能十分的强大,在开发中非常的实用,对于初接触者来说,要想熟练使用仍然需要一些时间来熟悉其中基础的语法规则,一些基本的用法,虽然说代码不需要死记硬背,但是对于新学习一个知识点,记住其中的一些基础概念,基本的用法,在这里不是提倡大家去背一些代码,该处谈到的熟悉指的是自己多动手去写,实在是想不起了再查看资料,尽量凭借

浅谈 Web框架

一.Web框架本质 所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端 二.Web框架功能 socket收发消息 —— wsgiref(测试).uwsgi(线上) 根据不同的路径返回不同的字符串 返回动态页面(字符串的替换)—— jinja2 三.Web框架种类 django 根据不同的路径返回不同的字符串 返回动态页面(字符串的替换) flask 根据不同的路径返回不同的字符串 tornado socket收发消息 根据不同的路径返回不同的字符串 返回动

[.NET] 浅谈可扩展性框架:MEF

之前在使用Prism框架时接触到了可扩展性框架MEF(Managed Extensibility Framework),体验到MEF带来的极大的便利性与可扩展性. 此篇将编写一个可组合的应用程序,帮助大家快速熟悉MEF并将其应用于实际项目中. 有关MEF中的名词含义及功能实现,请大家移步:火车票 介绍下将要编写的Demo程序(下图),使用winform开发. 通过组合操作,程序动态加载可用部件进行组合操作. 通过解体操作,程序卸载所加载的所有部件. 新建项目后需引用程序集: System.Com

浅谈MVC框架设计

为什么要设计框架? 在开发的过程中为了方便管理资源,统一一定的规范,所以采用使用框架,为了以后项目的延伸和拓展,比如我们的代码其实都可以在一个方法内进行,而且也不需要相互调用也许会更快吧?但是要是发生错误呢 ?好不容易执行的最后一行结果突然发生错误了,很难受不是  题外话,但是也可以感受到一个好的设计会给我们带来很大的方便 题外话继续 :开发过程中,我们前后台之间相互关联,有一个项目的改动就需要一起协调,这还是一个小功能,要是同时进行一次开发,那就难免深受其累了,怎么样才能方便管理.前后台分离呢

浅谈SpringMVC4 框架技术

为什么要使用SpringMVC 基于MVC架构:(SpringMVC 也叫 Spring web mvc.是 Spring 框架的一部分,是在 Spring3.0 后发布的) 基于MVC架构,功能分工明确,解耦合. 容易理解,上手快,使用简单 开发一个注解的springMVC项目,SpringMVC 也是轻量级的,jar 很小.不依赖的 特定的接口和类. SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解.方便灵活 作 为 Spring 框 架 一 部 分 ,

浅谈跨平台框架 Flutter 的优势与结构

作者:个推iOS工程师 伊泽瑞尔 一.背景 目前,移动开发技术主要分为原生开发和跨平台开发两种.其中,原生应用是指在某个特定的移动平台上,使用平台所支持的开发工具和语言,直接调用系统提供的API所开发的应用.原生开发的主要优势体现在:1.可以快速访问本平台的全部功能,比如摄像头.GPS等:2.原生应用的速度快.性能高,而且可以实现比较复杂的动画和绘制效果,用户体验较好.原生开发的缺点也很明显,主要体现在:1.开发成本较高,不同的平台必须维护不同的代码,人力成本也会随之增加:2.有新的功能需要更新