栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 方法
 
    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 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 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类的一个静态内部类,这个类用于生成代理对象。

 
    private static final class ProxyClassFactory
        implements BiFunction[], 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, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class intf : interfaces) {
                
                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");
                }
                
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                
                 //验证这个接口是否是重复的
                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;

            
            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 + ".";
            }

            
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

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

Cglib动态代理 引入Cglib

    cglib
    cglib
    3.1

使用 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。
MethodInterceptor 的 intercept 方法中参数列表与 InvocationHandler 的 invoke 方法类似,唯独多了一个 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 环绕通知:编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/354370.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号