spring之AOP

接口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("進入日誌切面前置結束!!");
    }

 





 

 

發佈了53 篇原創文章 · 獲贊 5 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章