一、注解
1.@RequestMapping:
1.1、@RequestMapping是什么?
用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。用来处理请求地址映射的注解,可用于方法上。
@RequestMapping 修饰类 :
类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录;
方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。
若类定义处未标注 @RequestMapping,则方法处标记的 URL相对于 WEB 应用的根目录
返回ModelAndView时的url会根据你的 @RequestMapping实际情况组成。
如果类上没有映射,那么url直接就是方法的映射;否则url为类上+方法上映射路径组合。
对应项目jsp位置则是一级路径对应一级文件目录。
如url为/default/index
对应项目中webapp/default/index.jsp
1.2、@
RequestMapping注解的属性:
1.2.1、@RequestMapping都有哪些属性?
value:指定请求的实际地址,指定的地址可以是URI Template 模式;
method: 指定请求的method类型, GET、POST、PUT、DELETE等;
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
params: 指定request中必须包含某些参数值时,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
name:
path:
2.
@ArchivesLog:自定义注解类
fileQuery为controller层的方法
@RequestMapping(value="/fileQuery")
@ArchivesLog(operationType="操作类型",operationName="方法作用")
public ModelAndView fileQuery(HttpServletRequest request, HttpServletResponse response,String ip){
return new ModelAndView("archives/fileQuery.ftl");
}
@Target({ElementType.TYPE}) 注解
ElementType 这个枚举类型的常量提供了一个简单的分类:注释可能出现在Java程序中的语法位置(这些常量与元注释类型(@Target)一起指定在何处写入注释的合法位置)
@Retention({RetentionPolicy.Runtime}) 注解
RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间
@Documented注解
Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
ArchivesLog:自定义注解类
import java.lang.annotation.*;
/**
* @author CB
* @Title: ArchivesLog
* @Package
* @Description: <日志打印注解类>
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ArchivesLog {
/** 要执行的操作类型比如:add操作 **/
public String operationType() default "";
/** 要执行的具体操作比如:添加用户 **/
public String operationName() default "";
}
Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持:
<aop:aspectj-autoproxy/>
通过以上配置,Spring就能发现用@Aspect注解的切面内并把它应用到目标对象上。
@component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)
ArchivesLogAspect:切面日志类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
@Aspect
@Component
public class ArchivesLogAspect {
private long startTimeMillis = 0; // 开始时间
private long endTimeMillis = 0; // 结束时间
private HttpServletRequest request = null;
/**
* @param joinPoint
* @Description: 方法调用后触发 记录结束时间
* @author CB
*/
@Around("within(自己的项目路径..*) && @annotation(ArchivesLog)")
public void after(JoinPoint joinPoint) {
startTimeMillis = System.currentTimeMillis();
request = getHttpServletRequest();
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = null;
try {
targetClass = Class.forName(targetName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = targetClass.getMethods();
String operationName = "";
String operationType = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs != null && clazzs.length == arguments.length && method.getAnnotation(ArchivesLog.class) != null) {
operationName = method.getAnnotation(ArchivesLog.class).operationName();
operationType = method.getAnnotation(ArchivesLog.class).operationType();
break;
}
}
}
endTimeMillis = System.currentTimeMillis();
//操作时间
String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);
String ip = getIpAddress(request);
String uri = request.getRequestURI();//返回请求行中的资源名称
String url = request.getRequestURL().toString();//获得客户端发送请求的完整url
String params = request.getQueryString();//返回请求行中的参数部分
String host = request.getRemoteHost();//返回发出请求的客户机的主机名
int port = request.getRemotePort();//返回发出请求的客户机的端口号。
System.out.println(ip);
System.out.println(url);
System.out.println(uri);
System.out.println(params);
System.out.println(host);
System.out.println(port);
System.out.println(" 操作人: " + request.getRemoteUser() + " 操作方法: " + operationName + " 操作时间: " + startTime
+ "operationType" + operationType + methodName + "用户ip:" + ip);
}
/**
* @param
* @return HttpServletRequest
* @Description: 获取request
* @author CB
*/
public HttpServletRequest getHttpServletRequest() {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
return request;
}
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
* 用户真实IP为: 192.168.1.110
* @param request
* @return
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
Sring配置文件
<!--AOP操作日志打印-->
<aop:aspectj-autoproxy proxy-target-class="true" />
通过配置织入@Aspectj切面
虽然可以通过编程的方式织入切面,但是一般情况下,我们还是使用spring的配置自动完成创建代理织入切面的工作。
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring
在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
@AspectJ语法基础
@AspectJ使用jdk5.0注解和正规则的AspectJ 5的切面表达式语言描述切面,由于spring只支持方法的连接点,所以spring仅支持部分aspectJ的切面语言,在这节时,我们将对AspectJ切点表达式语言进行必要的学习。
切点表达式函数
AspectJ 5的切点表达式由关键字和操作参数组成。如execution(*greeTo(..))的切点表达式,"execute"为关键字,而"*greeTo(..)"为操作参数。在这里,execution代表目标类执行某一方法,而"*greeTo(..)"是描述目标方法的匹配模式串,两者联合起来所表示的切点匹配目标类greeTo(..)方法的连接点。为了描述方便,我们将execution()称作函数,而将匹配串"*greeTo(..)"称作函数的入参。