Spring AOP概念を徐々に浸透させる(二)---コードプレゼンテーション
前の記事ではSpringAOPに関する注釈とエージェントモデルについていくつか紹介し理解したが,本記事ではプロジェクトで切面AOPをよりよく運用するための考え方を指す
一:操作ログ
私たちはプロジェクトでユーザーの操作記録を保存して、プロジェクトを監視する役割を果たす可能性があります.このとき、ビジネスロジックとシステムに対する操作記録を毎回1つの方法に置くと、プロジェクトが肥大化します.メンテナンスに不利で、性能も悪くなります.だから私たちはAOPカットプログラミングの思想を使ってこの問題を処理することができます.ここではカスタム注釈+SpringAOP方式でユーザのシステムに対する操作を記録する:1:ログ注釈を定義する
@Documented,@Target,@Retentionの3つのメタ注釈の代表的な意味2:切面処理クラス切面処理クラスの主な処理@SysLog注釈をどのような方法で処理するかの論理を定義する
SysLogEntityエンティティーエンティティークラス
以上,操作ログを記録するコード実装について述べたが,主にどのメソッドに注釈をカスタマイズし,その後,被注釈のメソッド名とパラメータの名前を切面処理で取得するかを用いた.データベースの操作ログに保存
二:データ権限
プロジェクトでは、データ権限の問題に関連します.データ権限において我々は一般的にsql文によってデータの制限を行うが、もし、現在プロジェクトに多くのモジュールがデータ権限の問題に関与しているならば、我々は各モジュール応答のsqlにデータ権限関連のsql文を加える必要があり、このように実現すれば我々はMapperにもたらす.xmにはsqlの文が非常に多い.コードは肥大化しており、メンテナンスが容易ではないように見えます.そこで,コードを簡略化するために,注釈方式を用いて,断面クラスを介して統一的なデータ権限のsqlを組み立てる.1:データ権限を定義する注記
2:接面類を定義し、注記定義のパラメータを取得し、データを組み立てる
3:こちらはMybatisPlusコンポーネントを採用しているので、ブロッカー(sql文を再生成するとき)を使用してsqlをブロックし、sql文を再アセンブリすることができます.
上記の2つはSpringAopのコンセプトを利用してコードの操作を簡略化しています誤りがあればご指摘ください
一:操作ログ
私たちはプロジェクトでユーザーの操作記録を保存して、プロジェクトを監視する役割を果たす可能性があります.このとき、ビジネスロジックとシステムに対する操作記録を毎回1つの方法に置くと、プロジェクトが肥大化します.メンテナンスに不利で、性能も悪くなります.だから私たちはAOPカットプログラミングの思想を使ってこの問題を処理することができます.ここではカスタム注釈+SpringAOP方式でユーザのシステムに対する操作を記録する:1:ログ注釈を定義する
package com.hwrest.jeadmin.common.annotation;
import java.lang.annotation.*;
/**
* @author queiter.ex
* @title: SysLog
* @projectName
* @description:
* @date 2019/12/309:13
*/
@Documented
@Target(ElementType.METHOD) //
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {
String name() default "";
}
@Documented,@Target,@Retentionの3つのメタ注釈の代表的な意味2:切面処理クラス切面処理クラスの主な処理@SysLog注釈をどのような方法で処理するかの論理を定義する
package com.hwrest.jeadmin.common.aspect;
import com.google.gson.Gson;
import com.hwrest.jeadmin.common.annotation.SysLog;
import com.hwrest.jeadmin.common.utils.HttpContextUtils;
import com.hwrest.jeadmin.common.utils.IPUtils;
import com.hwrest.jeadmin.common.utils.ShiroUtils;
import com.hwrest.jeadmin.modules.sys.entity.SysLogEntity;
import com.hwrest.jeadmin.modules.sys.entity.SysUserEntity;
import com.hwrest.jeadmin.modules.sys.service.SysLogService;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author queiter.ex
* @title: SysLogAspect
* @projectName je_admin
* @description:
* @date 2019/12/309:40
*/
@Component
@Aspect
public class SysLogAspect {
//
@Autowired
private SysLogService sysLogService;
//
@Pointcut("@annotation(com.hwrest.jeadmin.common.annotation.SysLog)")
public void logPointCut(){
}
//
@Around("logPointCut()") //
public Object around(ProceedingJoinPoint point) throws Throwable {
//
long begTime = System.currentTimeMillis();
//
Object proceed = point.proceed();
//
long time = System.currentTimeMillis() - begTime;
//
saveLog(point, time);
return proceed;
}
/**
* @Author liuhongwei
* @Description
* @Date 10:05 2019/12/30
* @param
* @return
*/
private void saveLog(ProceedingJoinPoint point, long time) {
MethodSignature signature = (MethodSignature) point.getSignature();
//
Method method = signature.getMethod();
SysLogEntity sysLogEntity = new SysLogEntity();
//
SysLog annotation = method.getAnnotation(SysLog.class);
//
if (annotation != null) {
// annotation.name()
sysLogEntity.setOperation(annotation.name());
}
//
String className = point.getTarget().getClass().getName();
//
String methodName = signature.getName();
//
sysLogEntity.setMethod(className + "." + methodName + "()");
//
Object[] args = point.getArgs();
try {
String params = new Gson().toJson(args);
sysLogEntity.setParams(params);
} catch (Exception e) {
}
// Request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
// request ip
sysLogEntity.setIp(IPUtils.getIpAddr(request));
//
SysUserEntity sysUserEntity = (SysUserEntity) ShiroUtils.getSubject();
sysLogEntity.setUsername(sysLogEntity.getUsername());
//
sysLogEntity.setTime(time);
sysLogEntity.setCreateDate(new Date());
//
sysLogService.save(sysLogEntity);
}
}
SysLogEntityエンティティーエンティティークラス
package com.hwrest.jeadmin.modules.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @author queiter.ex
* @title: SysLogEntity
* @projectName je_admin
* @description: TODO
* @date 2019/12/309:43
*/
@Data
@TableName(value = "sys_log")
public class SysLogEntity implements Serializable {
@TableId
private Long id;
//
private String username;
//
private String operation;
//
private String method;
//
private String params;
// ( )
private Long time;
//IP
private String ip;
//
private Date createDate;
}
以上,操作ログを記録するコード実装について述べたが,主にどのメソッドに注釈をカスタマイズし,その後,被注釈のメソッド名とパラメータの名前を切面処理で取得するかを用いた.データベースの操作ログに保存
二:データ権限
プロジェクトでは、データ権限の問題に関連します.データ権限において我々は一般的にsql文によってデータの制限を行うが、もし、現在プロジェクトに多くのモジュールがデータ権限の問題に関与しているならば、我々は各モジュール応答のsqlにデータ権限関連のsql文を加える必要があり、このように実現すれば我々はMapperにもたらす.xmにはsqlの文が非常に多い.コードは肥大化しており、メンテナンスが容易ではないように見えます.そこで,コードを簡略化するために,注釈方式を用いて,断面クラスを介して統一的なデータ権限のsqlを組み立てる.1:データ権限を定義する注記
package com.hwrest.jeadmin.common.annotation;
import java.lang.annotation.*;
/**
* @author quiter.ex
* @title: DataFilter
* @projectName je_admin
* @description:
* @date 2019/12/3014:02
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataFilter {
/**
*
*/
String tableAlias() default "";
/**
* , :[where、and]
*/
String prefix() default "";
/**
* ID
*/
String userId() default "creator";
/**
* ID
*/
String deptId() default "dept_id";
}
2:接面類を定義し、注記定義のパラメータを取得し、データを組み立てる
package com.hwrest.jeadmin.common.aspect;
import cn.hutool.core.collection.CollUtil;
import com.hwrest.jeadmin.common.annotation.DataFilter;
import com.hwrest.jeadmin.common.enumAdmin.SuperAdminEnum;
import com.hwrest.jeadmin.common.utils.ShiroUtils;
import com.hwrest.jeadmin.modules.sys.entity.SysUserEntity;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author liuhongwei.ex
* @title: DataFilterAspect
* @projectName je_admin
* @description:
* @date 2019/12/3014:10
*/
@Aspect //
@Component // Bean
public class DataFilterAspect {
//
@Pointcut("@annotation(com.hwrest.jeadmin.common.annotation.DataFilter)")
public void dataFilterCut() {
}
// sql
@Before("dataFilterCut()")
public void dataFilter(JoinPoint point) {
//
Object params = point.getArgs()[0];
if (params != null && params instanceof Map) {
SysUserEntity sysUserEntity = (SysUserEntity) ShiroUtils.getSubject();
//
if (sysUserEntity.getIsAdmin() == SuperAdminEnum.NO.value()) {
Map map = (Map)params;
String sqlFilter = getSqlFilter(user, point);
map.put("sqlFilter", new DataScope(sqlFilter));
}
}
}
public String getSqlFilter(JoinPoint point, SysUserEntity sysUserEntity) {
//
MethodSignature signature = (MethodSignature) point.getSignature();
//
DataFilter annotation = signature.getMethod().getAnnotation(DataFilter.class);
//
String tableAlias = annotation.tableAlias();
if (StringUtils.isNotBlank(tableAlias)) {
tableAlias += ".";
}
StringBuilder stringBuilder = new StringBuilder();
//
String prefix = annotation.prefix();
//
if (StringUtils.isNotBlank(prefix)) {
stringBuilder.append(" ").append(prefix);
}
stringBuilder.append(" (");
//
List<Long> roleIdList = sysUserEntity.getDeptIdList();
if (CollUtil.isNotEmpty(roleIdList)) {
stringBuilder.append(tableAlias).append(annotation.deptId());
stringBuilder.append(" in(").append(StringUtils.join(roleIdList, ",")).append(") ");
}
//
if (CollUtil.isNotEmpty(roleIdList)) {
stringBuilder.append(" or ");
}
stringBuilder.append(tableAlias).append(annotation.userId()).append(" = ").append(sysUserEntity.getUserId());
stringBuilder.append("(");
return stringBuilder.toString();
}
}
3:こちらはMybatisPlusコンポーネントを採用しているので、ブロッカー(sql文を再生成するとき)を使用してsqlをブロックし、sql文を再アセンブリすることができます.
package com.hwrest.jeadmin.common.interceptor;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.hwrest.jeadmin.modules.sys.entity.DataScope;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
/**
* @author quiter.ex
* @title: DataFilterInterceptor
* @projectName je_admin
* @description: TODO
* @date 2019/12/3016:04
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataFilterInterceptor extends AbstractSqlParserHandler implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
// sql
this.sqlParser(metaObject);
// SELECT
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return invocation.proceed();
}
// rowBounds, mapper
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.mappedStatement");
// sql
String originalSql = boundSql.getSql().toUpperCase();
// sql
Object parameterObject = boundSql.getParameterObject();
// DataScope
DataScope scope = null;
if (parameterObject instanceof DataScope) {
scope = (DataScope) parameterObject;
} else if (parameterObject instanceof Map) {
for (Object args : ((Map) parameterObject).values())
if (args instanceof DataScope) {
scope = (DataScope) args;
break;
}
}
//
if (scope == null) {
return invocation.proceed();
}
// sql
if (originalSql.indexOf(" ORDER BY ") != -1) {
originalSql = originalSql.replace("ORDER BY", scope.getSqlFilter() + " ORDER BY ");
} else {
originalSql = originalSql + scope.getSqlFilter();
}
// sql
metaObject.setValue("delegate.boundSql.sql", originalSql);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
上記の2つはSpringAopのコンセプトを利用してコードの操作を簡略化しています誤りがあればご指摘ください