关于Spring aop的理解

前言:

最怕面试问到这种开放性的问题,毕竟仁者见仁智者见智,可能你说的面试官并不会觉得你说到他想要听的点子上。好了废话不多了,开始我们的正文:

什么是 AOP?

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。通俗点来说就是非业务代码对主业务的一种补充(增强),而不侵入原业务代码中实现解耦。

AOP中的一些术语

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、- - 异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

实际开发中难免会遇到添加非业务功能的代码,如统计、日志、权限验证、效率测试等,功能通用不可能在每个类中都去写一遍,这时候就需要“代理类”,最早的方法主要是两种:1-聚合织入;2-继承。看一下伪代码

传统AOP的实现

  1. 聚合:
class A{
		private C;
		function a{
		    C.c();
			print("a");
		}
}

class B{
		private C;
		function b{
			C.c();
			print("b");
		}
}
// 公用方法类
class C{
		// 增强方法
		function c{
			print("a");
		}
}
  1. 继承
class A extends C{
   	function a{
   	    super.c();
   		print("a");
   	}
}

class B extends C{
   	function b{
   		super.c();
   		print("b");
   	}
}
// 公用方法类
class C{
   	// 增强方法
   	function c{
   		print("a");
   	}
}

很明显,上述方式代码过于耦合。

Spring AOP五种通知(advice)方式:

  • @Before:前置通知,在调用目标方法之前执行通知定义的任务
  • @After:后置通知,在目标方法执行结束后,无论执行结果如何都执行通知定义的任务
  • @After-returning:后置通知,在目标方法执行结束后,如果执行成功,则执行通知定义的任务
  • @After-throwing:异常通知,如果目标方法执行过程中抛出异常,则执行通知定义的任务
  • @Around:环绕通知,在目标方法执行前和执行后,都需要执行通知定义的任务。

Spring AOP代理原理的实现
这里直接用代码来说明

/** 
 * 我们定义的接口
 * @author lix 
 * @Date 2020/5/30 
 */ 
public interface MyInterface {

    void sayHello();

}
/**************************华丽的分隔符******************************/
/**
 * 我们的接口实现类
 * @author lix
 * @Date 2020/5/30
 */
public class MyInterfaceImpl implements MyInterface {

    /**
     * 需要被代理的方法
     */
    @Override
    public void sayHello() {
        System.out.println("---execute sayHello---");
    }

}
/**************************华丽的分隔符******************************/
/**
 * 代理对象
 * @author lix
 * @Date 2020/5/30
 */
public class ProxyObject implements MyInterface{

    /**
     * 被代理的接口
     */
    private MyInterface myInterface;

    public ProxyObject(MyInterface myInterface) {
        // 这里传进来的是MyInterface myInterface = new MyInterfaceImpl();
        this.myInterface = myInterface;
    }

    /**
     * 代理的方法
     */
    @Override
    public void sayHello() {
        // @Before
        System.out.println("---before execute do somethings---");
        try {
            myInterface.sayHello();
        }catch (Exception e){
            // @After-throwing
            System.out.println("---catch exception do somethings---");
            throw e;
        }finally {
            // @After
            System.out.println("---after execute do somethings---");
        }
    }

}

意思是这么个意思,但是实际上的JDK代理类是通过反射执行的,后面会讲到。

看看 JDK动态代理的实现 ,这里比较重要的两个类:

java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler

这里直接使用上面的代码改造:

/**
 * 我们定义的接口
 * @author lix
 * @Date 2020/5/30
 */
public interface MyInterface {

    void test();

    void sayHello();

}
/**************************华丽的分隔符******************************/
/**
 * 我们的接口实现类
 * @author lix
 * @Date 2020/5/30
 */
public class MyInterfaceImpl implements MyInterface {

    /**
     * 需要被代理的方法
     */
    @Override
    public void test() {
        System.out.println("---my test---");
    }
    
    /**
     * 需要被代理的方法
     */
    @Override
    public void sayHello() {
        System.out.println("---execute sayHello---");
    }

}
/**************************华丽的分隔符******************************/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/** 
 * 动态代理类的实现
 * @author lix 
 * @Date 2020/5/30 
 */ 
public class JdkDynamicProxy implements InvocationHandler {

    private MyInterface object;

    public JdkDynamicProxy(MyInterface object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // @Before
        System.out.println("---JdkDynamicProxy before execute do somethings---");
        Object invoke = null;
        try {
            invoke = method.invoke(object, args);
        }catch (Exception e){
            // @After-throwing
            System.out.println("---JdkDynamicProxy catch exception do somethings---");
            throw e;
        }finally {
            // @After
            System.out.println("---JdkDynamicProxy after execute do somethings---");
        }
        return invoke;
    }
}

/**************************华丽的分隔符******************************/
import java.lang.reflect.Proxy;

public class SpringContainer {

    /**
     * 通过spring容器被调用
     * @param args
     */
    public static void main(String[] args) {
        MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(SpringContainer.class.getClassLoader(),
                new Class[]{MyInterface.class}, new JdkDynamicProxy(new MyInterfaceImpl()));
        myInterface.sayHello();
    }

}

执行结果:
在这里插入图片描述
可能这么看来优势不明显,但是如果我们被代理的接口有很多方法,如果按照传统集合的的形式我们就要这么写:
在这里插入图片描述
出现大量的重复代码,而动态代理则不需要再对队里实现类做修改,直接调用方法就能实现代理的增强:在这里插入图片描述

