重构使用aop的方式记录异常日志-引入Spring AOP

This commit is contained in:
2021-03-22 21:36:14 +08:00
parent 0b5d9243e8
commit af9a4a86a1
11 changed files with 144 additions and 73 deletions

View File

@@ -20,6 +20,18 @@ interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
var service: IService<TKey>?
@DeleteMapping("/{id:.+}")
fun remove(@PathVariable id: TKey): HttpMessage {
return this.safeExecute("删除${this.name}[id: $id]失败") {
if (this.service != null)
it.data = this.service!!.remove(id)
else {
it.status = Status.Error
it.message = "没有对应服务,无法执行该操作"
}
}
}
@PostMapping
fun add(@RequestBody command: TCommand): HttpMessage {
return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
@@ -33,18 +45,6 @@ interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
}
}
@DeleteMapping("/{id:.+}")
fun remove(@PathVariable id: TKey): HttpMessage {
return this.safeExecute("删除${this.name}[id: $id]失败") {
if (this.service != null)
it.data = this.service!!.remove(id)
else {
it.status = Status.Error
it.message = "没有对应服务,无法执行该操作"
}
}
}
@PutMapping("/{id:.+}")
fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage {
return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {

View File

@@ -11,24 +11,21 @@ import org.springframework.stereotype.Component
import java.io.File
@Component
class EmailMessenger : IEmailMessenger {
class EmailMessenger(var mailSender: JavaMailSender?) : IEmailMessenger {
@Autowired
private lateinit var mailSender: JavaMailSender
@Value("\${spring.mail.host}")
@Value("\${spring.mail.host:}")
var host = ""
@Value("\${spring.mail.port}")
@Value("\${spring.mail.port:}")
var port = ""
@Value("\${spring.mail.sender}")
@Value("\${spring.mail.sender:}")
var sender = ""
@Value("\${spring.mail.username}")
@Value("\${spring.mail.username:}")
var username = ""
@Value("\${spring.mail.password}")
@Value("\${spring.mail.password:}")
var password = ""
@Autowired
@@ -42,13 +39,20 @@ class EmailMessenger : IEmailMessenger {
* @param receivers 邮件接受者
* @param files 附件
*/
override fun sendMessage(subject: String, content: String, receivers: List<String>, files: Map<String, String>): Map<String, Boolean> {
override fun sendMessage(
subject: String,
content: String,
receivers: List<String>,
files: Map<String, String>
): Map<String, Boolean> {
val result = mutableMapOf<String, Boolean>()
this.check()
receivers.forEach { receiver ->
result[receiver] = true
if (this.mailSender == null)
throw Exception("没有配置JavaMailSender的实现实例请重新配置")
try {
val mail = mailSender.createMimeMessage()
val mail = this.mailSender!!.createMimeMessage()
val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8")
mimeMessageHelper.setFrom(sender)
mimeMessageHelper.setTo(receiver)
@@ -59,7 +63,7 @@ class EmailMessenger : IEmailMessenger {
val file = FileSystemResource(File(path))
mimeMessageHelper.addAttachment(name, file)
}
mailSender.send(mail) //发送
this.mailSender!!.send(mail) //发送
} catch (e: Exception) {
logger.error(e, "发送邮件[$subject]至地址[$receiver]失败")
result[receiver] = false

View File

@@ -1,7 +0,0 @@
package com.synebula.gaea.app.component.aop
import java.lang.Exception
interface AnnotationHandler {
fun handle(clazz: Class<Any>, func: String, args: Array<Any>, exception: Exception?)
}

View File

@@ -1,68 +1,86 @@
package com.synebula.gaea.app.component.aop
import com.synebula.gaea.app.component.HttpMessage
import com.synebula.gaea.app.component.aop.annotation.SafeExec
import com.synebula.gaea.app.component.aop.annotation.ExceptionMessage
import com.synebula.gaea.app.component.aop.annotation.Handler
import com.synebula.gaea.app.component.aop.annotation.ModuleName
import com.synebula.gaea.data.message.Status
import com.synebula.gaea.log.ILogger
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.*
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.util.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import com.fasterxml.jackson.databind.ObjectMapper
@Aspect
@Component
class AppAspect {
private val mapper = ObjectMapper()
@Autowired
lateinit var logger: ILogger
@Autowired
lateinit var applicationContext: ApplicationContext
@Pointcut("within(com.synebula..app.*)")
fun log() {
}
//后置异常通知
@AfterThrowing("log()")
fun throws(point: JoinPoint): Any {
/**
* 后置异常通知
*/
@AfterThrowing("log()", throwing = "ex")
fun throws(point: JoinPoint, ex: Throwable) {
val clazz = point.signature.declaringType
println("${clazz.name} - ${point.signature.name}异常:${point.signature.name}")
return "error"
logger.error(
ex,
"${clazz.name}.${point.signature.name} exception${ex.message} args${mapper.writeValueAsString(point.args)}"
)
}
//后置最终通知,final增强不管是抛出异常或者正常退出都会执行
@After("log()")
fun after(point: JoinPoint) {
println("方法最后执行.....")
}
//环绕通知,环绕增强相当于MethodInterceptor
/**
* 环绕通知,环绕增强相当于MethodInterceptor
*/
@Around("log()")
fun around(point: ProceedingJoinPoint): Any? {
val clazz = point.signature.declaringType
val func = clazz.methods.find {
it.name == point.signature.name
}!!
val clazzAnnotations = clazz.annotations
val funcAnnotations = func.annotations ?: arrayOf()
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
val request = attributes.request
// 记录下请求内容
println("URL : " + request.requestURL.toString())
println("HTTP_METHOD : " + request.method)
println("IP : " + request.remoteAddr)
println("CLASS_METHOD : " + clazz.name + "." + point.signature.name)
println("ARGS : " + Arrays.toString(point.args))
var exceptionMessage = func.name
//遍历方法注解
for (funcAnnotation in funcAnnotations) {
val annotations = funcAnnotation.annotationClass.annotations
//尝试寻找方法注解的处理类
val handler = annotations.find { it is Handler }
if (handler != null && handler is Handler) {
val handleClazz = applicationContext.getBean(handler.value.java)
handleClazz.handle(clazz, func, point.args)
}
if (funcAnnotation is ExceptionMessage)
exceptionMessage = funcAnnotation.message
}
return try {
val res = point.proceed()
res
} catch (ex: Throwable) {
val msg = HttpMessage(Status.Success)
for (item in funcAnnotations) {
if (item.annotationClass == SafeExec::javaClass) {
}
//找到类的模块名称,否则使用类名
var moduleName = clazz.name
val name = clazz.annotations.find { it is ModuleName }
if (name != null && name is ModuleName) {
moduleName = name.value
}
println("方法${point.signature}异常: $msg - ${ex.message}")
return "error"
val message = "$moduleName - $exceptionMessage"
logger.error(ex, "$message, args: ${mapper.writeValueAsString(point.args)}")
return HttpMessage(Status.Error, message)
}
}
}

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation
import com.synebula.gaea.app.component.aop.handler.AccessLogHandler
@Target(AnnotationTarget.FUNCTION)
@Handler(AccessLogHandler::class)
@Retention(AnnotationRetention.RUNTIME)
annotation class AccessLog

View File

@@ -3,8 +3,8 @@ package com.synebula.gaea.app.component.aop.annotation
/**
* 标记方法安全执行由AOP负责try catch异常
*
* @param errorMessage 异常消息
* @param message 异常消息
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class SafeExec(val errorMessage: String)
annotation class ExceptionMessage(val message: String)

View File

@@ -1,5 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation
import com.synebula.gaea.app.component.aop.handler.AnnotationHandler
import kotlin.reflect.KClass
annotation class Handler(val value: KClass<Any>)
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Handler(val value: KClass<out AnnotationHandler>)

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation
/**
* 模块的业务名称
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ModuleName(val value: String)

View File

@@ -0,0 +1,29 @@
package com.synebula.gaea.app.component.aop.handler
import com.fasterxml.jackson.databind.ObjectMapper
import com.synebula.gaea.log.ILogger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.lang.reflect.Method
@Component
class AccessLogHandler : AnnotationHandler {
private val mapper = ObjectMapper()
@Autowired
lateinit var logger: ILogger
override fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception?) {
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
val request = attributes.request
logger.info(
"${request.method} ${request.requestURL} from ${request.remoteAddr}, call function ${clazz.name}.${func.name}, args: ${
mapper.writeValueAsString(
args
)
}"
)
}
}

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.handler
import java.lang.Exception
import java.lang.reflect.Method
interface AnnotationHandler {
fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception? = null)
}

View File

@@ -11,8 +11,8 @@ import com.synebula.gaea.query.type.Order
*/
data class Params(var page: Int = 1, var size: Int = 10) {
private var _parameters = mutableMapOf<String, Any>()
private var _orders = mutableMapOf<String, Order>()
private var _parameters = linkedMapOf<String, Any>()
private var _orders = linkedMapOf<String, Order>()
/**
* 数据索引从0开始。表示数据在总量的第几条。index = (page - 1) * size
@@ -24,13 +24,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
/**
* 排序条件。
*/
var orders: Map<String, Order>
var orders: LinkedHashMap<String, Order>
set(value) {
this._orders = value.toMutableMap()
this._orders = value
}
get() {
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
val params = mutableMapOf<String, Any>()
val params = linkedMapOf<String, Any>()
this._parameters.forEach {
if (it.key.startsWith("@")) {
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
@@ -45,13 +45,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
/**
* 查询条件。
*/
var parameters: Map<String, Any>
var parameters: LinkedHashMap<String, Any>
set(value) {
this._parameters = value.toMutableMap()
this._parameters = value
}
get() {
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
val params = mutableMapOf<String, Any>()
val params = linkedMapOf<String, Any>()
this._parameters.forEach {
if (it.key.startsWith("@")) {
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())