接口calculator
public interface calculator {
public int add(int a,int b);
public int sub(int a,int b);
public int mut(int a,int b);
public int div(int a,int b);
}
類calculatorImp
public class calculatorImp implements calculator{
public int add(int a, int b) {
return a+b;
}
public int sub(int a, int b) {
return a-b;
}
public int mut(int a, int b) {
return a*b;
}
public int div(int a, int b) {
return a/b;
}
}
代理類calculatorProxy
public class calculatorProxy {
//需要被代理的對象
private calculator target;
public calculatorProxy(calculator target){
this.target=target;
}
public calculator getLoggingProxy(){
calculator proxy=null;
//代理對象從由哪一個類加載器負責加載
ClassLoader loader=target.getClass().getClassLoader();
//代理對象的類型 即有哪些方法
Class[] interfaces=new Class[]{calculator.class};
//當調用代理對象其中的方法時需執行的代碼
InvocationHandler h=new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName=method.getName();
System.out.println("method-->"+methodName+" params"+Arrays.asList(args));
//執行方法
Object result=method.invoke(target, args);
return result;
}
};
proxy=(calculator)Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
}
main:
public static void main(String[] args){
calculator cal=new calculatorImp();
calculator proxy=new calculatorProxy(cal).getLoggingProxy();
int result=proxy.add(1, 2);
System.out.println("-->"+result);
result=proxy.div(4, 2);
System.out.println("-->"+result);
}
這樣利用代理的方法可以實現日誌的功能。但是有點麻煩。spring有個AOP(面向切面編程)可以很簡單的實現日誌。
AOP實現步驟:
1.jar包:AspectJ Weaver , spring aop
2.配置文件中添加
<!-- 讓aspectj註解起作用 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3.給IOC容器中的bean加入@Component如 calculatorImp,LoggingProxy 和給切面(也是一個類 LoggingProxy)加上註解@Aspect
4.在切面(LoggingProxy裏)聲明通知 有5中通知註解
@Before(方法執行之前)
@After 執行之後,不能訪問執行結果
@@AfterReturning 返回通知 方法返回結果之後通知
@AfterThrowing 方法異常之後通知
@Around 環繞通知 圍繞着方法執行
//component將該類導入到ioc容器中 aspectj聲明一個切面 Order 對多個切面進行優先級。
@Component
@Aspect
@Order(1)
public class LogginProxy {
//定義切入點表達式 該方法不需添加其他代碼 calculatorImp中的所有方法是切入點
//正常是public int com.fitch.aop.calculator.add(int,int)
//第一個* 代表 權限和返回值類型 第二個*代表 方法 裏面的..代表參數
@Pointcut("execution(* com.fitch.aop.calculator.*(..))")
public void declareJoinPointExpression(){}
//指定在哪些方法之前執行
@Before("declareJoinPointExpression()")
public void beforeMethod(JoinPoint joinpoint){
String methodName=joinpoint.getSignature().getName();
Object[] args=joinpoint.getArgs();
System.out.println("start method->"+methodName+" params"+Arrays.asList(args));
}
@After("execution(* com.fitch.aop.calculator.*(..))")
public void afterMethod(JoinPoint joinpoint){
System.out.println("after");
}
//返回通知 參數中有返回值 註解中的result跟參數中的result綁定了
@AfterReturning(value="declareJoinPointExpression()",returning="result")
public void afterReturning(JoinPoint joinpoint, Object result){
System.out.println("afterReturning result:"+result);
}
@AfterThrowing(value="declareJoinPointExpression()",throwing="ex")
public void afterThrowing(JoinPoint joinpoint, Exception ex){
System.out.println("afterThrowing ");
}
/*//環繞需攜帶ProceedingJoinPoint類型的參數
//這個通知類似於動態代理的全過程:ProceedingJoinPoint決定是否執行目標方法,而且必須有目標方法的返回值
@Around(value="declareJoinPointExpression()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("around");
Object result=null;
String methodName=proceedingJoinPoint.getSignature().getName();
//執行目標方法
try {
//前置通知
System.out.println("methodname:"+methodName +" params:"+Arrays.asList(proceedingJoinPoint.getArgs()));
result=proceedingJoinPoint.proceed();
//返回通知
} catch (Throwable e) {
//異常通知
e.printStackTrace();
}
//後置通知
return result;
}*/
}
main:
ApplicationContext ctx=new ClassPathXmlApplicationContext("springAop.xml");
calculator cal=ctx.getBean(calculator.class);
System.out.println("class:"+cal.getClass().getName());
int result=cal.add(1, 2);
System.out.println("main--->result:"+result);
以上是通過註解的方式配置Aop
基於配置文件的配置方式:
講上面類裏的所有註解@都刪除掉 然後在配置文件裏寫上用的bean.如<bean id="" class="">
要寫 calculatorImp 和 LoggingProxy的bean。然後用<aop:config>
<aop:config>
<!-- 配置切點表達式 -->
<aop:pointcut expression="execution(* com.fitch.aop.calculator.*(..))" id="pointcut"/>
<!-- 配置切面和通知 -->
<aop:aspect ref="LoggingProxy" order="1">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
************************************************************2018.11.17**********************************
/**
* 操作日誌
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogAop {
String value() default "";
String logType() default "";
String operationTable() default "";
String operationType() default "";// 新增和修改無需填,刪除需要填一下
}
@Override
@OperationLogAop(logType = EnumInfo.logType.BUILDING, operationTable = "building_material")
@Transactional(rollbackFor = Exception.class)
public BaseModel<String> editBuildingMaterial(BuildingMaterialReqVO vo, BaseVO base) {
BaseModel<String> res = new BaseModel<>();
if (StringUtils.isEmpty(vo.getBuildingId())) {
res.setStatus(RespStatus.FAIL_INVALID_DATA);
res.setMessage(RespMessage.FIELD_IS_EMPTY);
return res;
}
**************
}
@Aspect
@Component
public class OperationLogAspect {
private static Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);
@Autowired
RedisUtil redisUtil;
....
@Pointcut("@annotation(com.dtz.oneCapital.share.annotation.OperationLogAop)")
public void operationAspect(){}
@Before("operationAspect()")
public void doBefore(JoinPoint point){
long startTime = System.currentTimeMillis();
logger.info("進入日誌切面前置通知!!");
String methodName = point.getSignature().getName();
String className = point.getTarget().getClass().getName();
Annotation logAno = PermissionUtils.getOperationLogAnnotion(className,methodName);
if(logAno == null){
return;
}
try {
String logType = (String)PermissionUtils.getAnnotationValue(logAno,"logType");
String operationTable = (String)PermissionUtils.getAnnotationValue(logAno,"operationTable");
String operationType = (String)PermissionUtils.getAnnotationValue(logAno,"operationType");
Object[] params = point.getArgs();
List<JSONObject> paramObjList = new ArrayList<>();
String loginUserId = "";
for(Object obj : params){
String objStr = JSONObject.toJSONString(obj);
JSONObject objJSON = JSONObject.parseObject(objStr);
paramObjList.add(objJSON);
if(objStr.indexOf("loginUserId") > 0){
String aftStr = objStr.substring(objStr.indexOf("loginUserId") + 14);
loginUserId = aftStr.substring(0,aftStr.indexOf(",")-1);
break;
}
}
if(StringUtils.isEmpty(loginUserId)){
logger.info("OperationLogAspect loginUserId 爲空!!");
return;
}
OperationLog log = new OperationLog();
log.setLogType(logType);
log.setOperationTable(operationTable);
log.setCreateBy(loginUserId);
log.setCreateTime(CommonUtil.getCurrentDate());
String logTypeName = "";
String logTypeId = "";
JSONObject oldRecord = new JSONObject();
JSONObject newRecord = new JSONObject();
JSONObject param = paramObjList.get(0);
switch (methodName){
case "editBuildingBasic":
String buildingId = param.getString("buildingId");
String buildingName = param.getString("name");
String buildingNameEn = param.getString("nameEn");
logTypeName = buildingName;
logTypeId = buildingId;
if(StringUtils.isEmpty(buildingName) ){
logTypeName = buildingNameEn;
}
newRecord = param;
if(StringUtils.isEmpty(buildingId)){
break;
}
BuildingBasic buildingBasic = buildingBasicMapper.selectById(buildingId);
List<BuildingOwnerRel> buildingOwnerRels = buildingOwnerRelMapper.selectList(new EntityWrapper<BuildingOwnerRel>()
.eq("building_id", buildingId));
List<String> oldOwnerIds = buildingOwnerRels.stream().map(x -> x.getOwnerId()).collect(Collectors.toList());
oldRecord = JSONObject.parseObject(JSONObject.toJSONString(buildingBasic, SerializerFeature.WriteMapNullValue));
oldRecord.put("ownerIdList",JSONArray.parseArray(JSON.toJSONString(oldOwnerIds)));
oldRecord.put("buildingId",buildingId);
String key = "BuildingDetailVO_" + loginUserId;
Object oldDetail = redisUtil.get(key);
redisUtil.remove(key);
JSONObject oldObjt = oldDetail != null ? JSONObject.parseObject(oldDetail.toString()) : null;
if(oldObjt != null){
oldRecord.put("teamLeaderId", oldObjt.getString("teamLeaderId"));
oldRecord.put("teamMembers", oldObjt.getJSONArray("teamMembers"));
}
break;
case "publishBuildingHisPrice":
String buildingIdPrice = param.getString("buildingId");
logTypeId = buildingIdPrice;
newRecord = param;
BuildingBasic buildingBasicPrice = buildingBasicMapper.selectById(buildingIdPrice);
if(buildingBasicPrice != null){
logTypeName = buildingBasicPrice.getName();
if(StringUtils.isEmpty(logTypeName) ){
logTypeName = buildingBasicPrice.getNameEn();
}
oldRecord.put("price",buildingBasicPrice.getTotalPrice());
}
break;
case "editBuildingSpec":
String buildingIdS = param.getString("buildingId");
JSONArray newFileList = param.getJSONArray("fileList");
logTypeName = getBuildingName(buildingIdS);
logTypeId = buildingIdS;
param.put("fileList",getFileNameList(newFileList));
newRecord = param;
String specId = buildingSpecMapper.selectIdByBuildingId(buildingIdS);
if(StringUtils.isEmpty(specId)){
break;
}
BuildingSpec buildingSpec = buildingSpecMapper.selectById(specId);
oldRecord = JSONObject.parseObject(JSONObject.toJSONString(buildingSpec, SerializerFeature.WriteMapNullValue));
oldRecord.put("buildingId",buildingIdS);
BuildingFile bf = new BuildingFile();
bf.setBuildingId(buildingIdS);
bf.setFileType("BUILD_SPEC_FILE");
List<BuildingFileUrlVO> buildingFileUrlVOS = buildingFileMapper.selectBuildingFile(bf);
List<String> fileNameList = buildingFileUrlVOS.stream().map(x -> x.getFileName()).collect(Collectors.toList());
oldRecord.put("fileList", fileNameList);
break;
case "editBuildingProduceFile":
String buildingIdp = param.getString("buildingId");
JSONArray newpFileList = param.getJSONArray("list");
logTypeName = getBuildingName(buildingIdp);
logTypeId = buildingIdp;
List<String> pFileNameList = getFileNameList(newpFileList);
if(!CollectionUtils.isEmpty(pFileNameList)){
newRecord.put("teaserFile",pFileNameList);
}
List<BuildingFileUrlVO> oldFiles = buildingFileMapper.selectTeaserFile(buildingIdp);
List<String> filepNameList = oldFiles.stream().map(x -> x.getFileName()).collect(Collectors.toList());
if(!CollectionUtils.isEmpty(filepNameList)){
oldRecord.put("teaserFile",filepNameList);
}
break;
*************
}
String opType = oldRecord.size() > 0 ? EnumInfo.operationType.U : EnumInfo.operationType.N;
if(!StringUtils.isEmpty(operationType)){
opType = EnumInfo.operationType.D;
}
log.setOperationType(opType);
log.setLogTypeId(logTypeId);
log.setLogTypeName(logTypeName);
long endTime = System.currentTimeMillis();
logger.info("花費時間:" + (endTime - startTime));
HashMap<String,HashMap<String,Object>> map = LogUtil.recordHandler(oldRecord,newRecord);
if(MapUtils.isEmpty(map.get("old")) && MapUtils.isEmpty(map.get("new"))){
logger.info("return 數據沒有修改");
return;
}
if(MapUtils.isNotEmpty(map.get("old"))){
log.setOldRecord(JSONObject.toJSONString(map.get("old")));
}
if(MapUtils.isNotEmpty(map.get("new"))){
log.setNewRecord(JSONObject.toJSONString(map.get("new")));
}
redisUtil.sendMsg(QueueConstants.QUEUE_LOG,log);
}catch (Exception e){
e.printStackTrace();
}
logger.info("進入日誌切面前置結束!!");
}