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);
    }
posted @ 2020-05-13 20:48  continued258  阅读(97)  评论(0)    收藏  举报