那么Spring在哪里做的代理?

在Spring容器启动后首先要对管理的类进行实例化,之后才会对实例化后的对象做加工处理,空口无凭,来跟一下代码:
这里我提供一个测试类:
在这里插入图片描述
发现该类在进入一个org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization的方法后发生了改变。
代理前:
在这里插入图片描述
代理后:
在这里插入图片描述
也就是说我们的后置处理器对原对象进行了加工,那么具体是哪个处理器呢?
在这里插入图片描述
通过类名我们大概锁定到这个类,于是便跟进去(这里直接写省略一些堆栈的执行图片描述):

  1. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
  2. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
  3. org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy看到这里已经确认是spring做代理的代码
  4. org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
  5. org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
  6. org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy见下图判断代理类型,果然这里是对的
  7. org.springframework.aop.framework.AopProxy#getProxy(java.lang.ClassLoader)
    在这里插入图片描述
    最终返回的Object就是我们的代理对象。代码分析到此结束

这里不对JDK具体的生成代理类的方式深究,感兴趣的小伙伴可以看看Proxy的相关源码,重点是getProxyClass0方法。这里贴一份JDK动态代理类生成的类。开启保留JDK代理class文件:System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);

public final class $Proxy0 extends Proxy implements MyInterface {
    private static Method m1;
    private static Method m4;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void test() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("MyInterface").getMethod("sayHello");
            m3 = Class.forName("MyInterface").getMethod("test");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

Spring AOP提供的两种实现

(1)JDK动态代理(基于反射调用,动态生成代理类的实现类,实现类会继承Proxy类,所以为什么必须要有接口)
(2)Cglib动态代理(基于继承,动态编译生成代理类的子类,不依赖接口)
在这里插入图片描述
这里如果有接口,通常走JDK的动态代理,如果没有接口则走cglib的代理,但是还有种情况,就是强制使用cglib代理,可以使用配置指定:

xml中使用:
<aop:aspectj-autoproxy proxy-target-class="true"/>

springboot在启动类上添加注解:
@EnableAspectJAutoProxy(proxyTargetClass = true)

Spring 中AOP的应用:
1.事务管理
2.缓存管理
3.权限控制
…等等…

提问:
Spring是如何知道哪些类需要被代理的?
SpringAOP是如何实现链式调用的?

到此本篇文章结束,SpringAop实战请转义至《如何自定义一个Spring Aop切面功能注解》,内容包括springaop配置方法实现,注解通配符形式实现,注解标记注解方式实现,标记类实现所有公共方法的代理。。。

只是对aop简单的做一下分析记录,欢迎大佬补充指教。









补充一份CGLIB Enhancer API

Object create()
必要时生成一个新类,并使用指定的回调(如果有的话)创建一个新的对象实例。
Object create(Class[] argumentTypes, Object[] arguments)
必要时生成一个新类,并使用指定的回调(如果有的话)创建一个新的对象实例。
static Object create(Class type, Callback callback)
帮助器方法来创建拦截的对象。
static Object create(Class superclass, Class[] interfaces, Callback callback)
帮助器方法来创建拦截的对象。
static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)
帮助器方法来创建拦截的对象。
Class createClass()
如果需要,生成一个新类并返回它,而不创建一个新实例。
protected void filterConstructors(Class sc, List constructors)
从超类中筛选构造函数列表。
protected Object firstInstance(Class type)
不应该在常规流中调用此方法。
protected Class generate(AbstractClassGenerator.ClassLoaderData data)
void generateClass(org.objectweb.asm.ClassVisitor v)
protected ClassLoader getDefaultClassLoader()
static void getMethods(Class superclass, Class[] interfaces, List methods)
使用指定的超类和接口,查找将由增强器生成的类扩展的所有方法。
protected ProtectionDomain getProtectionDomain()
返回定义类时要使用的保护域。
static boolean isEnhanced(Class type)
确定是否使用增强器生成了一个类。
protected Object nextInstance(Object instance)
static void registerCallbacks(Class generatedClass, Callback[] callbacks)
在通过反射创建生成类的新实例之前,调用此方法来注册要使用的回调数组。
static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks)
与registerCallbacks(java.lang.Class,net.sf.cglib.proxy.Callback [])相似,但适用于多个线程将创建所生成类的实例的情况。
void setCallback(Callback callback)
设置要使用的单个回调。
void setCallbackFilter(CallbackFilter filter)
设置用于将生成的类的方法映射到特定回调索引的CallbackFilter。
void setCallbacks(Callback[] callbacks)
设置要使用的回调数组。
void setCallbackType(Class callbackType)
设置要使用的回调类型。
void setCallbackTypes(Class[] callbackTypes)
设置要使用的回调类型数组。
void setInterceptDuringConstruction(boolean interceptDuringConstruction)
设置是否将拦截从代理的构造函数中调用的方法。
void setInterfaces(Class[] interfaces)
设置要实现的接口。
void setSerialVersionUID(Long sUID)
将静态serialVersionUID字段插入到生成的类中。
void setSuperclass(Class superclass)
设置生成的类将扩展的类。
void setUseFactory(boolean useFactory)
设置增强的对象实例是否应实现Factory接口。
protected Object unwrapCachedValue(Object cached)
protected Object wrapCachedClass(Class klass)

扩展资料:Cglib 如何实现多重代理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值