AOP切面编程

AOP的实现底层实际上即为反射,JDK中的反射类java.lang.reflect.Proxy是Java中唯一可以访问调度器的类。类似地,常见的动态代理库cglib也是通过反射机制实现了动态代理的封装。技术成熟度相对较高的AspectJ和Spring AOP会在底层实现一套reflect机制,区别是两者对规范实现如何定义而已。

无论是AspectJ还是Spring AOP,所有这些AOP编程都是基于反射和运行时的动态代理控制实现的。下面通过Proxy和Cglib来实现自己的动态代理切面。

假设我要实现一套基于注解的Trasaction事务管理,通过AOP动态代理来实现。注解和接口实现如下

/**
 * @author Barudisshu
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TransactionAnn {
    String[] name() default {};

    boolean[] readonly() default false;

    int[] level() default Connection.TRANSACTION_READ_COMMITTED;

    String value() default "";
}
/**
 * @author Barudisshu
 */
public interface Transaction {
    void open();
    void rollBack();
    void commit();
    void closeIfStillOpen();
}

Proxy动态代理

动态代理的机制如下

实现一个代理工厂

/**
 * 代理工厂类
 * @author Barudisshu
 */
public class AspectFactory {
    /**单例实现*/
    private AspectFactory(){

    }
    @SuppressWarnings("unchecked")
    public static <T> T getInstance(T target,Aspect... aspects){
        AspectHandler handler = new AspectHandler(target, aspects);
        Class clazz = target.getClass();
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), clazz.getInterfaces(), handler);

    }
}

封装并继承调度器

/**
 * @author Barudisshu
 */
public class AspectHandler implements InvocationHandler {

  private Object target = null;
  private Aspect[] aspects = null;
  private int index = -1;

  public AspectHandler(int index, Object target, Aspect[] aspects) {
    this.index = index;
    this.target = target;
    this.aspects = aspects;
  }

  public AspectHandler(Object target, Aspect[] aspects) {
    this.target = target;
    this.aspects = aspects;
  }

  public Object getTarget() {
    return target;
  }

  public void setTarget(Object target) {
    this.target = target;
  }

  public Aspect[] getAspects() {
    return aspects;
  }

  public void setAspects(Aspect... aspects) {
    this.aspects = aspects;
  }

  /**
   * 委托方法
   *
   * @param proxy  代理对象
   * @param method 代理方法
   * @param args   方法参数
   */
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    if (index == -1) {
      return new AspectHandler(0, target, aspects).invoke(proxy, method, args);
    }
    Object result = null;
    if (index < aspects.length) {
      result = aspects[index++].aspect(this, proxy, method, args);
    } else if (index++ == aspects.length) {
      result = method.invoke(target, args);
    }
    return result;
  }

}
/**
 * @author Barudisshu
 */
public interface Aspect {

  public Object aspect(InvocationHandler ih, Object proxy, Method method, Object[] args) throws Throwable;

}

实现真实对象,真实对象继承Aspect,并将通过AspectHandler被委托代理

/**
 * @author Barudisshu
 */
public class TransactionAspect implements Aspect {

    public Object aspect(InvocationHandler ih, Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;

        TransactionAnn transactionAnn = method.getAnnotation(TransactionAnn.class);
        if (transactionAnn != null) {
            Transaction transaction = new TransactionAdapter();

            // TODO: 获取注解信息或设置值
            String[] names = transactionAnn.name();
            String values = transactionAnn.value();
            int[] level = transactionAnn.level();
            boolean[] readOnly = transactionAnn.readonly();

            try {

                //执行操作
                result = ih.invoke(proxy, method, args);

                // TODO: 处理
                transaction.open();
                transaction.commit();
            }catch (Throwable t) {

                // if exception
                transaction.rollBack();

                Throwable cause = t.getCause();
                if (cause != null) {
                    // you should define your own Exception here
                    throw new TransactionException(cause.getMessage(), cause);
                } else {
                    throw new TransactionException(t.getMessage(), t);
                }

            }finally {
                // finally
                transaction.closeIfStillOpen();
            }
        } else {
            //执行操作
            result = ih.invoke(proxy, method, args);
        }
        return result;
    }
}

根据时序图,我们接下来只需要创建相应的客户端进行调度即可

/**
 * @author Barudisshu
 */
public class AspectClient {

    public static void main(String[] args) {

        PersistenceService persistenceService = AspectFactory.getInstance(new PersistenceServiceImpl(), new TransactionAspect());

        persistenceService.save(3,"321");
    }
}
/**
 * @author Barudisshu
 */
public interface PersistenceService {

    @TransactionAnn(name = "save")
    void save(long id,String data);

