Initial project
This commit is contained in:
17
src/main/java/com/imyeyu/spring/annotation/AOPLog.java
Normal file
17
src/main/java/com/imyeyu/spring/annotation/AOPLog.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* AOP 切面日志注解,应用在 Controller 的接口上
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-07-21 23:34
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AOPLog {
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.spring.TimiSpring;
|
||||
import com.imyeyu.spring.bean.PageResult;
|
||||
import com.imyeyu.spring.entity.IDEntity;
|
||||
import com.imyeyu.utils.Text;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* AOP 切面日志
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-17 16:26
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class AOPLogInterceptor {
|
||||
|
||||
public static final String REQUEST_ID = "TIMI_SPRING_REQUEST_ID";
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AOPLogInterceptor.class);
|
||||
|
||||
/** 注入注解 */
|
||||
@Pointcut("@annotation(annotation.com.imyeyu.spring.AOPLog)")
|
||||
public void logPointCut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行前
|
||||
*
|
||||
* @param joinPoint 切入点
|
||||
*/
|
||||
@Before("logPointCut()")
|
||||
public void doBefore(JoinPoint joinPoint) {
|
||||
String uuid = Text.tempUUID();
|
||||
TimiSpring.setSessionAttr(REQUEST_ID, uuid);
|
||||
log.info("ID: {} Request -> IP: {}, URI: {}", uuid, TimiSpring.getRequestIP(), TimiSpring.getRequest().getRequestURI());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行后
|
||||
*
|
||||
* @param response 返回内容
|
||||
* @throws Throwable 异常
|
||||
*/
|
||||
@AfterReturning(returning = "response", pointcut = "logPointCut()")
|
||||
public void doAfterReturning(Object response) throws Throwable {
|
||||
String msg = "ID: {} Response <- Return.";
|
||||
if (response instanceof IDEntity<?> entity) {
|
||||
// 返回实体
|
||||
msg += entity.getClass().getSimpleName() + "." + entity.getId();
|
||||
} else if (response instanceof PageResult<?> pageResult) {
|
||||
// 返回数组
|
||||
if (pageResult.getList().isEmpty()) {
|
||||
msg += "PageResult<?> Empty";
|
||||
} else {
|
||||
if (pageResult.getList().get(0) == null) {
|
||||
msg += "PageResult<?>." + pageResult.getList().size();
|
||||
} else {
|
||||
msg += "PageResult<" + pageResult.getList().get(0).getClass().getSimpleName() + ">[" + pageResult.getList().size() + "]";
|
||||
}
|
||||
}
|
||||
// 返回数据页
|
||||
} else if (response instanceof String string) {
|
||||
// 返回字符串
|
||||
if (string.length() < 64) {
|
||||
msg += string;
|
||||
} else {
|
||||
msg += string.substring(0, 64) + "..";
|
||||
}
|
||||
msg = msg.replaceAll("[\\r\\n]+", "");
|
||||
} else if (response instanceof Boolean bool) {
|
||||
// 返回布尔值
|
||||
msg += bool;
|
||||
} else if (response instanceof Number number) {
|
||||
// 返回数字
|
||||
msg += response.getClass().getSimpleName() + ".[" + number.doubleValue() + "]";
|
||||
} else {
|
||||
// 其他对象
|
||||
if (TimiJava.isNotEmpty(response)) {
|
||||
msg += response.getClass().getSimpleName();
|
||||
} else {
|
||||
msg += "NULL";
|
||||
}
|
||||
}
|
||||
log.info(msg, TimiSpring.getSessionAttr(REQUEST_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕
|
||||
*
|
||||
* @param pjp 切入点
|
||||
* @return 执行返回
|
||||
* @throws Throwable 异常
|
||||
*/
|
||||
@Around("logPointCut()")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return pjp.proceed();
|
||||
}
|
||||
}
|
||||
46
src/main/java/com/imyeyu/spring/annotation/Entity.java
Normal file
46
src/main/java/com/imyeyu/spring/annotation/Entity.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 实体注解,Component 别名,只是为了在实体类注入服务接口(如果实体需要注入服务,需要这个类注解)
|
||||
* <pre>
|
||||
*
|
||||
* @Entity
|
||||
* @NoArgsConstructor // 需要个空的构造方法让 MyBatis 正常实例化
|
||||
* public class Entity {
|
||||
*
|
||||
* @Transient
|
||||
* private transient static Service service;
|
||||
*
|
||||
* // 通过构造方法注入
|
||||
* @Autowired
|
||||
* public Entity(Service service) {
|
||||
* Entity.service = service;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-18 16:31
|
||||
*/
|
||||
@Component
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Entity {
|
||||
|
||||
/**
|
||||
* 设置控制反转 ID
|
||||
*
|
||||
* @return 控制反转 ID
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 忽略全局返回处理器注解
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-07-16 10:58
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface IgnoreGlobalReturn {
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 应用在 Controller 的接口上
|
||||
*
|
||||
* <p>{@link #lifeCycle()} 生命周期内(秒)限制访问 {@link #value()} 次
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-16 17:57
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
public @interface RequestRateLimit {
|
||||
|
||||
/**
|
||||
* 生命周期内访问限制,默认 10 秒内 10 次
|
||||
*
|
||||
* @return 生命周期内限制访问次数
|
||||
*/
|
||||
int value() default 10;
|
||||
|
||||
/**
|
||||
* 生命周期
|
||||
*
|
||||
* @return 生命周期秒数
|
||||
*/
|
||||
int lifeCycle() default 10;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* 抽象访问频率限制,具体子类实现
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-16 18:07
|
||||
*/
|
||||
public abstract class RequestRateLimitAbstractInterceptor implements HandlerInterceptor {
|
||||
|
||||
protected String buildId(HandlerMethod handlerMethod) {
|
||||
return handlerMethod.getMethod().getDeclaringClass().getSimpleName() + "." + handlerMethod.getMethod().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前
|
||||
*
|
||||
* @param req 请求
|
||||
* @param resp 返回
|
||||
* @param handler 处理方法
|
||||
* @return true 为通过
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest req,
|
||||
@NonNull HttpServletResponse resp,
|
||||
@NonNull Object handler) {
|
||||
// 方法注解
|
||||
if (handler instanceof HandlerMethod handlerMethod) {
|
||||
RequestRateLimit requestRateLimit = handlerMethod.getMethodAnnotation(RequestRateLimit.class);
|
||||
if (requestRateLimit == null) {
|
||||
return true;
|
||||
}
|
||||
// 频率限制
|
||||
return beforeRun(req, resp, buildId(handlerMethod), requestRateLimit.lifeCycle(), requestRateLimit.value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口访问前触发
|
||||
*
|
||||
* @param req 请求
|
||||
* @param resp 返回
|
||||
* @param id 方法
|
||||
* @param cycle 生命周期
|
||||
* @param limit 访问限制
|
||||
* @return true 为通过
|
||||
*/
|
||||
public abstract boolean beforeRun(HttpServletRequest req, HttpServletResponse resp, String id, int cycle, int limit);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 单字段 Json 数据体
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-08-09 10:36
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RequestSingleParam {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import com.imyeyu.io.IO;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
@Component
|
||||
public class RequestSingleParamResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestSingleParam.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(@Nonnull MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
if (request == null) {
|
||||
throw new TimiException(TimiCode.REQUEST_BAD, "request illegal");
|
||||
}
|
||||
JsonElement element = JsonParser.parseString(IO.toString(request.getInputStream()));
|
||||
if (!element.isJsonObject()) {
|
||||
throw new TimiException(TimiCode.ARG_BAD, "not json object");
|
||||
}
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
String parameterName = parameter.getParameterName();
|
||||
if (!object.has(parameterName)) {
|
||||
throw new TimiException(TimiCode.ARG_MISS, "not found " + parameterName + " param");
|
||||
}
|
||||
JsonElement el = object.get(parameterName);
|
||||
if (parameter.getParameterType().isAssignableFrom(Long.class)) {
|
||||
return el.getAsLong();
|
||||
}
|
||||
if (parameter.getParameterType().isAssignableFrom(Integer.class)) {
|
||||
return el.getAsInt();
|
||||
}
|
||||
if (parameter.getParameterType().isAssignableFrom(String.class)) {
|
||||
return el.getAsString();
|
||||
}
|
||||
throw new TimiException(TimiCode.ERROR, "not support parameter type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 应用在 Controller 接口上
|
||||
*
|
||||
* <p>需要验证令牌,只验证该令牌是否有效,不验证数据和令牌的关系
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-16 17:58
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Component
|
||||
public @interface RequiredToken {
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.imyeyu.spring.annotation;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* 抽象验证令牌
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-08-16 18:07
|
||||
*/
|
||||
public abstract class RequiredTokenAbstractInterceptor<A extends Annotation> implements HandlerInterceptor {
|
||||
|
||||
private final Class<A> annotation;
|
||||
|
||||
public RequiredTokenAbstractInterceptor(Class<A> annotation) {
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前
|
||||
*
|
||||
* @param req 请求
|
||||
* @param resp 返回
|
||||
* @param handler 处理方法
|
||||
* @return true 为通过
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest req,
|
||||
@NonNull HttpServletResponse resp,
|
||||
@NonNull Object handler) {
|
||||
// 方法注解
|
||||
if (handler instanceof HandlerMethod handlerMethod) {
|
||||
A requiredTokenAnnotation = handlerMethod.getMethodAnnotation(annotation);
|
||||
if (requiredTokenAnnotation == null) {
|
||||
return true;
|
||||
}
|
||||
return beforeRun(req, resp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 访问前(通过 Token 限制)
|
||||
*
|
||||
* @param req 请求
|
||||
* @param resp 返回
|
||||
* @return true 为通过
|
||||
*/
|
||||
protected abstract boolean beforeRun(HttpServletRequest req, HttpServletResponse resp);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/** 注解和注解实现 */
|
||||
package com.imyeyu.spring.annotation;
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.imyeyu.spring.annotation.table;
|
||||
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自动生成 UUID,在 {@link SQLProvider} 代理器中的方法会对此注解字段自动生成 UUID
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-02-05 23:44
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface AutoUUID {
|
||||
|
||||
/**
|
||||
* 是否全大写 UUID
|
||||
*
|
||||
* @return true 为使用全大写 UUID
|
||||
*/
|
||||
boolean upper() default false;
|
||||
}
|
||||
21
src/main/java/com/imyeyu/spring/annotation/table/Column.java
Normal file
21
src/main/java/com/imyeyu/spring/annotation/table/Column.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.spring.annotation.table;
|
||||
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 指定列名,在 {@link SQLProvider} 代理器中的方法无法简单将字段转为数据库列名时,使用此注解指定
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-01-29 09:53
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Column {
|
||||
|
||||
String value();
|
||||
}
|
||||
25
src/main/java/com/imyeyu/spring/annotation/table/Id.java
Normal file
25
src/main/java/com/imyeyu/spring/annotation/table/Id.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.imyeyu.spring.annotation.table;
|
||||
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 标记为 ID 字段,在 {@link SQLProvider} 代理器中的方法使用此字段进行以下操作
|
||||
* <ul>
|
||||
* <li>{@link SQLProvider#select(ProviderContext, Object)}</li>
|
||||
* <li>{@link SQLProvider#delete(ProviderContext, Object)}</li>
|
||||
* <li>{@link SQLProvider#destroy(ProviderContext, Object)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-01-29 09:54
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Id {
|
||||
}
|
||||
21
src/main/java/com/imyeyu/spring/annotation/table/Table.java
Normal file
21
src/main/java/com/imyeyu/spring/annotation/table/Table.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.imyeyu.spring.annotation.table;
|
||||
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 指定表名,在 {@link SQLProvider} 代理器中的方法无法简单将实体类名转为数据库表名时,使用此注解指定
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-01-29 09:53
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Table {
|
||||
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.imyeyu.spring.annotation.table;
|
||||
|
||||
import com.imyeyu.spring.util.SQLProvider;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* 忽略字段或表示非表实体,在 {@link SQLProvider} 代理器中的方法无需处理该字段时,使用此注解标记,不标记的字段均视为映射数据库列
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2025-02-06 17:46
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transient {
|
||||
}
|
||||
Reference in New Issue
Block a user