美文网首页我爱编程
Spring的AOP原理以及简单实现可配置的AOP

Spring的AOP原理以及简单实现可配置的AOP

作者: 纳米君 | 来源:发表于2018-05-22 23:35 被阅读49次

首先,什么是AOP?
AOP(Aspect Oriented Programming面向切面编程),它是对OOP(面向对象编程)的一种补充。在OOP中,我们把事物纵向抽象成一个个对象,而往往需要对这些对象进行一些横向操作(比如事务管理、日志记录、权限等)时,就需要AOP了。

Spring的AOP是基于JDK的动态代理cglib动态代理实现的。JDK的动态代理是代理那些实现接口的类,而cglib动态代理是代理那些继承父类的类。

所谓的切面aspect,是由切入点pointcut和通知advice组成。pointcut是指需要拦截哪些类的哪些方法,advice是指在这些方法执行前后可以进行额外的操作。

先看看JDK的动态代理:
因为是代理实现接口的类,所以先定义一个接口和实现类:

public interface Hello {

    String sayHello(String name);
}

public class HelloImpl implements Hello {

    @Override
    public String sayHello(String name) {
        return name + "2333";
    }
}

然后定义一个类,需要实现接口InvocationHandler,而且持有代理的目标类的引用target,如下:

public class HelloProxy implements InvocationHandler {

    private Object target;

    public HelloProxy(Object target) {
        this.target = target;
    }

    public Object getProxyBean() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  
        Object value = method.invoke(target, args);
        return value;
    }
}

最后写个测试类测试一下:

public class DynamicProxyTest {

    public static void main(String[] args) {
        Hello proxy = (Hello) new HelloProxy(new HelloImpl()).getProxyBean();
        System.out.println(proxy.sayHello("Fuck"));
    }
}

打印结果:Fuck2333

Java会动态生成一个代理类com.sun.proxy.$Proxy0,通过反编译,可以看到$Proxy0实现了Hello接口,持有HelloProxy的实例对象引用handler,而且会通过反射获取接口Hello的方法和参数,所以当执行proxy.sayHello("Fuck")这行代码时,实际上调用的是$Proxy0类中的sayHello方法,该方法代码如下:

public final String sayHello(String string) {
        try {
            return (String)this.handler.invoke((Object)this, "sayHello", new Object[]{string});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }

所以最终调用的是HelloProxy中的invoke方法。

了解了动态代理之后,我们就可以实现一个简单的AOP工具了。

  1. 我们得明确切入点和通知,切入点就是测试类中bean调用的方法,通知如下:
public interface Advice {

    void beforeMethod(Method method);

    void afterMethod(Method method);

}

public class MyAdvice implements Advice {

    @Override
    public void beforeMethod(Method method) {
        System.out.println("beforeMethod");
    }

    @Override
    public void afterMethod(Method method) {
        System.out.println("afterMethod");
    }
}
  1. 创建一个代理工厂类,需持有代理目标类和通知Advice引用,对外提供getProxyBean方法获取代理类实例,如下:
public class ProxyBeanFactory implements InvocationHandler {

    private Object target;
    private Advice advice;

    public Object getProxyBean() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //invoke方法执行前
        advice.beforeMethod(method);
        Object value = method.invoke(target, args);
        //invoke方法执行后
        advice.afterMethod(method);
        return value;
    }

    public Object getTarget() {
        return target;
    }

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

    public Advice getAdvice() {
        return advice;
    }

    public void setAdvice(Advice advice) {
        this.advice = advice;
    }
}
  1. 创建一个BeanFactory类,对外提供getBean方法获取bean实例,配置的bean不一定是代理工厂类。
public class BeanFactory {

    static Properties props = new Properties();

    public BeanFactory(InputStream is) {
        try {
            props.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String name) {
        Object bean = null;
        try {
            bean = Class.forName(props.get(name).toString()).newInstance();
            if (bean instanceof ProxyBeanFactory) {
                // 是代理工厂类
                ProxyBeanFactory proxyBeanFactory = (ProxyBeanFactory) bean;
                Object target = Class.forName(props.get(name + ".target").toString()).newInstance();
                Advice advice = (Advice) Class.forName(props.get(name + ".advice").toString()).newInstance();
                proxyBeanFactory.setTarget(target);
                proxyBeanFactory.setAdvice(advice);
                // 获取代理实例
                bean = proxyBeanFactory.getProxyBean();
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return bean;
    }

}
  1. 创建一个config.properties配置文件,配置Bean类、代理目标类和通知类。可以配置proxy来决定是否走代理。
proxy=cn.tl.aop.ProxyBeanFactory
#proxy=java.util.ArrayList
proxy.target=java.util.ArrayList
proxy.advice=cn.tl.aop.MyAdvice

测试类如下:

public class AopTest {

    public static void main(String[] args) {
        InputStream is = AopTest.class.getResourceAsStream("config.properties");
        BeanFactory beanFactory = new BeanFactory(is);
        Object bean = beanFactory.getBean("proxy");
        List list = (List) bean;
        list.add(233);
    }
}

打印结果:

beforeMethod
afterMethod

说明通知Advice起作用了,当然这是一种很简略的实现方式。
以上。

相关文章

网友评论

    本文标题:Spring的AOP原理以及简单实现可配置的AOP

    本文链接:https://www.haomeiwen.com/subject/tckwdftx.html