springAOP实现操作⽇志记录,并记录请求参数与编辑前后字段的具体改
本⽂为博主原创,未经允许不得转载:
  在项⽬开发已经完成多半的情况下,需要开发进⾏操作⽇志功能的开发,由于操作的重要性,需要记录下操作前的参数和请求时的参数,
在⽹上了很多,没到可⾏的⽅法.由于操作⽇志⽤注解⽅式的AOP记录操作⽇志⽐较便捷,所以想到了在注解中定义操作前查询数据
详情的bean,查询⽅法及参数,参数类型,在aop进⾏⽅法执⾏前,对指定的bean,⽅法,参数进⾏调⽤,获得修改前的参数,并进⾏保存.
此处需要注意:
1.在前⾯中调⽤指定bean的⽅法时,不可⽤反射进⾏调⽤,反射不能加载spring容器,⽆法获取指定的spring bean,下⾯⽅法中封装的获取spring bean的
  ⼯具类也需要配置为bean,⽽且被spring加载,才可以;
2.@Aspect注解的类⼀定要配置成bean,⽽且被spring加载,才可以,即同时配置@Component和@Aspect,或在spring的配置⽂件进⾏bean的配置
3.如果配置了bean,要检索component-scan扫描范围是否包括Aspect类;
⼀.定义切⾯执⾏的注解(该注解可根据⾃⼰实现的内容进⾏⾃定义)
1 import java.lang.annotation.Documented;
2 import java.lang.annotation.Retention;
3 import java.lang.annotation.Target;
4
5 import java.lang.annotation.ElementType;
6 import java.lang.annotation.RetentionPolicy;
7
8 @Target({ElementType.PARAMETER, ElementType.METHOD})
9 @Retention(RetentionPolicy.RUNTIME)
10 @Documented
11 public @interface SystemControllerLog {
12
13          /**查询模块*/
14          String module()  default "";
15
16          /**查询模块名称*/
17        String methods()  default "";
18
19        /**查询的bean名称*/
20        String serviceClass() default "";
21
22        /**查询单个详情的bean的⽅法*/
23        String queryMethod() default "";
24
25        /**查询详情的参数类型*/
26        String parameterType() default "";
27
28        /**从页⾯参数中解析出要查询的id,
29          * 如域名修改中要从参数中获取customerDomainId的值进⾏查询
30          */
31        String parameterKey() default "";
32
33        /**是否为批量类型操作*/
34        boolean paramIsArray() default false;
35
36    }
⼆.切⾯执⾏的⽅法
1
2import flect.Method;
SimpleDateFormat;
4import java.util.Date;
5
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpSession;
8
9import org.apachemons.lang3.StringUtils;
10import org.aspectj.lang.ProceedingJoinPoint;
11import org.aspectj.lang.Signature;
12import org.aspectj.lang.annotation.Around;
13import org.aspectj.lang.annotation.Aspect;
14import org.aspectj.lang.annotation.Pointcut;
15import org.flect.MethodSignature;
16import org.slf4j.Logger;
17import org.slf4j.LoggerFactory;
18import org.springframework.beans.factory.annotation.Autowired;
19import org.springframework.stereotype.Component;
20import org.springframework.util.ReflectionUtils;
21import org.t.request.RequestContextHolder;
22import org.t.request.ServletRequestAttributes;
23
24import com.alibaba.fastjson.JSON;
25import com.alibaba.fastjson.JSONArray;
26import com.alibaba.fastjson.JSONObject;
27import com.suning.fucdnmon.RequestResult;
28import com.ums.FucdnStrConstant;
29import com.ity.log.SystemControllerLogInfo;
30import com.suning.fucdn.impl.service.log.LogServiceImpl;
31import com.suning.fucdn.vo.AdminUserVO;
32
33/**
34  *
35  * 〈⼀句话功能简述:操作⽇志切⾯记录操作〉<br>
36  * 〈功能详细描述〉
37  *
38  * @author xiang
39  * @see [相关类/⽅法](可选)
40  * @since [产品/模块版本] (可选)
41*/
42  @Component
43  @Aspect
44public class ControllerLogAopAspect {
45
46private static final Logger LOGGER = Logger(ControllerLogAopAspect.class);
47
48//注⼊service,⽤来将⽇志信息保存在数据库
49      @Autowired
50private LogServiceImpl logservice;
51
52//配置接⼊点,如果不知道怎么配置,可以百度⼀下规则
      //指定controller的类进⾏切⾯  @Pointcut("execution(* ller..CustomerController.*(..))||execution(* ller.ManageController.*(..))")
53      @Pointcut("execution(* ller..*.*(..))")
54private void controllerAspect(){
55          System.out.println("point cut start");
56      }//定义⼀个切⼊点
57
58      @SuppressWarnings({ "rawtypes", "unused" })
59      @Around("controllerAspect()")
60public Object around(ProceedingJoinPoint pjp) throws Throwable {
61//常见⽇志实体对象
62          SystemControllerLogInfo log = new SystemControllerLogInfo();
63//获取登录⽤户账户
64          HttpServletRequest httpRequest = ((ServletRequestAttributes) RequestAttributes()).getRequest();
65
66//⽅法通知前获取时间,为什么要记录这个时间呢?当然是⽤来计算模块执⾏时间的
67//获取系统时间
68          String time = new SimpleDateFormat(FucdnStrConstant.YEAR_MONTH_DAY_HOUR_Constant()).format(new
Date());
69          log.setStartTime(time);
70
71//获取系统ip,这⾥⽤的是我⾃⼰的⼯具类,可⾃⾏⽹上查询获取ip⽅法
72//String ip = GetLocalIp.localIp();
73//log.setIP(ip);
74
75// 拦截的实体类,就是当前正在执⾏的controller
76          Object target = Target();
77// 拦截的⽅法名称。当前正在执⾏的⽅法
78          String methodName = Signature().getName();
79// 拦截的⽅法参数
80          Object[] args = Args();
81//String params = Args());
82          JSONArray operateParamArray = new JSONArray();
83for (int i = 0; i < args.length; i++) {
84              Object paramsObj = args[i];
85//通过该⽅法可查询对应的object属于什么类型:String type = Class().getName();
86if(paramsObj instanceof String || paramsObj instanceof JSONObject){
87              String str = (String) paramsObj;
88//将其转为jsonobject
89              JSONObject dataJson = JSONObject.parseObject(str);
90if(dataJson == null || dataJson.isEmpty() || "null".equals(dataJson)){
91break;
92              }else{
93                  operateParamArray.add(dataJson);
94              }
95          }else if(paramsObj instanceof Map){
96//get请求,以map类型传参
97//1.将object的map类型转为jsonobject类型
98              Map<String, Object> map = (Map<String, Object>) paramsObj;
99              JSONObject json =new JSONObject(map);
100              operateParamArray.add(json);
101          }
102          }
103//设置请求参数
104          log.JSONString());
105// 拦截的放参数类型
106          Signature sig = Signature();
107          MethodSignature msig = null;
108if (!(sig instanceof MethodSignature)) {
109throw new IllegalArgumentException("该注解只能⽤于⽅法");
110          }
111          msig = (MethodSignature) sig;
112
113          Class[] parameterTypes = Method().getParameterTypes();
114          Object object = null;
115// 获得被拦截的⽅法
116          Method method = null;
117try {
118              method = Class().getMethod(methodName, parameterTypes);
119          } catch (NoSuchMethodException e1) {
120              ("ControllerLogAopAspect around error",e1);
121          } catch (SecurityException e1) {
122              ("ControllerLogAopAspect around error",e1);
123          }
124if (null != method) {
125// 判断是否包含⾃定义的注解,说明⼀下这⾥的SystemLog就是我⾃⼰⾃定义的注解
126if (method.isAnnotationPresent(SystemControllerLog.class)) {
127
128//此处需要对⽤户进⾏区分:1为admin user 2为customer user
129// get session
130                  HttpSession httpSession = Session(true);
131// 从session获取登录⽤户
132                  AdminUserVO adminUserVO = (AdminUserVO) httpSession
133                          .getAttribute(FucdnStrConstant.SESSION_Constant());
134long adminUserId = AdminUserId();
135                  log.setUserId(String.valueOf(adminUserId));
136
137                  SystemControllerLog systemlog = Annotation(SystemControllerLog.class);
138
139                  log.dule());
140                  log.hods());
141//请求查询操作前数据的spring bean
142                  String serviceClass = systemlog.serviceClass();
143//请求查询数据的⽅法
144                  String queryMethod = systemlog.queryMethod();
spring framework和spring的关系145//判断是否需要进⾏操作前的对象参数查询
146if(StringUtils.isNotBlank(systemlog.parameterKey())
147                      &&StringUtils.isNotBlank(systemlog.parameterType())
148                      &&StringUtils.isNotBlank(systemlog.queryMethod())
149                      &&StringUtils.isNotBlank(systemlog.serviceClass())){
150boolean isArrayResult = systemlog.paramIsArray();
151//参数类型
152                      String paramType = systemlog.parameterType();
153                      String key = systemlog.parameterKey();
154
155if(isArrayResult){//批量操作
156//JSONArray jsonarray = (JSONArray) (key);
157//从请求的参数中解析出查询key对应的value值
158                          String value = "";
159                          JSONArray beforeParamArray = new JSONArray();
160for (int i = 0; i < operateParamArray.size(); i++) {
161                              JSONObject params =  JSONObject(i);
162                              JSONArray paramArray = (JSONArray) (key);
163if (paramArray != null) {
164for (int j = 0; j < paramArray.size(); j++) {
165                                      String paramId =  String(j);
166//在此处判断spring bean查询的⽅法参数类型
167                                      Object data = getOperateBeforeData(paramType, serviceClass, queryMethod, paramId);
168                                      JSONObject json = (JSONObject) JSON(data);
169                                      beforeParamArray.add(json);
170                                  }
171                              }
172                          }
173                          log.JSONString());
174
175                      }else{//单量操作
176
177//从请求的参数中解析出查询key对应的value值
178                          String value = "";
179for (int i = 0; i < operateParamArray.size(); i++) {
180                              JSONObject params =  JSONObject(i);
181                              value = String(key);
182if(StringUtils.isNotBlank(value)){
183break;
184                              }
185                          }
186//在此处获取操作前的spring bean的查询⽅法
187                          Object data = getOperateBeforeData(paramType, serviceClass, queryMethod, value);
188                          JSONObject beforeParam = (JSONObject) JSON(data);
189                          log.JSONString());
190                      }
191                  }
192
193try {
194//执⾏页⾯请求模块⽅法,并返回
195                      object = pjp.proceed();
196//获取系统时间
197                      String endTime = new SimpleDateFormat(FucdnStrConstant.YEAR_MONTH_DAY_HOUR_Constant()).format(new Date()); 198                      log.setEndTime(endTime);
199//将object 转化为controller封装返回的实体类:RequestResult
200                      RequestResult requestResult = (RequestResult) object;
201if(requestResult.isResult()){
202//操作流程成功
203if(StringUtils.ErrMsg())){
204                              log.ErrMsg());
205                          }else Data() instanceof String){
206                              log.setResultMsg((String) Data());
207                          }else{
208                              log.setResultMsg("执⾏成功");
209                          }
210                      }else{
211                          log.setResultMsg("失败");
212                      }
213//保存进数据库
214                      logservice.saveLog(log);
215                  } catch (Throwable e) {
216                      String endTime = new SimpleDateFormat(FucdnStrConstant.YEAR_MONTH_DAY_HOUR_Constant()).format(new Date()); 217                      log.setEndTime(endTime);
218
219                      log.Message());
220                      logservice.saveLog(log);
221                  }
222              } else {
223//没有包含注解
224                  object = pjp.proceed();
225              }
226          } else {
227//不需要拦截直接执⾏
228              object = pjp.proceed();
229          }
230return object;
231      }
232
233/**
234      *
235      * 功能描述: <br>
236      * 〈功能详细描述〉
237      *
238      * @param paramType:参数类型
239      * @param serviceClass:bean名称
240      * @param queryMethod:查询method
241      * @param value:查询id的value
242      * @return
243      * @see [相关类/⽅法](可选)
244      * @since [产品/模块版本](可选)
245*/
246public Object getOperateBeforeData(String paramType,String serviceClass,String queryMethod,String value){
247          Object obj = new Object();
248//在此处解析请求的参数类型,根据id查询数据,id类型有四种:int,Integer,long,Long
249if(paramType.equals("int")){
250int id = Integer.parseInt(value);
251              Method  mh = ReflectionUtils.Bean(serviceClass).getClass(), queryMethod,Long.class ); 252//⽤spring bean获取操作前的参数,此处需要注意:传⼊的id类型与bean⾥⾯的参数类型需要保持⼀致
253              obj = ReflectionUtils.invokeMethod(mh,  Bean(serviceClass),id);
254
255          }else if(paramType.equals("Integer")){
256              Integer id = Integer.valueOf(value);
257              Method  mh = ReflectionUtils.Bean(serviceClass).getClass(), queryMethod,Long.class ); 258//⽤spring bean获取操作前的参数,此处需要注意:传⼊的id类型与bean⾥⾯的参数类型需要保持⼀致
259              obj = ReflectionUtils.invokeMethod(mh,  Bean(serviceClass),id);
260
261          }else if(paramType.equals("long")){
262long id = Long.parseLong(value);
263              Method  mh = ReflectionUtils.Bean(serviceClass).getCla
ss(), queryMethod,Long.class ); 264//⽤spring bean获取操作前的参数,此处需要注意:传⼊的id类型与bean⾥⾯的参数类型需要保持⼀致
265              obj = ReflectionUtils.invokeMethod(mh,  Bean(serviceClass),id);
266
267          }else if(paramType.equals("Long")){
268              Long id = Long.valueOf(value);
269              Method  mh = ReflectionUtils.Bean(serviceClass).getClass(), queryMethod,Long.class ); 270//⽤spring bean获取操作前的参数,此处需要注意:传⼊的id类型与bean⾥⾯的参数类型需要保持⼀致
271              obj = ReflectionUtils.invokeMethod(mh,  Bean(serviceClass),id);
272          }
273return obj;
274      }
275  }
三.获取spring bean的⼯具类
1import org.springframework.beans.BeansException;
2import t.ApplicationContext;
3import t.ApplicationContextAware;
4import org.springframework.stereotype.Component;
5
6
7/**
8 * 获取spring容器,以访问容器中定义的其他bean
9 *  xiang
10 *  MOSTsView 3.0 2009-11-16
11*/
12 @Component
13public class SpringContextUtil implements ApplicationContextAware{
14
15private static ApplicationContext  applicationContext;
16
17/**
18    * 实现ApplicationContextAware接⼝的回调⽅法,设置上下⽂环境
19*/
20public void setApplicationContext(ApplicationContext applicationContext){
21        SpringContextUtil.applicationContext = applicationContext;
22    }
23
24public static ApplicationContext getApplicationContext(){
25return applicationContext;
26    }
27
28/**
29    * 获取对象
30    * @return  Object ⼀个以所给名字注册的bean的实例 (service注解⽅式,⾃动⽣成以⾸字母⼩写的类名为bean name)
31*/
32public static Object getBean(String name) throws BeansException{
Bean(name);
34    }
35 }
四.操作⽇志对应的实体类
1 public class SystemControllerLogInfo{
2
3    private long id;
4
5    /**⽤户id*/
6    private String userId;
7
8    /**⽤户类型*/
9    private int userType;
10
11    /**操作模块*/
12    private String module;
13
14    /**操作类型*/
15    private String method;
16
17    /**操作前参数*/
18    private String beforeParams;
19
20    /**操作时请求参数*/
21    private String operateParams;
22
23    /**开始时间*/
24    private String startTime;
25
26    /**结束时间*/
27    private String endTime;
28
29    /**操作状态描述*/
30    private int resultStatus;
31
32    /**操作结果描述*/
33    private String resultMsg;
五.进⾏注解切⾯调⽤
1 @ResponseBody
2    @RequestMapping(value = "/delete", method = { RequestMethod.POST })
3    @SystemControllerLog(module="域名管理",methods="域名删除",serviceClass="domainConfService",queryMethod="queryDomain",parameterType="Long",parameterKey="customerDomainId")
4    public RequestResult delete(@RequestBody String param) {
5        // 定义请求数据
6        RequestResult result = new RequestResult();
7        // 接收数据
8        CustomerDomain customerDomain = JSONObject.parseObject(param, CustomerDomain.class);
9        // 更新客户域名
10        try {
11            String data = domainConfService.Id());
12            // 设置true
13            if (StringUtils.isBlank(data)) {
14                result.setData("删除成功");
15            } else {
16                result.setData(data);
17            }
18            result.setResult(true);
19        } catch (Exception e) {
20            // 记录错误信息,并返回
21            ("delete failed", e);
22            result.Message());
23        }
24        // 返回
25        return result;
26    }
六.数据实例
补充:get请求参数类型解析和记录
1  JSONArray operateParamArray = new JSONArray();
2for (int i = 0; i < args.length; i++) {
3            Object paramsObj = args[i];
4//通过该⽅法可查询对应的object属于什么类型:String type = Class().getName();
5if(paramsObj instanceof String || paramsObj instanceof JSONObject){
6                String str = (String) paramsObj;
7//将其转为jsonobject
8                JSONObject dataJson = JSONObject.parseObject(str);
9if(dataJson == null || dataJson.isEmpty() || "null".equals(dataJson)){
10break;
11                }else{
12                    operateParamArray.add(dataJson);
13                }
14            }else if(paramsObj instanceof Map){
15//get请求,以map类型传参
16//1.将object的map类型转为jsonobject类型
17                Map<String, Object> map = (Map<String, Object>) paramsObj;
18                JSONObject json =new JSONObject(map);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。