pointcut xxxPointcut():execution(void H*.say*())
AOP是从程序运行角度考虑程序的流程,提取业务处理的过程的切面,AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。
AOP框架并不会与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(PointCut),而不是某个具体类的耦合。
各个步骤之间良好的隔离。
源代码无关。
注:Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理。从而提高了更多的复用。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${srping.version}version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.7.2version>
dependency>
实现AOP的切面主要有以下几个要素:
package com.lidong.util;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.mongodb.BasicDBObject;
/**
1. 保存请求数据到mongodb
2. @author lidong
3. @createTime 2016年12月24日
*/
@Aspect
@Order(1)
@Component
public class WebLogAspect {
private Logger logger = Logger.getLogger("mongodb");
ThreadLocal startTime = new ThreadLocal<>();
HttpServletRequest request;
JoinPoint mJoinPoint;
@Pointcut("execution (* com.lidong.*.controller.*.*(..))")
public void webLog(){}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
request = attributes.getRequest();
mJoinPoint = joinPoint;
}
private BasicDBObject getBasicDBObject(HttpServletRequest request, JoinPoint joinPoint) {
BasicDBObject r = new BasicDBObject();
r.append("requestURL", request.getRequestURL().toString());
r.append("requestURI", request.getRequestURI());
r.append("queryString", request.getQueryString());
r.append("remoteAddr", request.getRemoteAddr());
r.append("remoteHost", request.getRemoteHost());
r.append("remotePort", request.getRemotePort());
r.append("localAddr", request.getLocalAddr());
r.append("localName", request.getLocalName());
r.append("method", request.getMethod());
r.append("headers", getHeadersInfo(request));
r.append("parameters", request.getParameterMap());
r.append("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
r.append("args", Arrays.toString(joinPoint.getArgs()));
return r;
}
/**
* 获取请求头部的信息
* @param request
* @return
*/
private Map getHeadersInfo(HttpServletRequest request) {
Map map = new HashMap<>();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
BasicDBObject logInfo = getBasicDBObject(request, mJoinPoint);
// 处理完请求,返回内容
logInfo.append("response",ret.toString());
logInfo.append("spend_time", (System.currentTimeMillis() - startTime.get()));
logger.info(logInfo);
}
}
注意:在WebLogAspect切面中,分别通过doBefore和doAfterReturning两个独立函数实现了切点头部和切点返回后执行的内容,若我们想统计请求的处理时间,就需要在doBefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算得到请求处理的消耗时间。
思路:log4j提供的输出器实现自Appender接口,要自定义appender输出到MongoDB,只需要继承AppenderSkeleton类,并实现几个方法即可完成。
<dependency>
<groupId>org.mongodbgroupId>
<artifactId>mongodb-driverartifactId>
<version>3.2.2version>
dependency>
编写MongoAppender类继承AppenderSkeleton,实现如下:
package com.lidong.util;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* 自定义appender实现输出日志MongoDB
* @author lidong
*
*/
public class MongoAppender extends AppenderSkeleton {
private MongoClient mongoClient;
private MongoDatabase mongoDatabase;
private MongoCollection logsCollection;
/**
* mongodb连接的url
*/
private String connectionUrl;
/**
* 数据库的名称
*/
private String databaseName;
/**
* 集合的名称
*/
private String collectionName;
@Override
protected void append(LoggingEvent loggingEvent) {
if(mongoDatabase == null) {
MongoClientURI connectionString = new MongoClientURI(connectionUrl);
mongoClient = new MongoClient(connectionString);
mongoDatabase = mongoClient.getDatabase(databaseName);
logsCollection = mongoDatabase.getCollection(collectionName, BasicDBObject.class);
}
//将日志插入到集合
logsCollection.insertOne((BasicDBObject) loggingEvent.getMessage());
}
@Override
public void close() {
if(mongoClient != null) {
mongoClient.close();
}
}
@Override
public boolean requiresLayout() {
return false;
}
public MongoClient getMongoClient() {
return mongoClient;
}
public void setMongoClient(MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
public MongoDatabase getMongoDatabase() {
return mongoDatabase;
}
public void setMongoDatabase(MongoDatabase mongoDatabase) {
this.mongoDatabase = mongoDatabase;
}
public MongoCollection getLogsCollection() {
return logsCollection;
}
public void setLogsCollection(MongoCollection logsCollection) {
this.logsCollection = logsCollection;
}
public String getConnectionUrl() {
return connectionUrl;
}
public void setConnectionUrl(String connectionUrl) {
this.connectionUrl = connectionUrl;
}
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public String getCollectionName() {
return collectionName;
}
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
}
# mongodb输出
log4j.logger.mongodb=INFO, mongodb
log4j.appender.mongodb=com.lidong.util.MongoAppender
log4j.appender.mongodb.connectionUrl=mongodb://127.0.0.1:27017
log4j.appender.mongodb.databaseName=logs
log4j.appender.mongodb.collectionName=logs_request
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean class="com.lidong.util.WebLogAspect" />