AopContext接口
beanMake(Class<?> clz)
使用场景:
在开发插件(或在一些特殊条件下),自动扫描组件没有被扫描到,一般是因为要注册的组件没有在启动类的包下。
// 启动类所在包为 org.xxx
package org.xxx;
import org.noear.solon.Solon;
public class Main {
public static void main(String[] args) {
Solon.start(Main.class,args);
}
}
// 组要注册的 controller 组件却在 com.test 包下,此时TestController 组件是不会被扫描到的
package com.test;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Mapping;
@Controller
public class TestController {
@Mapping("t1")
public String t1(){
return "t1";
}
}
由此可以使用 context.beanMake(TestController.class);
来注册组件,并且会触发@Controller 的注解功能
beanBuilderAdd(Class anno, BeanBuilder builder)
使用场景:给带指定注解的类赋能(自定义注解),比如@MyController,@MyService 注解等
anno
参数为 注解类,
builder
参数为 一个函数式接口,可以传递lambda 表达式或者实现了 BeanBuilder 接口的实现类
BeanBuilder类中的参数列表
void doBuild(Class<?> clz, BeanWrap bw, T anno) throws Throwable;
源码示例:
/** AopContext.java
* ::初始化(独立出 initialize,方便重写)
*/
protected void initialize() {
// ...
//注册 @Controller 构建器
beanBuilderAdd(Controller.class, (clz, bw, anno) -> {
new HandlerLoader(bw).load(Solon.app());
});
//注册 @ServerEndpoint 构建器
beanBuilderAdd(ServerEndpoint.class, (clz, wrap, anno) -> {
if (Listener.class.isAssignableFrom(clz)) {
Listener l = wrap.raw();
Solon.app().router().add(Utils.annoAlias(anno.value(), anno.path()), anno.method(), l);
}
});
//注册 @Inject 注入器
beanInjectorAdd(Inject.class, ((fwT, anno) -> {
beanInject(fwT, anno.value(), anno.required(), anno.autoRefreshed());
}));
}
执行了beanInjectorAdd
方法后,传入的lambda 表达式 或者 实现了BeanBuilder 的类中的内容不会立即执行,它们会全部添加到beanBuilders 的Map集合中
/** BeanContainer.java
* bean 构建器
*/
protected final Map<Class<?>, BeanBuilder<?>> beanBuilders = new HashMap<>();
//
public <T extends Annotation> void beanBuilderAdd(Class<T> anno, BeanBuilder<T> builder) {
beanBuilders.put(anno, builder);
}
那它们执行的时机是什么?通过查看beanBuilders.get()方法,可以看到获取它的时机。
源码分析
// AopContext.java
// 片段1
public void beanScan(Class<?> source) {
//确定文件夹名
if (source.getPackage() != null) {
beanScan(source.getClassLoader(), source.getPackage().getName()); //跟踪这里
}
}
/** AopContext.java
* // 片段2
* ::扫描源下的所有 bean 及对应处理
*/
public void beanScan(ClassLoader classLoader,String basePackage) {
if (Utils.isEmpty(basePackage)) {
return;
}
if (classLoader == null) {
return;
}
String dir = basePackage.replace('.', '/');
//扫描类文件并处理(采用两段式加载,可以部分bean先处理;剩下的为第二段处理)
ScanUtil.scan(classLoader, dir, n -> n.endsWith(".class"))
.stream()
.sorted(Comparator.comparing(s -> s.length()))
.forEach(name -> {
String className = name.substring(0, name.length() - 6);
className = className.replace("/", ".");
Class<?> clz = Utils.loadClass(classLoader, className);
if (clz != null) {
tryCreateBean(clz); //跟踪这里
}
});
}
/**
* // 片段3
* 尝试生成 bean
*/
protected void tryCreateBean(Class<?> clz) { // 跟踪到这
tryCreateBean0(clz, (c, a) -> {
//包装 flag1
BeanWrap bw = this.wrap(clz, null);
c.doBuild(clz, bw, a);
//尝试入库
this.putWrap(clz, bw);
});
}
// 片段4
protected void tryCreateBean0(Class<?> clz, BiConsumerEx<BeanBuilder, Annotation> consumer) {
Annotation[] annS = clz.getDeclaredAnnotations();
if (annS.length > 0) {
//去重处理
if (tryCreateCached.contains(clz)) {
return;
} else {
tryCreateCached.add(clz);
}
//增加条件检测
if(ConditionUtil.test(this, clz) == false){
return;
}
for (Annotation a : annS) {
// 从beanBuilders集合中获取每个注解对应能力,即调用context.beanBuilderAdd(anno, builder)
// 方法时传递的 builder 对象
BeanBuilder builder = beanBuilders.get(a.annotationType());
if (builder != null) {
try {
// 执行accept() 方法后才会真正执行builder对象中的 doBuild 方法
// 即 下一步会执行到[片段3] 中的 flag1 处
consumer.accept(builder, a);
} catch (Throwable e) {
e = Utils.throwableUnwrap(e);
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
}
}
}
}
beanInjectorAdd(Class anno, BeanInjector injector)
使用场景:
自定义注解,让注解有@Inject{aa.xx} (如spring @Value{aa.bb})的能力
anno
参数为 注解类,
injector
参数为 一个函数式接口,可以传递lambda 表达式或者实现了 BeanInjector 接口的实现类
BeanInjector类中的参数列表
void doInject(VarHolder varH, T anno);
使用方式案例
@Override
public void start(AopContext context) {
// 以实现类的方式注册
// 注册CloudConfig 注入器
context.beanInjectorAdd(CloudConfig.class, CloudConfigBeanInjector.instance);
//lambda 方式
//注册 @Inject 注入器
beanInjectorAdd(Inject.class, ((fwT, anno) -> {
beanInject(fwT, anno.value(), anno.required(), anno.autoRefreshed());
}));
}
protected void beanInject(VarHolder varH, String name, boolean required, boolean autoRefreshed) {
try {
beanInjectDo(varH, name, required, autoRefreshed);
} catch (InjectionException e) {
throw e;
} catch (Throwable e) {
throw new InjectionException("Injection failed: " + varH.getFullName(), e);
}
}
CloudConfigBeanInjector.java 源码
public class CloudConfigBeanInjector implements BeanInjector<CloudConfig> {
public static final CloudConfigBeanInjector instance = new CloudConfigBeanInjector();
@Override
public void doInject(VarHolder varH, CloudConfig anno) {
if (CloudClient.config() == null) {
throw new IllegalArgumentException("Missing CloudConfigService component");
}
//支持${xxx}配置
String name = Solon.cfg().getByParse(Utils.annoAlias(anno.value(), anno.name()));
//支持${xxx}配置
String group = Solon.cfg().getByParse(anno.group());
Object tmp1 = build(varH.getType(), group, name);
if (tmp1 != null) {
varH.setValue(tmp1);
}
if (varH.isField() && anno.autoRefreshed()) {
CloudClient.config().attention(group, name, (cfg) -> {
Object tmp2 = build0(varH.getType(), cfg);
if (tmp2 != null) {
varH.setValue(tmp2);
}
});
}
}
public Object build(Class<?> type, String group, String name) {
Config cfg = CloudClient.config().pull(group, name);
return build0(type, cfg);
}
private Object build0(Class<?> type, Config cfg) {
if (cfg == null || cfg.value() == null) {
return null;
}
if (Properties.class.isAssignableFrom(type)) {
return cfg.toProps();
}
if (type.getName().startsWith("java.lang.") || type.isPrimitive()) {
//如果是java基础类型,则为null(后面统一地 isPrimitive 做处理)
//
return ConvertUtil.to(type, cfg.value());
} else {
//尝试转为实体
//
Properties val0 = cfg.toProps();
return PropsConverter.global().convert(val0, null, type, null);
}
}
}
beanExtractorAdd(Class anno, BeanExtractor extractor)
使用场景:
自定义注解,让注解具有解析BeanWrap、method和anno字段信息的功能,此接口注重的功能是提取信息
anno
参数为 注解类,
injector
参数为 一个函数式接口,可以传递lambda 表达式或者实现了 BeanInjector 接口的实现类
BeanExtractor 方法内的参数列表
void doExtract(BeanWrap bw, Method method, T anno)
beanAroundAdd(Class anno, Interceptor interceptor, int index)
使用场景:
自定义注解,让注解具有拦截某些方法的功能,例如缓存、事务控制等
anno
参数为 注解类,
interceptor
参数为 一个函数式接口,可以传递lambda 表达式或者实现了 Interceptor 接口的实现类
index
参数为 执行顺序的索引,
Interceptor 方法内的参数列表
/**
* 拦截
*
* @param inv 调用者
* */
Object doIntercept(Invocation inv) throws Throwable;
InterceptorEntity beanAroundGet(Class anno)
使用场景:
获取给定 具有拦截器属性注解 的拦截器
anno
参数为 拦截器注解类
beanScan(Class<?> source)
beanScan(String basePackage)
beanScan(ClassLoader classLoader,String basePackage)
扫描源下的所有 bean 及对应处理,以上三个功能相同
beanInject(Object obj)
使用场景:
注入Bean对象(即生成对应类型的Bean)
obj
参数为 要生成Bean的对象
beanInject(VarHolder varH, String name)
使用场景:
注入字段属性值,
varH
变量包装器
name
bean name || config $
beanInject(VarHolder varH, String name, boolean required, boolean autoRefreshed)
使用场景:
给指定的变量(varH),根据name的表达式设置值,在类中字段标注了@Inject的字段会调用此方法,如果name为空则是字段属性注入,不为空则是给字段设置
${xxx} 表达式所设置的值。
varH
参数为 变量包装器
name
bean name || config $
required
是否必须
autoRefreshed
是否自动刷新
//注册 @Inject 注入器
beanInjectorAdd(Inject.class, ((fwT, anno) -> {
beanInject(fwT, anno.value(), anno.required(), anno.autoRefreshed());
}));
beanOnloaded(Consumer fun)
beanOnloaded(int index, Consumer fun)
使用场景:
添加bean加载完成事件,在所有的Bean被加载完成后对指定的类做一些操作,例如初始化工作、判断该类型是否添加到了容器中,如果没有可以在此处添加到容器中
fun
参数为 一个消费型接口
index
执行顺序的索引
源码示例:
//添加缓存控制支持
context.beanOnloaded((ctx) -> {
if (ctx.hasWrap(CacheService.class) == false) {
ctx.wrapAndPut(CacheService.class, LocalCacheService.instance);
}
});
// smarthttp 服务器启动时 执行solon应用启动
@Override
public void start(AopContext context) {
if (Solon.app().enableHttp() == false) {
return;
}
context.beanOnloaded((ctx) -> {
try {
start0(Solon.app());
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new IllegalStateException(e);
}
});
}
运行与加载时机
// 添加完的事件全部缓存到了loadEvents中
//加载事件
private final Set<RankEntity<Consumer<AopContext>>> loadEvents = new LinkedHashSet<>();
@Note("添加bean加载完成事件")
public void beanOnloaded(int index, Consumer<AopContext> fun) {
loadEvents.add(new RankEntity<>(fun, index));
// 如果已加载完成,则直接返回
if (loadDone) {
fun.accept(this);
}
}
当服务启动完成后会调用此方法将 loadDone 赋值为 true
/**
* 完成加载时调用,会进行事件通知
*/
public void beanLoaded() {
loadDone = true;
//执行加载事件(不用函数包装,是为了减少代码)
loadEvents.stream()
.sorted(Comparator.comparingInt(m -> m.index))
.forEach(m -> m.target.accept(this));
}
用例
public abstract class Gateway extends HandlerAide implements Handler, Render {
// 省略字段和构造函数
/**
* 注册相关接口与拦截器
*/
@Note("注册相关接口与拦截器")
protected abstract void register();
@Note("添加接口")
public void addBeans(Predicate<BeanWrap> where) {
addBeans(where, false);
}
/**
* remoting 的 bean 建议一个个添加,并同时添加前缀 path
*/
@Note("添加接口")
public void addBeans(Predicate<BeanWrap> where, boolean remoting) {
Solon.context().beanOnloaded((ctx) -> {
ctx.beanForeach(bw -> {
if (where.test(bw)) {
if (remoting) {
add(bw, remoting); //强制为 remoting
} else {
add(bw); //自动判定
}
}
});
});
}
}
@Mapping("/t2/**")
@Component
public class TestGatewat extends Gateway {
@Override
protected void register() {
//添加服务(不带mapping的函数;需要 remoting = true,才会加载出来)
add("user", UserServiceImpl.class, true);
// 重点看这里,此方法会触发抽象类中的 beanOnloaded 方法
addBeans(bw -> "api".equals(bw.tag()));
}
}
解析:
solon框架中自带的Gateway抽象类中有一个register() 方法,此方法是必须要重写的,在此可以调用父类中的addBeans 方法,从而触发beanOnloaded 事件的执行。
beanForeach(BiConsumer<String, BeanWrap> action)
action
是一个消费型接口,支持传递两个参数的BiConsumer接口的实现类和lambda表达式
beanForeach(Consumer action)
action
是一个消费型接口,支持传递一个参数的Consumer接口的实现类和lambda表达式
使用场景:
一般结合 subWrapsOfType(Class<?> baseType, Consumer<BeanWrap> callback)
和 beanOnloaded(Consumer<AopContext> fun)
进行使用。因为这两个方法执行的时机已经是将bean 扫描之后。
遍历bean包装库。
public void subWrapsOfType(Class<?> baseType, Consumer<BeanWrap> callback) {
//获取现有的
beanForeach(bw -> {
if (baseType.isAssignableFrom(bw.clz())) {
callback.accept(bw);
}
});
//获取未来的
wrapExternalSubscribe((bw) -> {
if (baseType.isAssignableFrom(bw.clz())) {
callback.accept(bw);
}
});
}
context.beanOnloaded((ctx) -> {
ctx.beanForeach((k, v) -> {
System.out.println(k);
System.out.println(v);
});
});
List beanFind(Predicate condition)
condition
是一个断言型接口,支持传递一个参数的Predicate接口的实现类,和lambda 表达式
List beanFind(BiPredicate<String, BeanWrap> condition)
condition
是一个断言型接口,支持传递两个参数的Predicate接口的实现类,和lambda 表达式
使用场景:
查找bean包装库。这两个方法也是对执行时机有要求,参考beanForeach方法的执行时机。
示例:
context.beanOnloaded((ctx) -> {
ctx.beanFind(beanWrap -> {
System.out.println("------------------");
System.out.println(beanWrap);
System.out.println("------------------");
return false;
});
});
void beanRegister(BeanWrap bw, String name, boolean typed)
使用场景:
注册BeanWrap 到容器中(按名字和类型存入容器;并进行类型印射)
bw
对象的包装类
name
要注册Bean的name
typed
是否要按类型注册
示例
UserUtil userUtil = new UserUtil(type.getUserType()); // 创建对象
BeanWrap userUtilWrapByName = context.wrap(type, userUtil); // 将对象包装成BeanWarp
context.beanRegister(userServiceWrapByName,"beanName",true); // 注册BeanWrap 到容器中
BeanWrap wrap(Class<?> type, Object bean)
使用场景:
将指定对象按照给定类型包装成BeanWrap
type
对象类型
bean
要包装的对象
context.wrap(Dispatcher.class, dispatcher);
BeanWrap wrap(String name, Object bean)
使用场景:
将指定对象按照给定name包装成BeanWrap
name
beanName
bean
要包装的对象
context.wrap(Dispatcher.class, "beanName");
BeanWrap wrapAndPut(Class<?> type)
使用场景:
包装并推入容器
type
对象类型
context.wrapAndPut(Dispatcher.class);
BeanWrap wrapAndPut(Class<?> type, Object bean)
使用场景:
包装并推入容器
type
对象类型
bean
对象实例
context.wrapAndPut(Dispatcher.class,dispatcher);
void putWrap(String name, BeanWrap wrap)
使用场景:
将BeanWrap按照name 推送到容器,如果wrap为null,拒绝注册
name
beanName
wrap
对象包装实例
void putWrap(Class<?> type, BeanWrap wrap)
使用场景:
将BeanWrap按照type推送到容器,如果type 和 wrap为null,拒绝注册
type
对象类型
wrap
对象包装实例
boolean hasWrap(Object nameOrType)
使用场景:
根据BeanWrap的Name 或 Bean的类型 判断 该"bean"是否有包装类型,本质上就是从Map中根据Key取值,源码如下getWrap
nameOrType
bean类型或者BeanWrap的name(即包装成BeanWrap时所传入的name)
// BeanContainer.java
private final Map<String, BeanWrap> beanWrapsOfName = new HashMap<>();
private final Map<Class<?>, BeanWrap> beanWrapsOfType = new HashMap<>();
public boolean hasWrap(Object nameOrType) {
return getWrap(nameOrType) != null;
}
BeanWrap getWrap(Object nameOrType)
使用场景:
根据BeanWrap的Name 获取包装类型,本质上就是从Map中根据Key取值。
nameOrType
bean类型或者BeanWrap的name(即包装成BeanWrap时所传入的name)
/**
* 获取一个bean包装
*
* @param nameOrType bean name or type
*/
public BeanWrap getWrap(Object nameOrType) {
if (nameOrType instanceof String) {
return beanWrapsOfName.get(nameOrType);
} else {
return beanWrapsOfType.get(nameOrType);
}
}
void getWrapAsync(Object nameOrType, Consumer callback)
使用场景:
异步方式获取BeanWrap,一般在插件开发中用于获取BeanWrap
nameOrType
bean类型或者BeanWrap的name(即包装成BeanWrap时所传入的name)
callback
回调函数,一个消费型接口
@Override
public void start(AopContext context) throws Throwable {
Props cfg = context.cfg();
context.getWrapAsync(Dispatcher.class, bw -> {
// 此处能获取到
System.out.println(bw);
});
// 这里 wrap 为 null
BeanWrap wrap = context.getWrap(Dispatcher.class);
}
// 前提是对象 一定是要注册到容器中的!
void subWrapsOfType(Class<?> baseType, Consumer callback)
使用场景:
订阅某类型的 bean 包装bean
baseType
基类(抽象类或者接口、普通类)一般是接口
callback
消费型接口,回调函数
示例:
@Component
public class TestGatewat extends Gateway {
}
public abstract class Gateway extends HandlerAide implements Handler, Render {
//....
}
public class XPluginImp implements Plugin {
@Override
public void start(AopContext context) throws Throwable {
// 由于 TestGatewat 继承于Gateway,Gateway 又实现了Handler接口,而TestGatewat 又被@Component注解所标注
// 所以以下输出的beanWrap 就是TestGatewat的内容
context.subWrapsOfType(Handler.class, beanWrap -> {
System.out.println(beanWrap);
});
}
}
注意:subBeansOfType
、subWrapsOfType
,仅对 @Bean
或 @Component
产生的 Bean 有效
T getBean(String name)
使用场景:
根据bean名字 获取bean
name
bean名字
T getBean(Class type)
使用场景:
根据bean type获取bean
type
bean类型
List getBeansOfType(Class baseType)
使用场景:
根据baseType(基类)获取bean的集合
baseType
基类
T getBeanOrNew(Class type)
使用场景:
根据type 获取bean,如果不存在则创建
type
类型
void getBeanAsync(String name, Consumer callback)
使用场景:
根据name异步获取bean,使用方式和getWrapAsync
理念基本一致
name
beanName
callback
回调函数
void getBeanAsync(Class type, Consumer callback)
使用场景:
根据type异步获取bean,使用方式和getWrapAsync
理念基本一致
type
类型
callback
回调函数
void subBeansOfType(Class baseType, Consumer callback)
使用场景:
订阅某类型的 bean ,使用方式与 subWrapsOfType
基本一致
baseType
基类(抽象类或者接口、普通类)一般是接口
callback
消费型接口,回调函数
部分接口限制说明
- subBeansOfType、subWrapsOfType,仅对 @Bean 或 @Component 产生的 Bean 有效
- beanExtractorAdd 仅对 @Component 产生的 Bean 有效
- beanAroundAdd 仅对被代理的 Bean 有效,比如:@Controller、@Service、@Dao 等