Spring-AOP

面向对象编程oop,只能尽可能的减少重复的代码,但是无法避免重复代码的出现。面向对象是在类的基础上封装,对于多个类不同方法中重复的代码,无法优雅的解决复用问题。—AOP就是为了解决这个问题

AOP

在这里插入图片描述

图中的红框我们说它称为横切面,英文表示为 Aspect ,,它表示的是分布在一个 / 多个类的多个方法中的相同逻辑。利用动态代理,将这部分相同的逻辑抽取为一个独立的 Advisor 增强器,并在原始对象的初始化过程中,动态组合原始对象并产生代理对象,同样能完成一样的功能增强。在此基础上,通过指定增强的类名、方法名(甚至方法参数列表类型等),可以更细粒度的对方法增强。使用这种方式,可以在不修改原始代码的前提下,对已有任意代码的功能增强。而这种**针对相同逻辑的扩展和抽取,就是所谓的面向切面编程**(Aspect Oriented Programming,AOP)。

jdk原生动态代理

被代理的类必须实现接口。
场景
类A实现接口B。

代码中生成代理对象:

A a = new A();
Proxy.newProxyInstance(a.getClass().getClassLoader,a.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if ("自定义逻辑") {
                        return method.invoke(a, args);
                    }
                    return null;
                }
            });

jdk动态代理的核心API

jdk 的动态代理,要求被代理的对象所属类必须实现一个以上的接口,代理对象的创建使用 Proxy.newProxyInstance 方法,该方法中有三个参数:
1.ClassLoader loader :被代理的对象所属类的类加载器
2.Class<?>[] interfaces :被代理的对象所属类实现的接口
3.InvocationHandler h :代理的具体代码实现

最后一个 InvocationHandler 是一个接口,它的核心方法 invoke 中也有三个参数:
1.Object proxy :代理对象的引用(代理后的)
2.Method method :代理对象执行的方法
3.Object[] args :代理对象执行方法的参数列表

在这里插入图片描述
可以看到Proxy类中有个InvocationHandler的成员变量。要执行实现接口中的相关方法,都会调用InvocationHandler接口方法invoke的方法。

JDK动态代理有两大核心类,它们都在Java的反射包下(java.lang.reflect),分别为InvocationHandler接口和Proxy类。

Proxy

public interface TestInt {

    String ping(String name);

}
public class test implements TestInt {

    @Override
    public String ping(String name) {
        System.out.println("ping");
        return "pong";
    }

}
public class MyInvocationHandler implements InvocationHandler {

    // 目标对象
    private final Object target;

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


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy - " + proxy.getClass());
        System.out.println("method - " + method);
        System.out.println("args - " + Arrays.toString(args));
        return method.invoke(target, args);
    }
}

proxy有两个静态方法:

1.getProxyClass

@Test
public void test1() throws Exception {
    Test test = new Test();
    // 根据类加载器和接口数组获取代理类的Class对象
    Class<?> proxyClass = Proxy.getProxyClass(Test.class.getClassLoader(), Test.class);

    // 通过Class对象的构造器创建一个实例(代理类的实例)
    Test testProxy = (Test) proxyClass.getConstructor(InvocationHandler.class)
        .newInstance(new MyInvocationHandler(test));

    // 调用 ping 方法,并输出返回值
    String value = testProxy.ping("杨过");
    System.out.println(value);

}

2.newProxyInstance

Test testProxy = (Test) Proxy.newProxyInstance(Test.class.getClassLoader(),
                                                Test.class.getInterfaces(),
                                                (proxy, method, args) -> method.invoke(test, args));

代理类如何创建的

getProxyClass 和 newProxyInstance方法

在这里插入图片描述
在这里插入图片描述
两个方法最终都会调用getProxyClass0方法来生成代理类的Class对象。只不过newProxyInstance方法为我们创建好了代理实例,而getProxyClass方法需要我们自己创建代理实例。

getProxyClass0 方法
 /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

从源码和注解可以看出:
1.代理接口的最多不能超过65535个
2.会先从缓存中获取代理类,则没有再通过ProxyClassFactory创建代理类。(代理类会被缓存一段时间。)

