技术标签: spring spring boot 问题处理 java application javabean
微服务拆分,原有的核心业务抽出公共的核心依赖工程,在现有启动类的基础上,封装了很多核心默认的注解配置,避免应用单独使用注解(如Fegin\ComponentScan\ServletComponentScan等)导致核心服务不可用,需要在SpringBoot Application启动时,部分BeanDefinitionRegistrar执行之前把核心的配置信息添加到注解中,做到动态修改注解的效果。
解决在Spring上下文初始化加载之前进行注入,可以采用Springboot新增的类SpringApplicationRunListener,执行过程如下
SpringApplicationRunListener方法:
started(SpringBoot扫描spring.factorids后立刻执行;
SpringApplication run代码如下
。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
拿到了SpringBootApplication Context上下文之前的执行方法,直接撸代码,可能需要修改多个注解值,需要在逻辑上解耦,采用模板方法模式,时序图如下
实现代码:
1、DemoApplicationRunListener.class
public class CombApplicationRunListener implements SpringApplicationRunListener {
private SpringApplication application;
private String[] args;
public CombApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting() {
IApplicationAnnotationFilter.ANNOTATION_FILTERS.forEach(filter -> {
filter.run(application, args);
});
}
2、IApplicationAnnotationFilter.class
public interface IApplicationAnnotationFilter {
/**
* 通过package url扫描查找耗时3秒左右,故使用手工注册方式进行填充,后续考虑优化
**/
List<IApplicationAnnotationFilter> ANNOTATION_FILTERS = new ArrayList<IApplicationAnnotationFilter>() {
{
add(new MapperScanAnnotationFilter());
add(new ServletScanAnnotationFilter());
}
};
boolean run(SpringApplication application, String[] args);
}
3、AbstractAnnotationExecutor.class
public abstract class AbstractAnnotationExecutor implements IApplicationAnnotationFilter {
private Logger logger = LoggerFactory.getLogger(AbstractAnnotationExecutor.class);
@Override
public boolean run(SpringApplication application, String[] args) {
if (!this.getClass().isAnnotationPresent(Registar.class)) {
throw new FbRuntimeException("默认核心类未添加Register注解,无法识别需要修改的注解信息!");
}
Registar registar = this.getClass().getAnnotation(Registar.class);
if (!application.getMainApplicationClass().isAnnotationPresent(registar.value())) {
return false;
}
Annotation annotation = application.getMainApplicationClass().getAnnotation(registar.value());
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
try {
Field field = handler.getClass().getDeclaredField("memberValues");
field.setAccessible(true);
Map memberValues = (Map) field.get(handler);
boolean result = editAnnotationValue(memberValues);
if (registar.defaultPackages() != null && !"".equals(registar.defaultPackages())) {
result = fillDefaultPackages(memberValues, registar.defaultPackages(), registar.value().getSimpleName());
}
return result;
} catch (Exception e) {
logger.error(String.format("获取注解%s属性异常", annotation.getClass().getName()), e);
}
return false;
}
protected boolean fillDefaultPackages(Map memberValues, String corePackageName, String className) {
Object basePackagesObj = memberValues.get("basePackages");
if (basePackagesObj != null) {
//此处不要使用直接获取的Arrays.asList进行添加操作,因为Arrays默认的返回list属于内部类,内部类不支持add和romve
List<String> basePackages = new ArrayList(Arrays.asList((String[]) basePackagesObj));
if (!basePackages.contains(corePackageName)) {
basePackages.add(corePackageName);
//不能直接将list直接put到memberValues中,list默认都是Object类型,会导致取出无法强转String
String[] defaultPackages = new String[basePackages.size()];
basePackages.toArray(defaultPackages);
memberValues.put("basePackages", defaultPackages);
}
logger.info(String.format("[系统已经默认在%s.class增加默认核心包扫描->%s]", className, basePackages));
}
return true;
}
/**
* @param memberValues 入参,修改属性值的map容器
* @return 修改是否成功.
*/
protected abstract boolean editAnnotationValue(Map memberValues);
}
4、 提供两种使用模式
1)通过单独注解默认修改basePackages
@Registar(value = ServletComponentScan.class, defaultPackages = "com.god.demo.framework.common.filter")
public class ServletScanAnnotationFilter extends AbstractAnnotationExecutor {
@Override
protected boolean editAnnotationValue(Map memberValues) {
//如果只是修改basePackages(要同名),使用了默认实现过程
return true;
}
}
@Registar(MapperScan.class)
public class MapperScanAnnotationFilter extends AbstractAnnotationExecutor {
@Override
protected boolean editAnnotationValue(Map memberValues) {
fillDefaultPackages(memberValues, "com.god.demo.framework.common.mapper", "MapperScan");
Object sqlSessionFactoryRefObj = memberValues.get("sqlSessionFactoryRef");
if (sqlSessionFactoryRefObj == null || "".equals(sqlSessionFactoryRefObj)) {
memberValues.put("sqlSessionFactoryRef", SQL_FACTORY);
log.info("[系统已经默认在MapperScan.class增加默认sqlSessionFactory]" + SQL_FACTORY);
}
return true;
}
}
至此,启动公共注解修改完成
export default { // eslint-disable-next-line vue/multi-word-component-names name: "Home",}因为起名时没有使用大驼峰和横线拼接单词,所以报错,在idea中,我发现可以在name:“home”,这一句上面添加 // eslint-disable-next-line vue/multi-word-component-names这一句话,双斜杠也必须加上,不要隔行,紧挨着,向上面代码写的那样,这样可以不报错。._"component name \"about\" should always be multi-word vue/multi-word-component-"
406. 根据身高重建队列假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。 编写一个算法来重建这个队列。注意:总人数少于1100人。示例输入:[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]输出:[[5,0], [7,0], [5,2], [6,1], [..._根据身高重建队列java
在安装好node.js 和npm包管理工具后。使用npm config ls查看,安装完成后的路径。AppData 是隐藏文件夹, 在控制面板,设置显示隐藏文件,才看得到。为了保留C盘的剩余空间,有必要,把 npm的 global 路径修改到其他盘符下。npm config set cache "D:\Program Files\npm-c..._npm下载安装包时指定缓存目录去下载
此Demo只是一个极其简单的LINQ查询Demo一个类using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApp1{ public class NBA_Star ..._linq语言,连接asp。
话不多说, 先根据代码结果看block到底在内存的哪个分区:一:MRC下, 声明的block实现部分,没有引入外界的任何局部变量int main(int argc, const char * argv[]) { @autoreleasepool { //定义bolck void(^myBlock)() = ^{
819. 最常见的单词给定一个段落 (paragraph) 和一个禁用单词列表 (banned)。返回出现次数最多,同时不在禁用列表中的单词。题目保证至少有一个词不在禁用列表中,而且答案唯一。禁用列表中的单词用小写字母表示,不含标点符号。段落中的单词不区分大小写。答案都是小写字母。示例:输入:paragraph = “Bob hit a ball, the hit BALL flew far after it was hit.”banned = [“hit”]输出: “ball”解释:“_code着真正单词
共模信号与差模信号辨析差模又称串模,指的是两根线之间的信号差值;而共模噪声又称对地噪声,指的是两根线分别对地的噪声。对于一对信号线A、B,差模干扰相当于在A与B之间加上一个干扰电压,共模干扰相当于分别在A与地、B与地之间加上一个干扰电压;像平常看到的用双绞线传输差分信号就是为了消除共模噪声,原理很简单,两线拧在一起,受到的共模干扰电压很接近, Ua - Ub依然没什么变化,当然这是_ccmr是cop compare
0. 为什么使用Array 相对于Matrix提供的线性代数运算,Array类提供了更为一般的数组功能。Array类为元素级的操作提供了有效途径,比如点加(每个元素加值)或两个数据相应元素的点乘。1. Array Array是个类模板(类似于Matrx),前三个参数是必须指定的,后三个是可选的,这点和Matrix是相同的。 Array<typename Sc...
#一、通过判断当前的输入模式禁止表情输入- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ if (textField == self.searchText) { if (string.length == 0) return YES; //不支持系统表情的输入 _ios textfield对食物emoji输入做限制
1.实时性:保证消息实时触达是互动场景的必备能力。对于一个实时消息系统,“实时”二字很好地表达了这个系统的基本要求。通过微信和你的好友聊天,结果等半天对方才收到,基本上也没有意愿聊了;直播场景下,如果主播的互动消息房间里的粉丝要等很长时间才能收到,也很难让粉丝们有积极参与的欲望。实时性分为:短轮询,长轮询,WebSocket(长链接)。2.可靠性:“不丢消息”和“消息不重复”是系统值得信赖..._im能力
首先需要去qq群官方,然后点记加群组件,然后选择群,复制对应的代码即可登录到QQ群官网点击加群组件选择群,选择网页还是移动设备 复制代码示例:<html><head> <title>加群组件</title></head><body> <div> <a target="_blank" href="tencent://message/?uin=你的QQ号码">添加好友</a&g_vue移动端怎么写跳转qq加群
有些学做网站学员使用服务器建网站,并且安装了宝塔面板,建设好之后需要登录后台,但是有时会忘记账号和密码,应该怎么办?或者是在使用的过程中忘记了登陆密码,应该怎么解决呢?下面介绍一下宝塔面板管理员密码忘记解决方法。方法/步骤1,忘记密码之后,在阿里云服务器中找到对应的实例,进入‘远程连接’。2. 选择“Workbench远程连接”;3、填写操作系统用户名对应的密码,如果这个密码忘记,可以在“密码/密..._宝塔mysql密码