首页 文章 精选 留言 我的

精选列表

搜索[服务],共10000篇文章
优秀的个人博客,低调大师

Scala微服务架构 三

四 Controller层 之前我们已经把基层架构搭建好了,那么要如何使用呢? 首先看看我的Controller层代码 @Singleton class BMAuthController @Inject()(implicit cc: ControllerComponents, actorSystem: ActorSystem) extends AbstractController(cc) with Circe with CirceJsonapiSupport { val entry = PlayEntry() def parseJson(jsonString: String) : Json = io.circe.parser.parse(jsonString).right.get def decodeJson[T](json: Json)(implicit d: io.circe.Decoder[T]) : T = json.as[T].right.get def login = Action(circe.json[RootObject]) { implicit request => request import model.request.requestsJsonApiOpt.requestsJsonapiRootObjectReader._ val tt = fromJsonapi(request.body) val reVal = entry.commonExcution( SequenceSteps(testStep(tt.reqs.head) :: Nil, None)) val ctest = company("12", "alfred") val ctestj = asJsonApi(ctest) println(ctestj) val result = asJsonApiResult(reVal.asInstanceOf[userdetailresult]) Ok(result.asJson) } } 4.1 Controller 的声明 4.1.1 @Inject() 注解 @Inject()(implicit cc: ControllerComponents, actorSystem: ActorSystem) 首先这个@Inject会查询Play的System环境,并将查到的==单例实例==注入到参数中. 4.1.2 AbstractController 特质 官网解释 AbstractController: an abstract class extending BaseController with a ControllerComponents constructor parameter that can be injected using constructor injection. 也就是说,这就是BaseController的抽象子类,但是要带有一个ControllerComponents作为构造函数. 4.1.3 Circe 特质 circe是一个Scala的Json解析库.并且目前已经支持Play. Play的使用方式很简单,首先引入项目: libraryDependencies += "com.dripower" %% "play-circe" % "2609.1" 然后继承play.api.libs.circe.Circe特质 使用起来也很简单,上面代码的circe.json[RootObject] 部分就是在使用Circe解析JsonApi的Root部分. 4.1.4 CirceJsonapiSupport 特质 对JsonApi协议的支持,里面主要就是两个隐式,代码如下: trait CirceJsonapiSupport extends CirceJsonapiEncoders with CirceJsonapiDecoders { implicit val circeJsonapiMarshaller = Marshaller.delegate[RootObject, String]( `application/vnd.api+json`, `application/json`, ContentTypes.`application/json` )(_.asJson.noSpaces) implicit val circeJsonapiUnmarshaller = Unmarshaller.delegate[String, RootObject]( `application/vnd.api+json`, `application/json` )(decode[RootObject](_).right.get) } object CirceJsonapiSupport extends CirceJsonapiSupport 4.2 login 代码解析 对不起,各位,暂时没写!!! ,有兴趣的可以私信我,后期我会补上. ※. 本期语法糖 ※.1 Scala的构造函数 学自https://www.w3cschool.cn/scala/scala-constructors.html 今天要说的当然不是大家熟知的构造函数,而是以前我们可能忽略的细节. class User1(var id: String, var name: String) class User2(val id: String, val name: String) class User3(private var id: String,private var name: String) class User4(id: String, name: String) 上面代码定义了四个User类,每个类都有两个参数id和name,当然,他们构造函数的区别也很明显.那么这几种不同的定义方式,有什么区别呢? User1,定义了可变构造参数,同时编译器会自动生成setter和getter方法,但因为是默认修饰符,所以外部可以直接访问user1.id,或者赋值user1.id("001") User2,因为定义的构造参数是不可变的,所以只会生成getter方法,不会有setter方法,也是默认修饰符,所以外部只可以访问user2.id,无法赋值 User3,和User1很像,但是修饰符改为private,所以即便是var的构造参数,也不会生成getter方法和setter方法 User4,我们最常用的写法,其实就是private[this] val,也就是说对于自己和伴生类而言是可见的 应杨总要求,我们打印上面四个User类的编译信息 def tree1 = reify { class User1(var id: String, var name: String) }.tree def tree2 = reify { class User2(val id: String, val name: String) }.tree def tree3 = reify { class User3(private var id: String,private var name: String) }.tree def tree4 = reify { class User4(id: String, name: String) }.tree 然后分别打印上面的四个树,输出结果如下: tree1: { class User1 extends AnyRef { <paramaccessor> var id: Predef.String = _; <paramaccessor> var name: Predef.String = _; def <init>(id: Predef.String, name: Predef.String) = { super.<init>(); () } }; () } tree2: { class User2 extends AnyRef { <paramaccessor> val id: Predef.String = _; <paramaccessor> val name: Predef.String = _; def <init>(id: Predef.String, name: Predef.String) = { super.<init>(); () } }; () } tree3: { class User3 extends AnyRef { <paramaccessor> private var id: Predef.String = _; <paramaccessor> private var name: Predef.String = _; def <init>(id: Predef.String, name: Predef.String) = { super.<init>(); () } }; () } tree4: { class User3 extends AnyRef { <paramaccessor> private[this] val id: Predef.String = _; <paramaccessor> private[this] val name: Predef.String = _; def <init>(id: Predef.String, name: Predef.String) = { super.<init>(); () } }; () }

