切面编程思想
本片文章中,我们首先要了解切面编程思想,那么什么是切面编程思想呢
切面编程思想(AOP,Aspect-Oriented Programming) 是一种通过横向切割代码逻辑来解决横切关注点(Cross-Cutting Concerns)的编程范式。它的核心目标是解耦业务逻辑与非功能性需求(如日志、事务、权限等),避免代码重复和分散。
核心思想
-
横切关注点
系统中多个模块都需要用到的功能(如日志、事务、安全校验),这些功能通常分散在各个业务代码中,导致代码重复和耦合度高。 -
分离关注点
AOP 将横切关注点从业务逻辑中剥离出来,定义为独立的模块(称为切面),并通过动态织入的方式将这些模块注入到目标代码中。
核心概念
-
切面(Aspect)
封装横切关注点的模块(例如一个日志工具类)。 -
连接点(Join Point)
程序执行过程中的某个点(如方法调用、异常抛出)。 -
通知(Advice)
切面在特定连接点执行的动作,分为:-
@Before
:方法执行前 -
@After
:方法执行后(无论成功或失败) -
@AfterReturning
:方法成功返回后 -
@AfterThrowing
:方法抛出异常后 -
@Around
:包裹目标方法,可控制其执行
-
-
切点(Pointcut)
通过表达式定义哪些连接点需要被切面处理(例如匹配所有UserService
类的方法)。 -
织入(Weaving)
将切面代码动态合并到目标对象的过程,可通过编译期、类加载期或运行时代理实现。
AOP 的实现方式
-
动态代理(JDK/CGLIB)
-
JDK 动态代理:基于接口,运行时生成代理类。
-
CGLIB 代理:基于继承,可代理无接口的类。
-
-
字节码增强(如 AspectJ)
在编译期或类加载期修改字节码,直接植入切面逻辑。 -
框架支持
-
Spring AOP:基于动态代理,适合轻量级应用。
-
AspectJ:功能更强大,支持编译时织入。
-
典型应用场景
-
日志记录
自动记录方法入参、返回值、执行时间。 -
事务管理
通过@Transactional
注解自动开启/提交/回滚事务。 -
权限校验
在方法调用前检查用户权限。 -
性能监控
统计方法执行耗时。 -
异常处理
统一捕获异常并转换为友好提示。
简单来说,切面就是切方法,在进入主方法之前,进行前置控制
切面的基本构成
1.前置切面
2.环绕切面
3.异常通知
4.后置切面
5.最终切面
切面编程的体现,通过函数作为另外一个方法的参数
参考代码如下
package cj5.chapter1
//方法
public func before(a: Int64): Bool {
if (a > 18) {
true
} else {
false
}
}
//主方法
public func execAction() {
println("你才可以去聊天")
}
public func execBank(){
println("你才可以去做管理员")
}
public func execFun2(a: Int64, be: (a: Int64) -> Bool, action: () -> Unit) {
//切面编程思想 切面就是切方法,在进入主方法之前,进行前置控制
//1. 前置切面
// 2.环绕切面
//3.异常通知
// 4.后置切面
//5.最终切面
if (be(a)) {
action()
} else {
println("你没有能力去执行")
}
}
public func call(){
execFun2(22,before,execAction)
execFun2(18,before,execBank)
}
重点要去理解 切面的各个部分分别承担着什么样的任务,简单来说就是以下的五个方面
//1. 前置切面 权限控制
// 2.环绕切面 缓存机制 redis
//3. 异常通知 操作的文图
// 4.后置切面 日志
//5.最终切面 总结
package cj5.chapter15
public func add(a: Int64, b: Int64): Any {
a + b
}
public func sub(a: Int64, b: Int64): Any {
a - b
}
public func exec(op:String): (Int64, Int64) -> Any {
var any :(a:Int64,b:Int64)->Any = add
if(op =="+")
{
add
}
else if(op =="-")
{
return sub
}
any
}
public func call()
{
var result = exec("-")(100,200)
let re= (result as Int64).getOrThrow()
println("${re}")
}
函数类型: (参数)-> 返回值类型
var f1 : (参数)->返回值类型 = 方法名
lamdba表达式
lamdba表达式,就是匿名函数,所谓的匿名函数,就是没有名字的函数,就是函数的具体实现
var f1 : ( )->Unit = 没有名字的方法(lamdba表达式)
package cj5.chapter16
public func exec1()
{
var f1: ()->Unit = { =>println("这是lamdba表达式")}
f1()
}
public func exec2()
{
//立即调用
{ =>println("这是lamdba表达式")}()
}
public func exec3(){
var f2:(Int64,Int64)->Int64 = {a:Int64,b:Int64 => a+b}
f2(30,40)
{a:Int64,b:Int64 =>println("${a+b}")}(10,20)
}
接下来,我们学习仓颉和鸿蒙的联合,即仓颉鸿蒙,开发工具,用的就是之前开发鸿蒙时候写ArkTS语言时候用的DevEco Studio。第一部就是要建一个仓颉鸿蒙的工程,如下图所示
首先学习如何在仓颉鸿蒙中添加一个可以点击按钮,并且按下按钮之后,程序会有所反应,实现代码如下
package ohos_app_cangjie_entry
internal import ohos.base.*
internal import ohos.component.*
internal import ohos.state_manage.*
import ohos.state_macro_manage.*
@Entry
@Component
class Chapter1View{
func build(){
Column(){
Button("点击").shape(ShapeType.Normal).onClick({event=>
AppLog.info("仓颉鸿蒙,您好")})
}.width(100.percent).height(100.percent)
}
}
下面就是上述代码实现的效果图
lambda表达式做为方法的参数
package cj5.chapter16
public func execOne(a:Int64,b:Int64,op:(Int64,Int64)->Int64):Int64{
op(a,b)
}
public func call()
{
execOne(10,20, {a,b =>a*b})
}
lamdba表达式的参数和返回类型和函数类型保持一致
package cj5.chapter3
public func op():(String,String)->String{
{a,b=>a+b}
}
public func call()
{
op()("10","20")
}
package cj5.chapter4
public func Outer(){
func Inner()
{
println("这是内部函数Inner")
}
Inner
}
public func call()
{
let fn = Outer()
fn()
}
package cj5.chapter5
public func Outer(flag:Bool):(Int64,Int64)->Int64
{
if(true)
{
{a,b=>a+b}
}
else{
{a,b=>a-b}
}
}
上面举得三个例子已经将lamdba表达式的参数和返回类型和函数类型保持一致这句话解释的很清楚了,需要自己好好的体会体会。
仓颉的枚举类型
仓颉类型:
1. 基本数据类型 值类型: Int、String、 struct类型
2. 引用类型 类,接口和枚举类型
枚举类型是个固定的取值类型
package cj5.chapter6
public enum Famialy {
| FATHER | MOTHER | SON // 枚举类型的值必须大写
//枚举类型可以有实例函数和静态函数,不能有构造函数和属性设计器
public func execFamialy() {
println("枚举类型的函数")
}
}
public func call() {
var f = Famialy.FATHER
f.execFamialy()
}
package cjdemo5.chapter22
public interface A{
}
//一个枚举类型可以继承一个接口
public enum Values <: A{
|FATHER|MONTHER
}
public struct User <:A {
}
package cj5.chapter8
public enum Fam {
| FATHER | MONTHER | SON
}
public func execEnum1(v: Fam): Unit {
match (v) {
case FATHER => println("这是群主")
case MONTHER => println("这是管理员")
case SON => println("这是群友")
}
}
public func execEnum2(v: Fam): String {
let result = match (v) {
case FATHER => "群主"
case MONTHER => "管理员"
case SON => "群友"
}
result
}
好好体会仓颉的枚举类型以及切面编程的思想,也要尝试尝试第一次编写仓颉和鸿蒙的联合,好好体会,你会有新的发现的。