Spring AOP
Spring AOP(面向切面编程)
-
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。
-
AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
-
AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
-
目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。
-
Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。
-
AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。
-
为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示。
| 名称 | 说明 |
|---|---|
| Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,可以被动态代理拦截目标类的方法。 |
| Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
| Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
| Target(目标) | 指代理的目标对象。 |
| Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 |
| Proxy(代理) | 指生成的代理对象。 |
| Aspect(切面) | 切入点和通知的结合。 |
动态代理
jdk动态代理
public static CustomerServer getBean() {
// 准备目标类
final CustomerServer customerDao = new CustomerServerImpl();
// 创建切面类实例
final Aspect myAspect = new Aspect();
// 使用代理类,进行增强
//Proxy.newProxyInstance()的
// 第一个参数是类加载器,
// 第二个参数是代理类的class,
// 第三个参数是InvocationHandler需要重写invoke方法
return (CustomerServer) Proxy.newProxyInstance(
Main.class.getClassLoader(),
new Class[]{CustomerServer.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
myAspect.myBefore(); // 执行前
Object obj = method.invoke(customerDao, args);
myAspect.myAfter(); // 执行后
return obj;
}
});
}
public static void main(String[] args) {
CustomerServer bean = getBean();
bean.add();
bean.delete();
bean.find();
bean.update();
}
cglib动态代理
-
JDK 动态代理使用起来非常简单,但是它也有一定的局限性,这是因为 JDK 动态代理必须要实现一个或多个接口,如果不希望实现接口,则可以使用 CGLIB 代理。
-
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多 AOP 框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。
public class CglibProxy {
public static CustomerServer getBean() {
// 准备目标类
final CustomerServer server = new CustomerServerImpl();
// 创建切面类实例
final Aspect myAspect = new Aspect();
// 生成代理类,CGLIB在运行时,生成指定对象的子类,增强
Enhancer enhancer = new Enhancer();
// 确定需要增强的类
enhancer.setSuperclass(server.getClass());
// 添加回调函数
enhancer.setCallback(new MethodInterceptor() {
// intercept 相当于 jdk invoke,前三个参数与 jdk invoke—致
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
myAspect.myBefore(); // 前增强
Object obj = method.invoke(server, args); // 目标方法执行
myAspect.myAfter(); // 后增强
return obj;
}
});
// 创建代理类
CustomerServer serverProxy = (CustomerServer) enhancer.create();
return serverProxy;
}
public static void main(String[] args) {
CustomerServer bean = getBean();
bean.add();
bean.delete();
bean.find();
bean.update();
}
}
Spring 通知类型
通过前面的学习可以知道,通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。
Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型。
| 名称 | 说明 |
|---|---|
| org.springframework.aop.MethodBeforeAdvice(前置通知) | 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。 |
| org.springframework.aop.AfterReturningAdvice(后置通知) | 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。 |
| org.aopalliance.intercept.MethodInterceptor(环绕通知) | 在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。 |
| org.springframework.aop.ThrowsAdvice(异常通知) | 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。 |
| org.springframework.aop.IntroductionInterceptor(引介通知) | 在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。 |
声明式 Spring AOP
Spring 创建一个 AOP 代理的基本方法是使用 org.springframework.aop.framework.ProxyFactoryBean,这个类对应的切入点和通知提供了完整的控制能力,并可以生成指定的内容。
ProxyFactoryBean 类中的常用可配置属性
| 属性名称 | 描 述 |
|---|---|
| target | 代理的目标对象 |
| proxyInterfaces | 代理要实现的接口,如果有多个接口,则可以使用以下格式赋值: ... |
| proxyTargetClass | 是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理 |
| interceptorNames | 需要植入目标的 Advice |
| singleton | 返回的代理是否为单例,默认为 true(返回单实例) |
| optimize | 当设置为 true 时,强制使用 CGLIB |
Aspect类
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("方法执行前");
//执行方法
Object proceed = methodInvocation.proceed();
System.out.println("方法执行后");
return proceed;
}
}
Spring配置文件
<!--目标类 -->
<bean id="userServer" class="com.server.impl.UserServerImpl" />
<!-- 通知 advice -->
<bean id="myAspect" class="com.aop.MyAspect" />
<!--生成代理对象 -->
<bean id="userServerProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--代理实现的接口 -->
<property name="proxyInterfaces" value="com.server.UserServer" />
<!--代理的目标对象 -->
<property name="target" ref="userServer" />
<!--用通知增强目标 -->
<property name="interceptorNames" value="myAspect" />
<!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
测试
public static void main(String[] args) {
ApplicationContext context = SpringUtil.getApplicationContext();
User user = context.getBean("user", User.class);
UserServer userServer = context.getBean("userServerProxy", UserServer.class);
userServer.getUserMsg(user);
}

浙公网安备 33010602011771号