    @TransactionAnn(level = Connection.TRANSACTION_READ_UNCOMMITTED)
    String load(long id);
}
/**
 * @author Barudisshu
 */
public class PersistenceServiceImpl implements PersistenceService {

    @Override
    public void save(long id, String data) {
        throw new TransactionException("异常则回滚!!!");
    }

    @Override
    public String load(long id) {
        return null;
    }
}

在这里,我们显示抛出一个
运行时异常TranscationException,运行客户端将执行transaction.rollBack()方法。

既然有JDK的Proxy代理了,为什么还需要使用CGLIB之类的其他外部库?实际上Proxy只是对代理的简单实现,在实际使用中还不能满足需求,如上述设计中,Proxy的newProxyInstance静态方法是通过clone实现的,可能需要考虑是浅拷贝还是深拷贝的问题。

若以CGLIB实现,则问题会显得简单的多。

CGLIB动态代理

创建单例的代理工厂

public class CGLIBProxyFactory {

    private CGLIBProxyFactory() {
    }

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target, AOPInterceptor interceptor) {

        MethodInterceptor methodInterceptor =
            new CGLIBMethodInterceptor(interceptor);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setInterfaces(target.getClass().getInterfaces());
        enhancer.setCallback(methodInterceptor);
        return (T) enhancer.create();
    }
}

在Cglib则是通过方法拦截器直接实现,只需要继承拦截器即可

/**
 * @author Barudisshu
 */
public class CGLIBMethodInterceptor implements MethodInterceptor {

    private AOPInterceptor interceptor;

    public CGLIBMethodInterceptor(AOPInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        try {
            interceptor.before(method, args);
            Object returnValue = methodProxy.invokeSuper(object, args);
            interceptor.after(method, args);
            return returnValue;
        } catch (Throwable t) {
            interceptor.afterThrowing(method, args, t);
            throw t;
        } finally {
            interceptor.afterFinally(method, args);
        }
    }
}
/**
 * @author Barudisshu
 */
public interface AOPInterceptor {

    void before(Method method, Object[] args);
    void after(Method method, Object[] args);
    void afterThrowing(Method method, Object[] args, Throwable throwable);
    void afterFinally(Method method, Object[] args);
}

添加事务处理的真实对象

/**
 * @author Barudisshu
 */
public class TransactionCglib implements AOPInterceptor {

    private Transaction transaction;

    @Override
    public void before(Method method, Object[] args) {
        if (isRequiresNew(method)) {
            transaction = new TransactionAdapter();
            transaction.open();
        }
    }

    @Override
    public void after(Method method, Object[] args) {
        if (transaction != null) {
            transaction.commit();
        }
    }

    @Override
    public void afterThrowing(Method method, Object[] args, Throwable throwable) {
        if (transaction != null) {
            transaction.rollBack();
        }
    }

    @Override
    public void afterFinally(Method method, Object[] args) {
        if (transaction != null) {
            transaction.closeIfStillOpen();
            transaction = null;
        }
    }

    protected boolean isRequiresNew(Method method) {
        TransactionAnn transactionAnnotation = method.getAnnotation(TransactionAnn.class);

        if (transactionAnnotation != null) {
            if ("REQUIRES_NEW".equals(transactionAnnotation.value())) {
                return true;
            }
        }

        return false;
    }
}

OK,十分简单,Transaction真实对象将被MethodInterceptor封装,并在Enhancer中实现回调。下面只需要添加相应的客户端调用即可

/**
 * @author Barudisshu
 */
public class CglibClient {

    public static void main(String[] args) {
        PersistenceService persistenceService = CGLIBProxyFactory.createProxy(new PersistenceServiceImpl(), new TransactionCglib());

        persistenceService.save(3L, "321");
    }
}

注意和Proxy中实现的不同

/**
 * @author Barudisshu
 */
public class PersistenceServiceImpl implements PersistenceService {

    @TransactionAnn("REQUIRES_NEW")
    public void save(long id, String data) {
        System.out.println("Generally save ... ");
    }

    @TransactionAnn("NOT_SUPPORTED")
    public String load(long id) {
        return null;
    }
}

鉴于AspectJ和Spring AOP切面内容比较多,下面简单讲一下AspectJ的配置。

AspectJ部署

  1. 到eclipse官网下载http://www.eclipse.org/aspectj/downloads.php,直接用7zip解压或双击安装。
  2. 将aspectj1.x/lib/aspectjrt.jar拷贝到%JAVA_HOME%\jre\lib\ext中,注意这步很重要,否则会说找不到路径。
  3. 在IDE中设置Ajc编译器路径(编译器用于将aj文件编译成.class文件),以IDEA为例

编译器一定是aspectjtools,不要选错。

OK,差不多了,写个测试例子