WeakCache类

该类主要是为代理类进行缓存的。获取代理类时,会首先从缓存中获取,若没有会调用ProxyClassFactory类进行创建,创建好后会进行缓存。

 @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
                //调用代理工厂类创建代理类
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // put into reverseMap
            reverseMap.put(cacheValue, Boolean.TRUE);

            // try replacing us with CacheValue (this should always succeed)
            if (!valuesMap.replace(subKey, this, cacheValue)) {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }
    }
ProxyClassFactory类

ProxyClassFactory类是Proxy类的一个静态内部类,这个类用于生成代理对象。

 /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                 //验证这个接口是否是重复的
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

核心逻辑:
1.代理类的名称就是在这里定义的,其前缀是$Proxy,后缀是一个数字。
2.调用ProxyGenerator.generateProxyClass来生成指定的代理类。
3.defineClass0方法是一个native方法,负责字节码加载的实现,并返回对应的Class对象。

在这里插入图片描述

Cglib动态代理

引入Cglib

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

使用 Cglib 时有几个前提:
1.被代理的类不能是 final 的( Cglib 动态代理会创建子类,final 类型的 Class 无法继承)
2.被代理的类必须有默认的 / 无参构造方法(底层反射创建对象时拿不到构造方法参数)。

Cglib动态代理的核心API

Enhancer.create(Class type, new MethodInterceptor() {     
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                throws Throwable {
            if ("自定义逻辑判断") {
                return method.invoke(partner, args);
            }
            return null;
        }
    });
}

1.Class type :被代理的对象所属类的类型
2.Callback callback :增强的代码实现

一般情况下我们都是对类中的方法增强,所以在传入 Callback 时通常选择这个接口的子接口 MethodInterceptor
MethodInterceptorintercept 方法中参数列表与 InvocationHandlerinvoke 方法类似,唯独多了一个 MethodProxy它是对参数列表中的 Method 又做了一层封装,利用它可以直接执行被代理对象的方法,就像这样:

// 执行代理对象的方法
method.invoke(proxy, args);

// 执行原始对象(被代理对象)的方法
methodProxy.invokeSuper(proxy, args);

AOP术语

AOP的核心工作:解耦。通过解耦,业务逻辑只需要关注业务逻辑,扩展逻辑只需要关心扩展逻辑,以及切入业务逻辑的位置即可。

AOP面向切面编程,关注的核心是切面(Aspect)。AOP可以在不修改源码的前提下,使用动态代理技术对已有的代码进行逻辑增强。AOP可以实现组件化,可插拔的功能扩展,通过简单的配置即可将功能增强到指定的切入点

1.Target:目标对象
目标对象就是被代理的对象。

2.JoinPoint:连接点
简单的理解为目标对象的所属类中,定义的所有方法
切入点与连接点的关系应该是包含关系:切入点可以是 0 个或多个(甚至全部)连接点的组合。
切入点一定是连接点,连接点不一定是切入点。

3. Pointcut:切入点
那些被拦截 / 被增强的连接点

4.Advice:通知
增强的逻辑,也就是增强的代码。
切入点和通知是要配合在一起使用的,有了切入点之后,需要搭配上增强的逻辑,才能算是给目标对象进行了代理、增强。

5.Proxy:代理对象
代理对象 = 目标对象 + advice通知。

6.Aspect:切面
Aspect 切面 = PointCut 切入点 + Advice 通知。

7. Weaving:织入
它是一个动作。目的就是将Advice增强的逻辑应用到目标对象,生成代理对象的过程。

8.通知的类型
Spring框架中支持的通知类型包含5种,这些通知是基于AspectJ的。
1.Before 前置通知:目标对象的方法调用之前触发。

2.After 后置通知:目标对象的方法调用之后触发。

3.AfterReturning 返回通知:目标对象的方法调用完成,在返回结果值之后触发。

4.AfterThrowing 异常通知:目标对象的方法运行中抛出 / 触发异常后触发。
注意一点,AfterReturning 与 AfterThrowing 两者是互斥的!如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。

5.Around 环绕通知:编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值