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
- 自定义一个协议
// 设置默认扩展类型
@SPI(value = MyExtProtocol.NAME)
public interface MyProtocol {
String getName();
Integer getSize(String name);
}
- 默认扩展类(目标执行的扩展类)
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;
}
}
- 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;
}
}
- 扩展类配置文件
# 目标扩展类
myExt=per.qiao.myprotocol.impl.MyExtProtocol
# AOP类型
myWrapper=per.qiao.myprotocol.wrapper.MyProtocolWrapper
- 调用
MyProtocol myExt = ExtensionLoader.getExtensionLoader(MyProtocol.class).getExtension("myExt");
myExt.getName();
一个简单的AOP就完成了.