AOP

Posted qiaozhuangshi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AOP相关的知识,希望对你有一定的参考价值。

AOP

ExtensionLoader#loadDirectory

加载META-INF/services/,META-INF/dubbo/和META-INF/dubbo/internal/ 下与当前类型全类名相同的文件

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) 
        String fileName = dir + type.getName();
        try 
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
             // 加载当前类全限定名的文件
            if (classLoader != null) 
                urls = classLoader.getResources(fileName);
             else 
                urls = ClassLoader.getSystemResources(fileName);
            
            if (urls != null) 
                while (urls.hasMoreElements()) 
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL);
                
            
         catch (Throwable t) 
            ...
        
    

ExtensionLoader#loadClass

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException 
    if (!type.isAssignableFrom(clazz)) 
        throw ...
    
    //当前接口对应的文件下的被@Adaptive标识的类
    if (clazz.isAnnotationPresent(Adaptive.class)) 
        if (cachedAdaptiveClass == null) 
            cachedAdaptiveClass = clazz;
         else if (!cachedAdaptiveClass.equals(clazz)) 
            throw ...
        
     else if (isWrapperClass(clazz)) 
      //如果当前扫描的类有个构造方法,并且该构造方法的参数与当前类型相同(type),保存到cachedWrapperClasses,这里是实现AOP的一个关键点
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) 
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        
        wrappers.add(clazz);
     else 
        clazz.getConstructor();
         //校验扩展名, 这里可以发现扩展名可以为空 即配置文件不使用key=value,学JDK直接使用value
        // 那么1.该扩展类上有@Extension注解 或者2.该类的名字的末尾包含当前类的名字 
        // 即: 如果当前类叫 Qiao  那么该类的名字为 AbcQiao 那么这个类的扩展名就是abc 详见:注1
        if (name == null || name.length() == 0) 
            name = findAnnotationName(clazz);
            if (name.length() == 0) 
                throw ...
            
        
        String[] names = NAME_SEPARATOR.split(name);
        if (names != null && names.length > 0) 
            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) 
                cachedActivates.put(names[0], activate);
            
            for (String n : names) 
                if (!cachedNames.containsKey(clazz)) 
                    cachedNames.put(clazz, n);
                
                Class<?> c = extensionClasses.get(n);
                if (c == null) 
                    //加载不被@Adaptive标识、不是aop的其他扩展类
                    extensionClasses.put(n, clazz);
                 else if (c != clazz) 
                    throw ...
                
            
        
    

注1: 配置文件中不使用key=value的形式,使用JDK的 value形式

ExtensionLoader.getExtensionLoader(Qiao.class).getExtension("abc")

配置文件 META-INF/services/per.qiao.AbcQiao 如下

per.qiao.AbcQiao

注意,这个类的simpleName以Qiao结尾.

注意到上面,已经将当前类型(@SPI标识)的aop类型放到了cachedWrapperClasses、wrappers中.

在当我们调用getExtension方法时会调用createExtension方法,

createExtension方法如下

private T createExtension(String name) 
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) 
        throw findException(name);
    
    try 
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) 
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        
        // 对获取的扩展类进行IOC处理
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) 
            //这里将aop扩展类进行链式处理,实例化并进行IOC处理
            for (Class<?> wrapperClass : wrapperClasses) 
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            
        
        return instance;
     catch (Throwable t) 
        ...
    

上面方法会返回一个链式条用的第一个入口对象, 比如有两个AOP扩展类, A和B,目标扩展类是Target,

那么调用方法就是 A.method->B.method->Target.method (A,和B的执行顺序取决于你的文件中的配置顺序)。

还有要注意的是AOP类型要和当前类型,放在同一个文件里面(也可以说是目标Target)

了解到那么多之后,我们来自定义一个AOP实现。

AOP的Demo

  1. 自定义一个协议
// 设置默认扩展类型
@SPI(value = MyExtProtocol.NAME)
public interface MyProtocol 

    String getName();

    Integer getSize(String name);
  1. 默认扩展类(目标执行的扩展类)
public class MyExtProtocol implements MyProtocol 
  
    public static final String NAME = "myExt";
  
    @Override
    public String getName() 
        System.out.println("MyExtProtocol.getName" + "目标方法被调用");
        return "MyExtProtocol.getName";
    

    @Override
    public Integer getSize(String name) 
        return null;
    
  1. AOP类型
/**
 * MyProtocol类型的AOP扩展类
 * 与目标扩展类实现同一个接口
 */
public class MyProtocolWrapper implements MyProtocol 

    /**
     * AOP链式调用中的一节,这里我们只有一个AOP扩展类,故这个就是目标扩展类
     */
    private MyProtocol myProtocol;

    public MyProtocolWrapper(MyProtocol myProtocol) 
        this.myProtocol = myProtocol;
    

    @Override
    public String getName() 
        System.out.println("MyProtocolWrapper.getName === " + "前置方法被调用");
        String name = myProtocol.getName();
        System.out.println("MyProtocolWrapper.getName === " + "后置方法被调用");
      
        return name;
    

    @Override
    public Integer getSize(String name) 
        System.out.println("MyProtocolWrapper.getSize === " + "前置方法被调用");
        Integer size = myProtocol.getSize(name);
        System.out.println("MyProtocolWrapper.getSize === " + "后置方法被调用");
        return size;
    
  1. 扩展类配置文件
#  目标扩展类
myExt=per.qiao.myprotocol.impl.MyExtProtocol
# AOP类型
myWrapper=per.qiao.myprotocol.wrapper.MyProtocolWrapper
  1. 调用
MyProtocol myExt = ExtensionLoader.getExtensionLoader(MyProtocol.class).getExtension("myExt");
myExt.getName();

一个简单的AOP就完成了.

以上是关于AOP的主要内容,如果未能解决你的问题,请参考以下文章

阿里四面:你知道Spring AOP创建Proxy的过程吗?

AOP框架Dora.Interception 3.0 [3]: 拦截器设计

小议webpack下的AOP式无侵入注入

Spring AOP

AOP之AspectJ - 代码注入

AOP之AspectJ - 代码注入