重构使用aop的方式记录异常日志-引入Spring AOP
This commit is contained in:
@@ -20,6 +20,18 @@ interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
|
|||||||
|
|
||||||
var service: IService<TKey>?
|
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
|
@PostMapping
|
||||||
fun add(@RequestBody command: TCommand): HttpMessage {
|
fun add(@RequestBody command: TCommand): HttpMessage {
|
||||||
return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
|
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:.+}")
|
@PutMapping("/{id:.+}")
|
||||||
fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage {
|
fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage {
|
||||||
return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
|
return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
|
||||||
|
|||||||
@@ -11,24 +11,21 @@ import org.springframework.stereotype.Component
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class EmailMessenger : IEmailMessenger {
|
class EmailMessenger(var mailSender: JavaMailSender?) : IEmailMessenger {
|
||||||
|
|
||||||
@Autowired
|
@Value("\${spring.mail.host:}")
|
||||||
private lateinit var mailSender: JavaMailSender
|
|
||||||
|
|
||||||
@Value("\${spring.mail.host}")
|
|
||||||
var host = ""
|
var host = ""
|
||||||
|
|
||||||
@Value("\${spring.mail.port}")
|
@Value("\${spring.mail.port:}")
|
||||||
var port = ""
|
var port = ""
|
||||||
|
|
||||||
@Value("\${spring.mail.sender}")
|
@Value("\${spring.mail.sender:}")
|
||||||
var sender = ""
|
var sender = ""
|
||||||
|
|
||||||
@Value("\${spring.mail.username}")
|
@Value("\${spring.mail.username:}")
|
||||||
var username = ""
|
var username = ""
|
||||||
|
|
||||||
@Value("\${spring.mail.password}")
|
@Value("\${spring.mail.password:}")
|
||||||
var password = ""
|
var password = ""
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -42,13 +39,20 @@ class EmailMessenger : IEmailMessenger {
|
|||||||
* @param receivers 邮件接受者
|
* @param receivers 邮件接受者
|
||||||
* @param files 附件
|
* @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>()
|
val result = mutableMapOf<String, Boolean>()
|
||||||
this.check()
|
this.check()
|
||||||
receivers.forEach { receiver ->
|
receivers.forEach { receiver ->
|
||||||
result[receiver] = true
|
result[receiver] = true
|
||||||
|
if (this.mailSender == null)
|
||||||
|
throw Exception("没有配置JavaMailSender的实现实例,请重新配置")
|
||||||
try {
|
try {
|
||||||
val mail = mailSender.createMimeMessage()
|
val mail = this.mailSender!!.createMimeMessage()
|
||||||
val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8")
|
val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8")
|
||||||
mimeMessageHelper.setFrom(sender)
|
mimeMessageHelper.setFrom(sender)
|
||||||
mimeMessageHelper.setTo(receiver)
|
mimeMessageHelper.setTo(receiver)
|
||||||
@@ -59,7 +63,7 @@ class EmailMessenger : IEmailMessenger {
|
|||||||
val file = FileSystemResource(File(path))
|
val file = FileSystemResource(File(path))
|
||||||
mimeMessageHelper.addAttachment(name, file)
|
mimeMessageHelper.addAttachment(name, file)
|
||||||
}
|
}
|
||||||
mailSender.send(mail) //发送
|
this.mailSender!!.send(mail) //发送
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error(e, "发送邮件[$subject]至地址[$receiver]失败")
|
logger.error(e, "发送邮件[$subject]至地址[$receiver]失败")
|
||||||
result[receiver] = false
|
result[receiver] = false
|
||||||
|
|||||||
@@ -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?)
|
|
||||||
}
|
|
||||||
@@ -1,68 +1,86 @@
|
|||||||
package com.synebula.gaea.app.component.aop
|
package com.synebula.gaea.app.component.aop
|
||||||
|
|
||||||
import com.synebula.gaea.app.component.HttpMessage
|
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.data.message.Status
|
||||||
|
import com.synebula.gaea.log.ILogger
|
||||||
import org.aspectj.lang.JoinPoint
|
import org.aspectj.lang.JoinPoint
|
||||||
import org.aspectj.lang.ProceedingJoinPoint
|
import org.aspectj.lang.ProceedingJoinPoint
|
||||||
import org.aspectj.lang.annotation.*
|
import org.aspectj.lang.annotation.*
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import org.springframework.web.context.request.RequestContextHolder
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes
|
import org.springframework.context.ApplicationContext
|
||||||
import java.util.*
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
|
||||||
|
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
class AppAspect {
|
class AppAspect {
|
||||||
|
private val mapper = ObjectMapper()
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var logger: ILogger
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var applicationContext: ApplicationContext
|
||||||
|
|
||||||
@Pointcut("within(com.synebula..app.*)")
|
@Pointcut("within(com.synebula..app.*)")
|
||||||
fun log() {
|
fun log() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//后置异常通知
|
/**
|
||||||
@AfterThrowing("log()")
|
* 后置异常通知
|
||||||
fun throws(point: JoinPoint): Any {
|
*/
|
||||||
|
@AfterThrowing("log()", throwing = "ex")
|
||||||
|
fun throws(point: JoinPoint, ex: Throwable) {
|
||||||
val clazz = point.signature.declaringType
|
val clazz = point.signature.declaringType
|
||||||
println("${clazz.name} - ${point.signature.name}异常:${point.signature.name}")
|
logger.error(
|
||||||
return "error"
|
ex,
|
||||||
|
"${clazz.name}.${point.signature.name} exception:${ex.message}, args:${mapper.writeValueAsString(point.args)}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
|
/**
|
||||||
@After("log()")
|
* 环绕通知,环绕增强,相当于MethodInterceptor
|
||||||
fun after(point: JoinPoint) {
|
*/
|
||||||
println("方法最后执行.....")
|
|
||||||
}
|
|
||||||
|
|
||||||
//环绕通知,环绕增强,相当于MethodInterceptor
|
|
||||||
@Around("log()")
|
@Around("log()")
|
||||||
fun around(point: ProceedingJoinPoint): Any? {
|
fun around(point: ProceedingJoinPoint): Any? {
|
||||||
val clazz = point.signature.declaringType
|
val clazz = point.signature.declaringType
|
||||||
val func = clazz.methods.find {
|
val func = clazz.methods.find {
|
||||||
it.name == point.signature.name
|
it.name == point.signature.name
|
||||||
}!!
|
}!!
|
||||||
val clazzAnnotations = clazz.annotations
|
|
||||||
val funcAnnotations = func.annotations ?: arrayOf()
|
val funcAnnotations = func.annotations ?: arrayOf()
|
||||||
|
|
||||||
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
|
var exceptionMessage = func.name
|
||||||
val request = attributes.request
|
//遍历方法注解
|
||||||
// 记录下请求内容
|
for (funcAnnotation in funcAnnotations) {
|
||||||
println("URL : " + request.requestURL.toString())
|
val annotations = funcAnnotation.annotationClass.annotations
|
||||||
println("HTTP_METHOD : " + request.method)
|
|
||||||
println("IP : " + request.remoteAddr)
|
//尝试寻找方法注解的处理类
|
||||||
println("CLASS_METHOD : " + clazz.name + "." + point.signature.name)
|
val handler = annotations.find { it is Handler }
|
||||||
println("ARGS : " + Arrays.toString(point.args))
|
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 {
|
return try {
|
||||||
val res = point.proceed()
|
val res = point.proceed()
|
||||||
res
|
res
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
val msg = HttpMessage(Status.Success)
|
//找到类的模块名称,否则使用类名
|
||||||
for (item in funcAnnotations) {
|
var moduleName = clazz.name
|
||||||
if (item.annotationClass == SafeExec::javaClass) {
|
val name = clazz.annotations.find { it is ModuleName }
|
||||||
}
|
if (name != null && name is ModuleName) {
|
||||||
|
moduleName = name.value
|
||||||
}
|
}
|
||||||
println("方法${point.signature}异常: $msg - ${ex.message}")
|
val message = "$moduleName - $exceptionMessage"
|
||||||
return "error"
|
logger.error(ex, "$message, args: ${mapper.writeValueAsString(point.args)}")
|
||||||
|
return HttpMessage(Status.Error, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -3,8 +3,8 @@ package com.synebula.gaea.app.component.aop.annotation
|
|||||||
/**
|
/**
|
||||||
* 标记方法安全执行,由AOP负责try catch异常
|
* 标记方法安全执行,由AOP负责try catch异常
|
||||||
*
|
*
|
||||||
* @param errorMessage 异常消息
|
* @param message 异常消息
|
||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class SafeExec(val errorMessage: String)
|
annotation class ExceptionMessage(val message: String)
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.synebula.gaea.app.component.aop.annotation
|
package com.synebula.gaea.app.component.aop.annotation
|
||||||
|
|
||||||
|
import com.synebula.gaea.app.component.aop.handler.AnnotationHandler
|
||||||
import kotlin.reflect.KClass
|
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>)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.synebula.gaea.app.component.aop.annotation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块的业务名称
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class ModuleName(val value: String)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -11,8 +11,8 @@ import com.synebula.gaea.query.type.Order
|
|||||||
*/
|
*/
|
||||||
data class Params(var page: Int = 1, var size: Int = 10) {
|
data class Params(var page: Int = 1, var size: Int = 10) {
|
||||||
|
|
||||||
private var _parameters = mutableMapOf<String, Any>()
|
private var _parameters = linkedMapOf<String, Any>()
|
||||||
private var _orders = mutableMapOf<String, Order>()
|
private var _orders = linkedMapOf<String, Order>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据索引,从0开始。表示数据在总量的第几条。(index = (page - 1) * size)
|
* 数据索引,从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) {
|
set(value) {
|
||||||
this._orders = value.toMutableMap()
|
this._orders = value
|
||||||
}
|
}
|
||||||
get() {
|
get() {
|
||||||
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
||||||
val params = mutableMapOf<String, Any>()
|
val params = linkedMapOf<String, Any>()
|
||||||
this._parameters.forEach {
|
this._parameters.forEach {
|
||||||
if (it.key.startsWith("@")) {
|
if (it.key.startsWith("@")) {
|
||||||
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
|
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) {
|
set(value) {
|
||||||
this._parameters = value.toMutableMap()
|
this._parameters = value
|
||||||
}
|
}
|
||||||
get() {
|
get() {
|
||||||
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
if (this._parameters.keys.count { it.startsWith("@") } > 0) {
|
||||||
val params = mutableMapOf<String, Any>()
|
val params = linkedMapOf<String, Any>()
|
||||||
this._parameters.forEach {
|
this._parameters.forEach {
|
||||||
if (it.key.startsWith("@")) {
|
if (it.key.startsWith("@")) {
|
||||||
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
|
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
|
||||||
|
|||||||
Reference in New Issue
Block a user