/**
 * @author Barudisshu
 */
public aspect HelloWorld {

    pointcut callPointcut():
        call(void cn.barudisshu.aop.MyClass.foo(int,java.lang.String));

    before():callPointcut(){
        System.out.println("Hello World");
        System.out.println("In the advice attached to the call pointcut");
    }
    after():callPointcut(){
        System.out.println("All things has done");
    }
}

方法切入点

/**
 * @author Barudisshu
 */
public class MyClass {

    public void foo(int number,String name){
        System.out.println("Inside foo (int,String)");
    }

    public static void main(String[] args) {
        // Create an instance of MyClass
        MyClass myObject = new MyClass();
        // Make the call to foo
        myObject.foo(1,"Russ Miles");
    }
}

如下代码将输出

Hello World
In the advice attached to the call pointcut
Inside foo (int,String)
All things has done
时间: 2024-10-03 10:36:15

AOP切面编程的相关文章

动态代理和AOP切面编程

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /* * 动态代理和AOP切面编程 */ //定义一个接口 interface Person { void eat(); void breath(); } // 定义一个实现接口的实现类,即为被代理类 class Student implements Person { @Overr

AOP基础知识及AOP切面编程之注释方法、xml配置方法

<span style="font-family: 微软雅黑; font-size: 10.5pt; letter-spacing: 0pt; ">AOP概念</span> Aspect(切面):它跟类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是对横切性关注点的抽象 joinpoint(连接点):所谓连接点就是被拦截到的点,在spring中,这些点是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器

Spring AOP 切面编程记录日志和接口执行时间

最近客户现在提出系统访问非常慢,需要优化提升访问速度,在排查了nginx.tomcat内存和服务器负载之后,判断是数据库查询速度慢,进一步排查发现是因为部分视图和表查询特别慢导致了整个系统的响应时间特别长.知道了问题之后,就需要对查询比较慢的接口进行优化,但哪些接口需要优化.哪些不需要呢?只能通过日志里的执行时间来判断,那么如何才能知道每一个接口的执行时间呢? 如果想学习Java工程化.高性能及分布式.深入浅出.微服务.Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级

SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务

本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.AOP切面编程 1.什么是AOP编程 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部

SPRING学习(十九)--基于注解的AOP切面编程

上篇中介绍了基于XML配置的AOP切面编程,除了XML配置AOP切面编程外,还可以通过注解方式实现AOP切面编程,本篇通过一个小例子来介绍基于注解的AOP编程. 1.在spring中使用AOP变成,不止要导入spring-aop.jar,还需要导入spring-aspects.jar.aspectjweaver.jar和aopalliance.jar,但是aspectjweaver.jar被spring-aspects.jar依赖,aopalliance.jar被spring-aop.jar依赖

Spring AOP 切面编程的方法

spring aop的使用分为两种,一种是使用注解来实现,一种是使用配置文件来实现. 先来简单的介绍一下这两种方法的实现,接下来详细的介绍各处的知识点便于查阅.目录如下: 1.基于注解实现spring aop 2.基于配置文件实现spring aop 3.增强的分类 4.切点表达式的构成 1.基于注解来实现的方法 基于注解并不意味着不需要配置文件,只不过是在配置文件中所需要的配置很少.切面编程,显然我们需要一个增强类,一个被增强的普通类. 配置文件中需要添加以下的内容 xmlns:aop="ht

Spring Aop 切面编程

需求:之前的动态选择数据库,和现在的将某个service层的方法接入cat,都需要用到切面编程. 参考文献: http://www.blogjava.net/supercrsky/articles/174368.html http://my.oschina.net/itblog/blog/211693 一.简介 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切

AOP 切面编程

简介 如果你很熟悉面向方面编程(AOP),你就会知道给代码增加“切面”可以使代码更清晰并且具有可维护性.但是AOP通常都依赖于第三方类库或者硬编码的.net特性来工作.虽然这些实现方式的好处大于它们的复杂程度,但是我仍然在寻找一种实现AOP的更为简单的方式,来试我的代码更为清晰.我将它们单独移出来,并命名为AspectF. Aspect Oriented Programming (AOP)的背景 “切面”指的是那些在你写的代码中在项目的不同部分且有相同共性的东西.它可能是你代码中处理异常.记录方

Spring 框架基础(04):AOP切面编程概念,几种实现方式演示

本文源码:GitHub·点这里 || GitEE·点这里 一.AOP基础简介 1.切面编程简介 AOP全称:Aspect Oriented Programming,面向切面编程.通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.核心作用:可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的复用性和开发效率.AOP提供了取代继承和委托的一种新的方案,而且使用起来更加简洁清晰,是软件开发中的一个热点理念. 2.AOP术语 (1).通知类型:Advice