优秀的个人博客,低调大师

Scala微服务架构 二

三. Scala的Macro(宏) Scala Macros对scala函数库编程人员来说是一项不可或缺的编程工具,可以通过它来解决一些用普通编程或者类层次编程(type level programming)都无法解决的问题,这是因为Scala Macros可以直接对程序进行修改。 说到对程序进行修改,几个概念一定要先理解,"编译期"和"运行期",Java也有一个可以修改程序的功能,大家一定用过,就是反射.不是在运行期,编译器也不知道接下来会发生什么,会执行哪些代码(这就是==动态性==). 而scala是java的衍生语言,自然也有反射,而且它还有一种更高级的反射,就是编译时反射,它就是宏. 3.1 什么是宏? 一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常是字符串,或者是类,方法等)。这种替换在预编译时进行,称作宏展开。 通过上面的定义,感觉和C的宏概念差不多.但C的宏只不过是一段语法的替换,然而Scala的宏却可以通过表达式树控制一节代码(类,或者方法)的生成。获得了控制代码的执行顺序(见惰性计算和非限制函数)的能力,使得新创建的语法结构与语言内建的语法结构不可区分。 宏,从程序抽象的角度来看,可能不太容易调试和维护,但是能够很强大的固定我们的设计. 同时使用宏能够==大量==的减少样板代码.比如Scala的assert和require就是使用宏实现的. 3.2 宏出现的意义? 编译期元编程 更完善的错误检查 3.2.1 编译期元编程 什么是元编程? 百度词条的一句话: 元编程(Metaprogramming)是指某类计算机程序的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,==或者在运行时完成部分本应在编译时完成的工作==。很多情况下与手工编写全部代码相比工作效率更高。编写元程序的语言称之为元语言,被操作的语言称之为目标语言。==一门语言同时也是自身的元语言的能力称之为反射==。 元编程是用来产生代码的程序,操纵代码的程序,在运行时创建和修改代码而非编程时,这种程序叫做元程序。而编写这种程序就叫做元编程。 所以,元编程技术在多种编程语言中都可以使用,但更多的还是被应用于动态语言中,因为动态语言提供了更多的在运行时将代码视为数据进行操纵的能力。 虽然静态语言也支持元编程(反射机制),但是仍然没有诸如Ruby这样的更趋动态性的语言那么透明,这是因为静态语言在运行时其代码和数据是分布在两个层次上的。 最后可以理解为,元编程就是程序可以操作更小的粒度和动作. 3.2.2 更完善的错误检查 引自知乎https://www.zhihu.com/question/27685977/answer/38014170 首先思考一个问题:如果你的应用程序有bug,那么你希望在什么情况下发现呢? 编译时:这是最理想的状态,如果一个bug可以通过编译器检查出来,那么程序员可以在第一时间发现问题,基本上就是一边写一边fix。这也正是静态编译型语言的强大优势。 单元测试:没有那么理想但是也不差。每写完一段code跑一下测试,看看有没有新的bug出来。对于scala来说,现在的工具链已经不错了,左屏sbt > ~test,右屏写代码惬意得很。 运行时:这个就比较糟糕了。运行时才报错意味着你得首先打包部署,这个时间开销通常就比较大,而且在许多公司,你还要时不时的解决环境问题,很是让人抓狂。 而Scala的宏,就是可以将一些运行期才会出现的错误,在编译器暴露出来. 3.3 宏的编译过程? Scala是如何编译宏的呢? 引用自https://www.cnblogs.com/tiger-xc/p/6112143.html 明白了上面的流程之后,我们出个栗子: object modules { greeting("john") } object mmacros { def greeting(person: String): Unit = macro greetingMacro def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = { import c.universe._ println("compiling greeting ...") val now = reify {new Date().toString} reify { println("Hello " + person.splice + ", the time is: " + new Date().toString) } } } 以上代码的执行逻辑如下: 注意编译器在运算greetingMacro时会以AST方式将参数person传入。由于在编译modules对象时需要运算greetingMacro函数,所以greetingMacro函数乃至整个mmacros对象必须是已编译状态,这就意味着modules和mmacros必须分别在不同的源代码文件里,而且还要确保在编译modules前先完成对mmacros的编译. 3.4 编写宏实现 其实宏的使编写并不难,api已经帮我们做好了一切,我们只要关注如何使用获取宏参数和宏的返回值即可. 上面栗子中的代码,greetingMacro方法就是一个最简单的宏实现,代码如下: def greetingMacro(c: Context)(person: c.Expr[String]): c.Expr[Unit] = { import c.universe._ println("compiling greeting ...") val now = reify {new Date().toString} reify { println("Hello " + person.splice + ", the time is: " + new Date().toString) } } 但是想要实现更多的功能,还需要更加深入的学习Scala的宏和表达式树. ※. 本期语法糖 ※.1 implicit macor (隐式宏) 官方文档 开局出个栗子 trait Showable[T] { def show(x: T): String } def show[T](x: T)(implicit s: Showable[T]) = s.show(x) implicit object IntShowable extends Showable[Int] { def show(x: Int) = x.toString } show(42) // return "42" show("42") // compilation error 可以调用成功show(),主要因为名称空间存在Showable的子类IntShowable,并且是implicit object,这个implicit object的作用上一篇已经讲过了,就不说了. 上面代码,乍一看还可以,但是如果扩展起来就不是很舒服了,如果要让show("42")也可用,我们就需要添加如下代码: implicit object StringShowable extends Showable[String] { def show(x: String) = x } ※.2 宏注解 Macro Annotations ==> @compileTimeOnly("") 官方文档 开局处个栗子,可以自动为case class或class在编译时生成一个名为TempLog的方法. import scala.reflect.macros.Context import scala.language.experimental.macros import scala.annotation.StaticAnnotation import scala.annotation.compileTimeOnly @compileTimeOnly("temp log print") class AnPrint(msg: Any*) extends StaticAnnotation { def macroTransform(annottees : Any*) : Any = macro AnPrintMacroImpl.impl } 官网栗子,我们的代码也比较常见,继承了StaticAnnotation,表示这是一个注解类,有兴趣的朋友可以看看上一期文章. 主要说的是上面 @compileTimeOnly("temp log print") 官网解释 First of all, note the @compileTimeOnly annotation. It is not mandatory, but is recommended to avoid confusion 首先,这不是强制性的,即便不写,也会被编译器自动扩展上.但还是建议加上避免混乱. 然后是宏的具体实现,如下: object AnPrintMacroImpl { def impl(c : whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { import c.universe._ val tree = annottees.map(_.tree).toList.head val (className, fields, parents, body) = tree match{ case q"case class $className(..$fields) extends ..$parents { ..$body }" => (className, fields, parents, body) case q"class $className(..$fields) extends ..$parents { ..$body }" => (className, fields, parents, body) } //TempLog val LogDefName = TermName("TempLog") val LogDefImpl = q"""def $LogDefName(sss: Any):Unit=println(" ===> " + sss)""" val out = q""" case class $className(..$fields) extends ..$parents { ..$LogDefImpl ..$body } """ println(showRaw(tree)) c.Expr(out) } } 里面的具体细节,主要是宏将类变成AST,然后利用模式匹配,来解析类信息,之后可以加入自己定义的任何操作,最后用Block封装起来,这样子,一个简单的宏就实现了. 我们测试一下: package myTest @AnPrint("clock") class ccc(val a: String = "aaa", val b: String = "bbb"){ TempLog("init b") } object annotationPrintTest extends App { println("start") val a = new b("aiyou", "wolegequ") a.TempLog("打印我了") println("end") } 注意,这里需要先编译AnPrintMacroImpl和AnPrint文件,才可以测试通过 打印结果如下: start ===> init b ===> 打印我了 end

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册