场景原因:
在项目过程中,接到新需求,用户进行操作进行数据交换的时候,需要详细记录具体哪个值由什么变为了什么,在最开始实现的时候,我是在controller中每次insert或者update或者delet的时候前后各查询一次,然后对比,输出不一样的字段,
具体实现代码段:
/**
* 只修改clientIndivInfo非空字段
*
* @param clientIndivInfo
* @return
*/
@EnableGameleyLog(name = ModifyName.UPDATE,serviceclass = ClientIndivInfoService.class)
@RequestMapping(value = "/updateselective", method = RequestMethod.POST)
public
@ResponseBody
Object updateselective(@RequestBody ClientIndivInfo clientIndivInfo ,HttpServletRequest request) {
Track track = new Track();
ClientIndivInfo oldDlientIndivInfo = clientIndivInfoServiceImp.selectByPrimaryKey(clientIndivInfo.getClientno());
int result = clientIndivInfoServiceImp.updateByPrimaryKeySelective(clientIndivInfo);
LOGGER.info("{}选择性修改数据结果:{}", JsonUtil.toJson(clientIndivInfo), result);
ClientIndivInfo newDlientIndivInfo = clientIndivInfoServiceImp.selectByPrimaryKey(clientIndivInfo.getClientno());
track.setId(clientIndivInfo.getClientno());
try {
List<Map<String, Object>> changelist = CompareTwoClassUtil.compareTwoClass(oldDlientIndivInfo,newDlientIndivInfo);
StringBuilder str=new StringBuilder();
str.append("修改:\n");
for(Map<String,Object> map:changelist){
str.append("【"+map.get("name")+"】从【"+map.get("old")+"】改为了【"+map.get("new")+"】;\n");
}
track.setOperation(str.toString());
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String userId = SessionUtil.getUserId(request.getSession());
track.setUserId(userId);
String url = request.getRequestURI();
track.setUrl(url);
if(request.getRemoteAddr().toString().equals("0:0:0:0:0:0:0:1")){
String address = "0.0.0.0";
track.setAddress(address);
}else if(request.getRemoteAddr().toString().equals(null)||request.getRemoteAddr()==""){
String address = "- - - -";
track.setAddress(address);
}else {
String address = request.getRemoteAddr();
track.setAddress(address);
}
/*track.setOperation(dataInfo);*/
try {
trackServiceImpl.insert(track);
} catch (Exception e) {
e.printStackTrace();
}
return new RestBean(MsgCodeConstant.UPDATE_SUCCESS);
}
过来一段时间,发现这么写,每个controller都得写,而且,就算规范代码,写在serviceImpl里边,也是要耗费许多精力去写如此多的重复的代码,于是我就在想,可以使用AOP作为一个切面开发,写一个公共的代码块,这样岂不是就会省略很多事,我好天才,然后我就开始具体实现:
实现思考:
1.我需要一个入口来作为开启AOP切面的标志,我想了想,觉得使用自定义注解,然后在方法头打自定义注解就可以了
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface EnableGameleyLog {
/**
* 操作的中文说明 可以直接调用ModifyName
* @return
*/
String name() default "";
/**
* 获取编辑信息的解析类,目前为使用id获取,复杂的解析需要自己实现,默认不填写
* 则使用默认解析类
* @return
*/
Class parseclass() default DefaultContentParse.class;
/**
* 查询数据库所调用的class文件
* @return
*/
Class serviceclass() default IService.class;
/**
* 前台字段名称
*/
String[] feildName() default {"workingname"};
/**
* 具体业务操作名称
*/
String handleName() default "";
}
2.使用AOP注解配置,我是使用的SSM+AOP+MAVEN的配置,需要在xml文件中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
default-autowire="byName">
<mvc:resources location="/pages/" mapping="/pages/**"/>
<mvc:default-servlet-handler/>
<!-- task任务扫描注解 -->
<task:annotation-driven/>
<context:component-scan base-package="com.**.**.controller"/>
<aop:aspectj-autoproxy proxy-target-class="true" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean id="jacksonHttpMessageConverter"
class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 定义跳转的文件的前后缀 ,视图模式配置 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
<!-- 多部分文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857600"/>
<property name="maxInMemorySize" value="4096"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
</beans>
注意:你的xmlns必须配置xmlns:aop="http://www.springframework.org/schema/aop";
你的xsi必须配置http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd",并且和你的spirng版本相符合
3.书写AOP切面逻辑
@Aspect
@Component
//@Conditional(OperatelogService.class)
public class ModifyAspect {
private final static Logger logger = LoggerFactory.getLogger(ModifyAspect.class);
private Operatelog operateLog=new Operatelog();
private Object oldObject;
private Object newObject;
private Map<String,Object> feildValues;
@Autowired
private Service service;
@Before("@annotation(enableGameleyLog)")
public void doBefore(JoinPoint joinPoint, EnableGameleyLog enableGameleyLog){
//自己的代码
}
@AfterReturning(pointcut = "@annotation(enableGameleyLog)", returning = "object")
public void doAfterReturing(Object object, EnableGameleyLog enableGameleyLog){
//自己的代码
//将对比后的结果存入你的数据库
}
@After("@annotation(enableGameleyLog)")
public void doAfter(JoinPoint joinPoint, EnableGameleyLog enableGameleyLog) {
//自己的代码
}
注意:你使用的是注解作为AOP切面的入口,所以,你在AOP的切面注解中,比如:@Before,@After,@AfterRueturing中必须加上你的注解("@annoration(enableGameleyLog)"),这样才能保证你在controller打注解后能进行切面逻辑
4.具体实现代码controll
@EnableGameleyLog(name = ModifyName.UPDATE,serviceclass = ClientIndivInfoService.class)
@RequestMapping(value = "/updateselective", method = RequestMethod.POST)
public
@ResponseBody
Object updateselective(@RequestBody ClientIndivInfo clientIndivInfo ,HttpServletRequest request) {
//你的代码实现逻辑
}
5.进行完这些后,我发现一个问题,就是除开从数据库查出来数据外,我无论如何得不到当前数据,我能得到的只是修改后的数据,而后,要解决这个问题,就得在@Before注解下的代码中从数据库查询,如果的数据都是从一个表来的并且关联其他表都一样,那么,就可以从数据库查询,但是目前项目查询时关联的都不一样,几乎一个功能有7个模块,7个里边的查询都不一样,要是写7个AOP还不如我之前一个个写,代码量算过来都一样,坑爹,很烦,AOP这个不适用,下次再更新新想法