一、简介
1.AOP定义
AOP就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充,是Spring 一个重要的核心。
2.AOP作用
AOP主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。
3.AOP应用场景
比较典型应用有:
4.AOP 实现原理
AOP 保证开发者不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的源代码,达到增强功能的目的。AOP可以将其分为两类:
4.AOP核心概念
在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用术语如下:
Spring AOP 通知分类:
5.Spring AOP的实现原理
Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。
JDK动态代理
Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。
JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口,另一个是Proxy类。
我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。代码如下所示:
/**
* 动态代理
*/
public class JdkProxyDemo implements InvocationHandler {
private Log log;
public JdkProxyDemo(Log log) {
this.log= log;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before (前置通知)");
Object result = null;
try {
result = method.invoke(log, args);
}catch (Exception e) {
System.out.println("e: " + e.getMessage());
throw e;
}finally {
System.out.println("after (后置通知)");
}
return result;
}
}
第二个Proxy类
我们可以通过这个类的newProxyInstance方法,返回一个代理对象。
生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的invoke方法。
public class DemoMain { public static void main(String[] args) {
//获取InvocationHandler对象 在构造方法中注入目标对象
InvocationHandler handler = new JdkProxyDemo(new Log());
//获取代理类对象
Log proxyLog = (Log)Proxy.newProxyInstance(DemoMain.class.getClassLoader(), new Class[]{Log.class}, handler);
//调用目标方法
proxyLog.request();
proxyLog.response();
}
JDK动态代理是JDK原生的,不需要任何依赖即可使用;
通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;
但是如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;
CGLib 动态代理
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。
cglib 针对类进行代理,我们先创建一个类实现 MethodInterceptor接口
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogMethodInterceptor implements MethodInterceptor {
/**
*
* @param obj 表示增强的对象,即实现这个接口类的一个对象
* @param method 表示要被拦截的方法
* @param args 表示要被拦截方法的参数
* @param proxy 表示要触发父类的方法对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理开始...");
return methodProxy.invokeSuper(o, objects);
}
}
测试代码:
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;
public class TestApp {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程
enhancer.setSuperclass(Log.class); // 设置enhancer对象的父类
enhancer.setCallback(new LogMethodInterceptor()); // 设置enhancer的回调对象
Log proxy= (Log)enhancer.create(); // 创建代理对象
// 通过代理对象调用目标方法
proxy.logDeal();
}
}
统一的日志切面代码如下:
import com.test.dao.DaoSupport;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.awt.geom.Area;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
* 日志切面实现
*/
@Component
@Aspect
public class LogService {
@Resource(name = "daoSupport")
private DaoSupport dao;
public LogService() {
System.out.println("Aop");
}
/**
* 切点
*/
@Pointcut("@annotation(com.test.service.logAop.MethodLog)")
public void methodCachePointcut() { }
/**
* 切面
*
* @param point
* @throws Throwable
*/
@Around("methodCachePointcut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
Calendar ca = Calendar.getInstance();
String operDate = df.format(ca.getTime());
String loginName;
String name;
String methodRemark = getMthodRemark(point);
String methodName = point.getSignature().getName();
String packages = point.getThis().getClass().getName();
if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB动态生成的类
try {
packages = packages.substring(0, packages.indexOf("$$"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
String operatingcontent = "";
Object[] method_param = null;
Object object;
try {
method_param = point.getArgs(); //获取方法参数
object = point.proceed();
} catch (Exception e) {
throw e;
}
Area area = (Area) method_param[0];
return object;
}
/**
* 方法异常时调用
* @param ex
*/
public void afterThrowing(Exception ex) {
System.out.println("afterThrowing");
System.out.println(ex);
}
/**
* 获取方法中的中文备注
*
* @param joinPoint
* @return
* @throws Exception
*/
public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] method = targetClass.getMethods();
String methode = "";
for (Method m : method) {
if (m.getName().equals(methodName)) {
Class[] tmpCs = m.getParameterTypes();
if (tmpCs.length == arguments.length) {
MethodLog methodCache = m.getAnnotation(MethodLog.class);
if (methodCache != null) {
methode = methodCache.remark();
}
break;
}
}
}
return methode;
}
}
了解完后,想必大家也会对Spring AOP有一个更深的理解。