AOP的使用
1. 在引入命名空间头的时候.顺序要保持一致
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
2. SpringMVC中使用aop注解无效的问题
详细情况是这样的:项目引用了SpringMVC框架,在编写Controller以及Service的时候添加@Transactional的情况下和编写AspectJ的切面的情况下,事务和AOP都没有生效,而我的AOP配置是这样的:AOP命名空间和<aop:aspectj-autoproxy proxy-target-class="true />这两个都配在了ApplicationContext.xml里面了,而不是SpringMVC框架自己约定的配置文件中(我这个项目里面,这个配置文件叫做springmvc-servlet.xml),正因为这样,出问题了。后来,我把AOP命名空间和<aop:aspectj-autoproxy proxy-target-class="true />挪到SpringMVC自己的配置文件里面,AOP就生效了。同理,把注解相应的命名空间和<tx:annotation-driven>放在SpringMVC的配置文件里,事务也生效了。
另外以上所述的是对controller进行切面时的配置,如果是对service进行切面,那么
这两个注释就要用在ApplicationContext.xml里面了,注意此时不要开启aop的cglib代理模式。
解释:
1.SpringMVC这个框架很好用,没问题,他的注解也简化了很多的配置,但是请注意@Controller和@Service都是SpringMVC框架包里面的,也就是说,这些类的实例化以及注入也是由SpringMVC这个框架完成的(确切的来说是这个框架自己有的上下文的IoC容器完成的)。
2.而对AOP和事务的支持是Spring框架本身完成的,是Spring框架的应用上下文所扫描并处理的。
从1.2可以得出一个结论,如果SpringMVC和Spring本身用的是一个应用上下文,一个Ioc容器,那随便你的和命名空间配置在哪里,无论是Spring的ApplicationContext.xml还是SpringMVC的springmvc-servlet.xml里面,反正都是一个容器,怎么扫描,怎么处理都能找到。
但关键的是以上假设不成立,总的来说SpringMVC的应用上下文的 “ 父 ” 上下文才是Spring的应用上下文。那么这个也就是说Spring的应用上下文初始化完成的时候,它开始扫描到底哪些Bean用了AspectJ的注解,哪些用了Transactional的注解,但是利用SpringMVC注解配置的这些Bean它是找不到的,因为用了这些注解的Bean还没有被实例化甚至是还没有被装载,为什么呢?因为管理这些bean的SpringMVC的上下文可能还没有完成初始化。OK,既然Spring的上下文找不到到底哪些Bean应用了注解,那他自然也没有办法给这些Bean提供声明式AOP和事务的支持了。
### 问题3.
当 环绕通知捕获了异常, 异常通知 则不能执行
案列
1.创建注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 表示对标记有xxx注解的类,做代理 注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
* Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
* 这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配
* RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE
* 用@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候;
* 用@Retention(RetentionPolicy.SOURCE)修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
* 用@Retention(RetentionPolicy.RUNTIME)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时,
* 所以他们可以用反射的方式读取。RetentionPolicy.RUNTIME
* 可以让你从JVM中读取Annotation注解的信息,以便在分析程序的时候使用.
*
* 类和方法的annotation缺省情况下是不出现在javadoc中的,为了加入这个性质我们用@Documented
* java用 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。
* @interface是一个关键字,在设计annotations的时候必须把一个类型定义为@interface,而不能用class或interface关键字
*/
/**
* 自定义注解 拦截方法
* @author xzb_l
*
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemMethodLog {
String description() default "";
}
2.创建切面类
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import com.ai.civil.common.Constants;
import com.ai.civil.common.util.SystemLogUtil;
import com.ai.civil.cs.entity.TbCivilUser;
import com.ai.civil.log.MongoLogWriter;
import com.ai.trial.ct.session.SessionValueHoderFactory;
import com.ai.trial.ct.session.SessionValueHolder;
/**
* 系统日志切入点
* @author xzb_l
*
*/
@Aspect
@Component
public class SystemLogAspect {
@Autowired
SessionValueHoderFactory sessionValueHoderFactory;
@Resource
MongoLogWriter logWriter;
/**本地异常日志记录对象 */
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
/**
* Method 切入点
*/
@Pointcut("@annotation(com.ai.civil.annotation.SystemMethodLog)")
public void sysMethodAspect() {}
/**记录日志信息*/
private static Map<String,String> loggerMap;
/**
* 异常通知 用于拦截Method记录用户的操作
* 异常通知执行(最后),则后置通知不执行
* @param joinPoint 切点
*/
@AfterThrowing(value ="sysMethodAspect()",throwing = "e")
public void handleThrowing(JoinPoint joinPoint, Throwable e) {
try {
// 写入日志
loggerMap = SystemLogUtil.exceptionInfoCollect(joinPoint, e,logWriter,loggerMap);
loggerMap = null;
} catch (Exception ex) {
//记录本地异常日志
logger.error("收集异常出错");
ex.printStackTrace();
}
}
/**
* 前置通知
*/
@Before("sysMethodAspect()")
public void beforeInfo(JoinPoint point) {
try {
// 用于保存信息
loggerMap = new LinkedHashMap<String,String>();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//请求的IP
String ip = this.getIpAddr(request);
// 记录方法执行之前的各项参数
loggerMap = SystemLogUtil.permissionCheckInfo(point,getUserInfo(), ip,loggerMap);
} catch (Exception e) {
logger.error("收集异常出错");
e.printStackTrace();
}
}
/**
* 环绕通知
* @param point
* @throws Throwable
*/
@Around("sysMethodAspect()")
public Object process(ProceedingJoinPoint point) throws Throwable{
// 执行目标方法
Object returnValue = point.proceed();
return returnValue;
}
/**
* 后置通知:当方法执行完会触发(写入日志)
* 没有异常时后置通知执行最后
* @param point
* @param returnValue
*/
@AfterReturning(value = "sysMethodAspect()", returning="returnValue")
public void afterReturnInfo(JoinPoint point, Object returnValue) {
try {
loggerMap = SystemLogUtil.afterReturnInfo(point, getUserInfo(), returnValue,logWriter,loggerMap);
loggerMap = null;
} catch (Exception e) {
logger.error("收集异常出错");
e.printStackTrace();
}
}
/**
* 最终通知 (始终执行)
* @param point
*/
@After("sysMethodAspect()")
public void afterInfo(JoinPoint point) {
try {
// 记录方法执行之后的各项参数
loggerMap = SystemLogUtil.releaseResourceInfo(point,loggerMap);
} catch (Exception e) {
logger.error("收集异常出错");
e.printStackTrace();
}
}
/**
* 获取当前操作用户
* @return
*/
public TbCivilUser getUserInfo() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ServletWebRequest servletWebRequest=new ServletWebRequest(request);
HttpServletResponse response=servletWebRequest.getResponse();
SessionValueHolder sessionValueHolder = sessionValueHoderFactory.getSessionValueHolder(request, response);
TbCivilUser user = (TbCivilUser)sessionValueHolder.getValue(Constants.CUR_USER);
if (user == null) {
user = new TbCivilUser();
}
return user;
}
/**
* 获取系统登录IP
* @param request
* @return
*/
public String getIpAddr(HttpServletRequest request){
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
4.记录日志
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
/**
*
* 记录日志处理
*
* @author xzb
*
*/
public class SystemLogUtil {
private static final Logger logger = LoggerFactory.getLogger(SystemLogUtil.class);
private static String[] types = { "java.lang.Integer", "java.lang.Double",
"java.lang.Float", "java.lang.Long", "java.lang.Short",
"java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
"java.lang.String", "int", "double", "long", "short", "byte",
"boolean", "char", "float" };
// 定义异常
//private static final String EXCEPTION_START = "@AfterThrowing:抛出异常";
//private static final String EXCEPTION_DATA = "@AfterThrowing:异常发生时间";
private static final String EXCEPTION_NAME = "@AfterThrowing:异常名称";
private static final String EXCEPTION_METHOD = "@AfterThrowing:异常方法";
//private static final String EXCEPTION_DISCRIPTION = "@AfterThrowing:方法描述";
//private static final String EXCEPTION_USER = "@AfterThrowing:请求人";
//private static final String EXCEPTION_IP = "@AfterThrowing:请求IP";
private static final String EXCEPTION_PARAMS = "@AfterThrowing:请求参数";
private static final String EXCEPTION_INFO = "@AfterThrowing:抛出异常信息";
// 执行方法之前
//private static final String BEFORE_START = "@Before:模拟权限检查";
private static final String BEFORE_METHOD = "@Before:目标方法为";
private static final String BEFORE_PARAMS = "@Before:参数为(初始参数)";
//private static final String BEFORE_DISCRIPTION = "@Before:方法描述";
private static final String BEFORE_USER = "@Before:请求人";
private static final String BEFORE_IP = "@Before:请求IP";
// 执行方法之后
//private static final String AFTER_START = "@After:模拟释放资源";
//private static final String AFTER_METHOD = "@After:目标方法为";
private static final String AFTER_PARAMS = "@After:参数为(或者参数改变之后的参数)";
//private static final String AFTER_DISCRIPTION = "@After:方法描述";
// 执行结果
//private static final String RETURN_START = "@AfterReturning:执行结果";
//private static final String RETURN_METHOD = "@AfterReturning:目标方法为";
private static final String RETURN_RESULT = "@AfterReturning:返回值";
/**
* 异常处理
* @return
*/
public static Map<String, String> exceptionInfoCollect(JoinPoint joinPoint, Throwable e, MongoLogWriter logWriter,
Map<String, String> loggerMap) throws Exception {
if (MatchUtil.isEmpty(loggerMap)) {
loggerMap = new LinkedHashMap<String,String>();
}
loggerMap.put(EXCEPTION_NAME, e.getClass().getName());
loggerMap.put(EXCEPTION_METHOD, (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
loggerMap.put(EXCEPTION_PARAMS, getParameterNames(joinPoint));
loggerMap.put(EXCEPTION_INFO, e.getMessage());
// 记录本地日志
logger.error(loggerMap.toString()+"\r\n");
// 记录MongoDB
logWriter.insert(loggerMap);
return loggerMap;
}
/**
* 收集方法执行之前的参数信息
* @param point
* @return
* @throws Exception
*/
public static Map<String, String> permissionCheckInfo(JoinPoint joinPoint,TbCivilUser user,String ip,Map<String,String> loggerMap) throws Exception {
if (MatchUtil.isEmpty(loggerMap)) {
loggerMap = new LinkedHashMap<String,String>();
}
loggerMap.put(BEFORE_METHOD, (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
loggerMap.put(BEFORE_PARAMS, getParameterNames(joinPoint));
loggerMap.put(BEFORE_USER, user.getUserName());
loggerMap.put(BEFORE_IP, ip);
return loggerMap;
}
/**
* 收集执行方法的结果
* @param logWriter
* @return
*/
public static Map<String, String> afterReturnInfo(JoinPoint point, TbCivilUser user, Object returnValue,MongoLogWriter logWriter, Map<String,String> loggerMap) throws Exception {
if (MatchUtil.isEmpty(loggerMap)) {
loggerMap = new LinkedHashMap<String,String>();
}
if (returnValue == null) {
returnValue = "结果为null";
}
loggerMap.put(RETURN_RESULT, returnValue.toString());
// 记录本地日志
logger.error(loggerMap.toString()+"\r\n");
// 记录MongoDB
logWriter.insert(loggerMap);
return loggerMap;
}
/**
* 收集所有参数信息
* @param point
* @param exceptionFlag
* @return
* @throws Exception
*/
public static Map<String, String> releaseResourceInfo(JoinPoint point,Map<String,String> loggerMap) throws Exception {
if (MatchUtil.isEmpty(loggerMap)) {
loggerMap = new LinkedHashMap<String,String>();
}
loggerMap.put(AFTER_PARAMS, getParameterNames(point));
return loggerMap;
}
/**
* 获取注解中对方法的描述信息
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public static String getMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemMethodLog.class).description();
break;
}
}
}
return description;
}
/**
* 参数名称
* @param paramNames
* @param joinPoint
* @return
*/
private static String writeLogInfo(String[] paramNames, JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
boolean clazzFlag = true;
for(int k=0; k<args.length; k++){
Object arg = args[k];
sb.append(paramNames[k]+" ");
// 获取对象类型
String typeName = arg.getClass().getTypeName();
for (String t : types) {
if (t.equals(typeName)) {
sb.append("=" + arg+"; ");
}
}
if (clazzFlag) {
sb.append(getFieldsValue(arg));
}
}
return sb.toString();
}
/**
* 得到方法参数的名称
* @param cls
* @param clazzName
* @param methodName
* @return
* @throws NotFoundException
*/
@SuppressWarnings("rawtypes")
private static String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException{
ClassPool pool = ClassPool.getDefault();
//ClassClassPath classPath = new ClassClassPath(this.getClass());
ClassClassPath classPath = new ClassClassPath(cls);
pool.insertClassPath(classPath);
CtClass cc = pool.get(clazzName);
CtMethod cm = cc.getDeclaredMethod(methodName);
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if (attr == null) {
// exception
}
String[] paramNames = new String[cm.getParameterTypes().length];
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < paramNames.length; i++){
paramNames[i] = attr.variableName(i + pos); //paramNames即参数名
}
return paramNames;
}
/**
* 得到参数的值
* @param obj
*/
public static String getFieldsValue(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
String typeName = obj.getClass().getTypeName();
for (String t : types) {
if(t.equals(typeName))
return "";
}
StringBuilder sb = new StringBuilder();
sb.append("【");
for (Field f : fields) {
f.setAccessible(true);
try {
for (String str : types) {
if (f.getType().getName().equals(str)){
sb.append(f.getName() + " = " + f.get(obj)+"; ");
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
sb.append("】");
return sb.toString();
}
/**
* 获取参数的名称
* @param point
* @return
* @throws Exception
*/
public static String getParameterNames(JoinPoint point) throws Exception {
String classType = point.getTarget().getClass().getName();
Class<?> clazz = Class.forName(classType);
String clazzName = clazz.getName();
// String clazzSimpleName = clazz.getSimpleName();
String methodName = point.getSignature().getName();
String[] paramNames = getFieldsName(SystemLogAspect.class, clazzName, methodName);
String logContent = writeLogInfo(paramNames, point);
return logContent;
}
}
5.在使用的方法上加入注解
@SystemMethodLog(description = "庭审问题")