首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,649 阅读
2
Mac打印机设置黑白打印
5,043 阅读
3
修改elementUI中el-table树形结构图标
4,946 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,705 阅读
5
intelliJ Idea 2022.2.X破解
4,459 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Mac
Spring Cloud
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
MySQL
.Net
Spring Cloud Alibaba
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
629
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
629
篇与
的结果
2021-06-15
elementui textarea与input字体不一致问题解决
今天使用elementui中input及textarea时,发现两种控件默认显示的字体是不一致的,input的比较扁,而textarea显示的比较瘦高。可以看下下面这张图,比较明显。原因通过F12,我们可以看到textarea的字体为monospaceinput的字体为Arial解决在全局css文件加入以下代码即可, 因为我们项目使用的是ruoyi的系统,所以在sec/assets/styles/element-ui.scss文件中加入以下代码即可textarea { //font-family: Arial, Helvetica, sans-serif; //解决TextArea与input不一致的问题 font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; }
2021年06月15日
1,734 阅读
0 评论
0 点赞
2021-06-13
2021年最新版:全球最便宜VPS商家清单,便宜vps推荐
https://bwh81.net,企业级 美国 cn2 gia、日本软银、香港cn2 gia 加拿大公司IT7(2004~)旗下VPS品牌,主打面向中国的高端线路的VPS:10Gbps 美国 cn2 gia、10Gbps 日本软银、1Gbps带宽的香港cn2 gia。天价的成本,全球唯一一家,没有例外!国内访问速度超快,可以支付宝、PayPal付款。https://www.hostwinds.com ,最便宜的“全管理”VPS商家 哪怕是低价的“无管理”VPS,也能享受到别家的“全管理VPS”的服务;售后态度非常友善让人舒服,客服也是技术,有美国西雅图、达拉斯、荷兰三个机房。不想折腾,就稳定弄自己的小项目,hostwinds应该是相当靠谱的。支持支付宝、PayPal付款!https://gcorelabs.com,日本、韩国等30个机房,不限流量 这几年比较出名的卢森堡主机商,业务包括独立服务器、CDN、VPS等;提供全球30多个国家和地区的机器和服务;日本、韩国、新加坡、印度、美国、俄罗斯(包括远东)等,默认200Mbps带宽,不限制流量。 支持支付宝、PayPal、信用卡等付款!访问:https://gcorelabs.comhttps://www.racknerd.com ,低价、高配、大流量,售后超快 主打美国洛杉矶(multacom、sharktech机房)和colocrossing的圣何塞、西雅图、达拉斯、芝加哥、纽约、阿什本、亚特兰大数据中心。所有VPS都是基于KVM虚拟,solusvm面板管理,性价比超高!毫无疑问,2020年低端VPS行业里面,这就是扛把子;可以预见,在2021年,racknerd可能会有更多更大的动作!最新促销:https://www.zhujiceping.com/tag/racknerd/https://www.vultr.com,17个机房,按小时计费,自定义ISO 全球最流行最热门的cloud品牌之一,美国choopa机房的旗下的业务!机器部署在全球17个数据中心,包括日本、韩国、新加坡、韩国、美国、荷兰等;支持:IPV6、按小时付款、快照、区块存储、自定义ISO等多种特性。PayPal、支付宝、微信等付款!【推荐】https://buyvm.net,超级大硬盘,1Gbps带宽,不限流量 早在2010年的时候就已经名声在外,口碑和知名度很高;主要运作KVM虚拟的VPS,拉斯维加斯、纽约、卢森堡三个数据中心,1Gbps带宽,不限流量,免费DirectAdmin面板授权,免费Windows系统!官方提供超便宜的“Blockstorage”,能做到每256G仅需1.25美元/月,最高可以10T硬盘。如何使用,参考:https://www.zhujiceping.com/40168.htmlhttps://virmach.com ,曾今长期低价霸榜 2014年开始运作,主打colocrossing机房的10个数据中心的VPS业务。virmach依托批量低价拿服务器,曾今干过“秒杀”低至$2.5/年的VPS。支持“PayPal”和“支付宝”付款,怕ddos攻击花2美元就可以加voxility提供的1Tbps强悍防御!从2020年开始有示弱或者转变策略的迹象,受到racknerd等众多低价品牌的挑战!https://cloudcone.com,按小时计费,超强ddos高防 印度公司,主打美国洛杉矶Multacom机房的VPS和独立服务器。VPS支持按小时付款,支持换IP、快照、区快存储、负载均衡、autoscaling、超强高防IP(由voxility提供高达1Tbps防御,每个每月2.5美元)。https://friendhosting.net/en/,8个机房,不限流量 2009年成立于保加利亚,每个VPS都是100Mbps带宽,不限流量,支持自定义ISO。可选机房有:捷克、瑞士、保加利亚、立陶宛、波兰、荷兰、乌克兰、美国,支持PayPal、支付宝、比特币等付款https://www.itldc.com,新加坡、乌克兰等10机房,不限流量 好有历史的立陶宛古董公司了,ITL Bulgaria Ltd,Camper Solutions Corp,ITL Internet Service,都是一个老板;主要运作独立服务器和VPS,不限制流量,KVM虚拟;数据中心有:美国(洛杉矶、新泽西、迈阿密)、新加坡、乌克兰、瑞士、保加利亚、立陶宛、荷兰、捷克。支持paypal!https://hosteons.com ,不限流量,有Windows,100G高防 默认100Gbps高防保护!每个VPS给100Mbps带宽,不限流量随便跑,或者1Gbps带宽限制流量!VPS分OpenVZ 7 和KVM(支持Windows),免费送directadmin收费面板,solusvm管理面板非常轻松。“PayPal”和“支付宝”付款。https://digital-vm.com ,日本、新加坡等,10Gbps带宽、不限流量 2019年3月开始运作的,相对来说还算是个新商家,主要提供10Gbps带宽、不限流量的VPS。数据中心分布在:日本、新加坡、美国洛杉矶、英国、荷兰、挪威、丹麦、西班牙。亚洲给大带宽还不限流量的VPS特别少,西班牙、丹麦、挪威等机房的VPS也是比较少见的,PayPal、支付宝等可以付款!https://www.pacificrack.com,一个在作死的VPS品牌 QN机房旗下直属品牌,有足够的资源,主打洛杉矶的VPS业务。可惜的是开卖之初在技术规划、价格定位等方面存在较为严重的失误,一直到PR-V和PR-L系列出现才有明显好转。该品牌口碑有不少负面评价,谨慎考虑!
2021年06月13日
1,244 阅读
0 评论
0 点赞
2021-06-12
uni-app基础之应用生命周期
uni-app 支持如下应用生命周期函数:函数名说明onLaunch当uni-app 初始化完成时触发(全局只触发一次)onShow当 uni-app 启动,或从后台进入前台显示onHide当 uni-app 从前台进入后台onError当 uni-app 报错时触发onUniNViewMessage对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯onUnhandledRejection对未处理的 Promise 拒绝事件监听函数(2.8.1+)onPageNotFound页面不存在监听函数onThemeChange监听系统主题变化注意应用生命周期仅可在App.vue中监听,在其它页面监听无效。<script> export default { /** * */ onLaunch: function() { console.log('应用初始化完成后触发,全局只触发一次') }, /** * */ onShow: function() { console.log('App启动或者从后台重新进入前台时触发') }, /** * */ onHide: function() { console.log('App从前台进入后台时触发') }, /** * */ onError: function() { console.error('App报错时触发') }, /** * 对 nvue 页面发送的数据进行监听 */ onUniNViewMessage: () => { console.log('对 nvue 页面发送的数据进行监听') }, /** * */ onUnhandledRejection: () => { console.log('对未处理的 Promise 拒绝事件监听函数(2.8.1+)') }, /** * 页面不存在监听函数 */ onPageNotFound: () => { console.log('页面不存在时触发') }, /** * 监听系统主题变化 */ onThemeChange: () => { console.log('主题切换后触发') } } </script> <style> /*每个页面公共css */ </style>
2021年06月12日
1,301 阅读
0 评论
0 点赞
2021-06-11
Spring Boot 使用mybatis-plus逻辑删除选装件
试想一下我们的系统,我们所有的表都有五个字段,分别是创建者(create_by)、创建时间(create_time)、修改者(update_by)、修改时间(update_time)、删除标志(del_flag), 同时通过mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间。目前在数据库新增,能自动注入创建人、创建时间,修改时,能自动注入修改人、 修改时间。如果我们调用mybatis-plus提供的删除方法(deleteById),既然是逻辑删除,我们设想的肯定是此时能够自动注入修改人、修改时间,但是,实际测试你会发现,此时sql并没有注入修改人、修改时间。为了解决这个问题,mybatis-plus提供了一个LogicDeleteByIdWithFill的逻辑删除组装件。通过此方法,我们在进行逻辑删除时,便能自动注入修改人、修改时间。继承DefaultSqlInjector/** * Description:官方逻辑删除选装件 * * @author : laughing * DateTime: 2021-06-11 15:21 */ @Component public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass); //2.官方选装件,逻辑删除时,自动填充其他字段(例如逻辑删除时,自动填充删除人是谁,什么时候删除的) methodList.add(new LogicDeleteByIdWithFill()); return methodList; } }配置逻辑删除插件/** * Mybatis-Plus配置 * * @author laughing */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { logger.info("orgCode=======" + SecurityUtils.getOrgCode()); return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || WisdomAgricultureConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(), mybatisPlusInterceptor()); sessionFactory.setGlobalConfig(globalConfig()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } /** * 逻辑删除插件 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setLogicDeleteValue("Y"); dbConfig.setLogicNotDeleteValue("N"); globalConfig.setDbConfig(dbConfig); globalConfig.setSqlInjector(sqlInjector()); return globalConfig; } /** * 自定义Sql注入器 */ @Bean public MySqlInjector sqlInjector() { return new MySqlInjector(); } }扩展BaseMapper/** * Description: * * @author : laughing * DateTime: 2021-06-11 15:23 */ public interface MyBaseMapper<T> extends BaseMapper<T> { /** * 逻辑删除,并且带自动填充 */ int deleteByIdWithFill(T entity); }修改Mapper接口修改Mapper接口,将原来继承BaseMapper改成我们自定义的MyBaseMapper/** * Description:红外测温 * * @author : laughing * DateTime: 2021-05-18 10:28 */ public interface CulturePigTemperatureMapper extends MyBaseMapper<CulturePigTemperature> { /** * 红外测温列表查询 * @param pig 查询条件 * @return 结果 */ List<CulturePigTemperature> selectCulturePigTemperatureList(CulturePig pig); }修改删除方法将原来的删除方法deleteById改成deleteByIdWithFill再次测试,可以看到修改人、修改时间字段能够在逻辑删除方法时自动注入。
2021年06月11日
1,731 阅读
0 评论
0 点赞
2021-06-11
Spring Boot解决fastjson序列化丢失小数点后零的问题
项目上使用数值类型(金额)时,比如价格,我们可能会将数值格式化成两位小数。比如12.34或者12.00。如果项目上使用FastJson进行序列化,你会发现如果我们金额是整数,比如12.00,FastJson序列化之后,返回到前端的是12而不是12.00。查阅FastJson文档,我们发现,在fastjson 1.2.16版本之后,JSONField支持新的定制化配置serializeUsing,可以单独对某一个类的某个属性定制序列化。实现ObjectSerializer接口第一步,我们实现ObjectSerializer,提供我们自己序列化的规则,如下/** * Description:FastJson金额序列化格式化成两位 * * @author : laughing * DateTime: 2021-06-11 11:49 */ public class Keep2Format implements ObjectSerializer { private DecimalFormat df = new DecimalFormat("0.00"); @Override public void write(JSONSerializer jsonSerializer, Object object, Object fieldName, Type type, int i) throws IOException { jsonSerializer.write(df.format(object)); } }@JSONField注解字段第二步,通过@JSONField设置我们自定义的序列化规则。@JSONField(serializeUsing = Keep2Format.class)再次查看数据,可以发现price字段已经格式化成两位小数。{message type="warning" content="注意:此功能需要fastjson 1.2.16及之后的版本"/}
2021年06月11日
1,902 阅读
0 评论
0 点赞
2021-06-09
Spring Boot Session共享
正常情况下,HttpSession是通过Servlet容器创建并进行管理的。创建成功之后都是保存在内存中的。如果开发者需要对项目进行横向扩展搭建集群,那么可以利用一些硬件或者软件工具来做负载均衡,此时,来自同一个用户的HTTP请求就有可能被分发到不同的实例上去,如何保证各个实例之间Session的同步就成为了一个必须要解决的问题。Spring Boot提供了自动化的Session共享配置,它结合Redis可以非常方便的解决这个问题。使用Redis解决Session共享问题的原理非常简单,就是把原本存储在不同服务器上的Session拿出来放到一个独立的服务器上。添加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>配置redis修改配置文件,增加redis配置spring.redis.database=0 spring.redis.host=localhost spring.redis.port=6379创建controller测试@RestController public class RedidSessionController { @Value("${server.port}") public String port; @PostMapping("/save") public String saveName(String name, HttpSession session) { session.setAttribute("name", name); return "hello," + name + ":" + port; } @PostMapping("/get") public String getName(HttpSession session) { session.getAttribute("name"); return "hello," + session.getAttribute("name") + ":" + port; } }打包jar包通过mvn clean package打包成jar包。分别执行java -jar demo-0.0.1-SNAPSHOT.jar --server.port=8080 java -jar demo-0.0.1-SNAPSHOT.jar --server.port=8081通过8080、8081两个端口运行配置nginx负载均衡主要配置内容如下upstream test.com{ server localhost:8080 weight=1; server localhost:8081 weight=1; } server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://test.com; root html; index index.html index.htm; } } 配置文件中首先配置上游服务器,即两个real server,权重都是1,也就是说,请求平均分配到8080及8081端口。测试我们通过postman,调用save方法,保存session信息。然后再次通过postman,调用get方法,可以看到,系统正确的获取到8080端口保存的session信息。再次打开redis,查看session信息可以看到,session信息正确保存到了我们配置的redis数据库中。
2021年06月09日
1,054 阅读
0 评论
1 点赞
2021-06-09
Spring Boot @WebServlet、@WebFilter、@WebListener的使用
三个注解都必须在启动类增加@ServletComponentScan才能够被扫描到。@WebServlet可用于根据不同条件,重定向请求至不同URL。示例代码@WebServlet(name = "/my") @Slf4j public class MyWebServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { log.info("进入get方法"); log.info(req.getParameter("name")); doPost(req,resp); } @Override protected void doHead(HttpServletRequest req, HttpServletResponse resp) { log.info("进入get方法"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { log.info("进入post方法"); log.info(req.getParameter("name")); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) { log.info("进入put方法"); } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) { log.info("进入delete方法"); } @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) { log.info("进入option方法"); } @Override public void destroy() { log.info("servlet>>>destroy"); } @Override public void init() { log.info("servlet>>>init"); } } 使用postman分别通过get、post、delete等进行请求http://localhost:8080/my?name=张三,可以查看输出结果@WebFilter可用于拦截/过滤请求,提前对请求的request进行判断处理,在doFilter后过滤器放行,调用实际请求URL路径。示例代码@WebFilter("/my") @Slf4j public class MyWebFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("filter>>>init"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info("进入doFilter方法"); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { log.info("filter>>>destroy"); } }再次请求,查看输出结果{message type="error" content="注意看输出顺序"/}@WebListener在spring启动之前监听执行初始化操作,可用于提前加载缓存等。@WebListener @Slf4j public class MyWebListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { log.info("requestDestroyed"); } @Override public void requestInitialized(ServletRequestEvent sre) { log.info("requestInitialized"); } }
2021年06月09日
909 阅读
0 评论
0 点赞
2021-06-08
Spring Boot通过ApplicationRunner实现系统启动任务
在Spring Boot通过CommandLineRunner实现系统启动任务中我们介绍了通过CommandLineRunner实现启动任务。但是CommandLineRunner实现的启动任务,我们在传递入口参数时,只能传递基本类型,入法通过键值对的形式传递参数。ApplicationRunner的实现原理跟CommandLineRunner基本类似,我们终点说一下ApplicationRunner的用法。ApplicationRunner实现类中run()方法参数为ApplicationArguments,ApplicationArguments说明如下:通过getNonOptionArgs()获取基本参数,这个与CommandLineRunner的参数是一致的。通过getOptionNames()获取键。通过getOptionValues获取值。演示代码@Component @Order(1) @Slf4j public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { List<String> nonOptionArgs = args.getNonOptionArgs(); Set<String> optionNames = args.getOptionNames(); List<String> optionValues = args.getOptionValues("name"); log.info("MyApplicationRunner>>>nonOptionArgs>>>" + nonOptionArgs.toString()); log.info("MyApplicationRunner>>>optionNames>>>" + optionNames.toString()); log.info("MyApplicationRunner>>>optionValues>>>" + optionValues.toString()); } }修改入口参数--name代表键,=后面是值,注意是--不是-
2021年06月08日
1,058 阅读
0 评论
0 点赞
2021-06-08
Spring Boot通过CommandLineRunner实现系统启动任务
所谓系统启动任务,是在系统启动时执行的一些操作,一般而言只在系统启动之时执行,并且一般只执行一次。比如加载配置文件、数据库初始化操作等。Spring Boot针对系统启动任务,提供了两种解决方案:一是实现CommandLineRunner接口另一种方式是实现ApplicationRunner接口。本文针对CommandLineRunner方式进行说明。Spring Boot项目在启动时,会遍历所有CommandLineRunner实现类并调用其中的run()方法。如果系统中有多个CommandLineRunner实现类,可以通过@Order()注解指定实现类的调用顺序,数值越小越先执行。{mtitle title="演示代码"/}MyCommandLineRunner1@Component @Order(1) @Slf4j public class MyCommandLineRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("Runner1>>>" + Arrays.toString(args)); } }MyCommandLineRunner2@Component @Order(1) @Slf4j public class MyCommandLineRunner2 implements CommandLineRunner { @Override public void run(String... args) { log.info("Runner2>>>" + Arrays.toString(args)); } }idea配置入口参数@Order(1)用来描述CommandLineRunner的执行顺序,值越小越先执行。run()方法的参数是系统启动时传入的参数。
2021年06月08日
1,044 阅读
0 评论
0 点赞
2021-06-08
vue-baidu-map通过路书实现轨迹回放
网上实现轨迹回放的代码很多,但是很少有vue实现的。本文通过vue-baidu-map对vue轨迹回放功能进行说明。安装依赖npm install vue-baidu-map --save修改main.jsmain.js增加以下内容,注意ak需要替换成自己的。import BaiduMap from 'vue-baidu-map' Vue.use(BaiduMap, { ak: '替换成自己的ak' })前端页面<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" :inline="true"> <el-row> <el-col :span="6"> <el-form-item label="车辆" prop="vehicleLicence"> <el-select filterable v-model="queryParams.vehicleLicence" placeholder="请选择车辆" clearable size="small" style="width: 100%" > <el-option v-for="vehicle in vehicleOptions" :key="vehicle.licence" :label="vehicle.licence" :value="vehicle.licence" /> </el-select> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="查询时间" prop="dateRange"> <el-date-picker v-model="dateRange" size="small" style="width: 100%" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" range-separator="-" start-placeholder="请选择开始时间" end-placeholder="请选择结束时间" :picker-options="pickerOptions" ></el-date-picker> </el-form-item> </el-col> <el-col :span="5"> <el-form-item label="轨迹速度"> <el-slider v-model="speed" style="width: 200px" :step="1"></el-slider> </el-form-item> </el-col> <el-col :span="5"> <el-form-item style="float: right"> <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-col> </el-row> </el-form> <baidu-map style="height:47.1rem;width:100%" :center="center" :zoom="15" :scroll-wheel-zoom="true"> <bm-polyline :path="path" stroke-color="blue" :stroke-opacity="0.5" :stroke-weight="3" :editing="false" ></bm-polyline> <bm-marker :position="{lng: startMark.lng, lat: startMark.lat}"></bm-marker> <bm-marker :position="{lng: endMark.lng, lat: endMark.lat}"></bm-marker> <bml-lushu @stop="reset" :path="path" :icon="icon" :play="play" :rotation="true" :speed="speed * 10" :infoWindow="true" :content="content" > </bml-lushu> </baidu-map> </div> </template> <script> //百度地图 import BaiduMap from 'vue-baidu-map/components/map/Map.vue' import BmScale from 'vue-baidu-map/components/controls/Scale' import BmNavigation from 'vue-baidu-map/components/controls/Navigation' import BmMarkerClusterer from 'vue-baidu-map/components/extra/MarkerClusterer' import BmMarker from 'vue-baidu-map/components/overlays/Marker' import BmInfoWindow from 'vue-baidu-map/components/overlays/InfoWindow' import { BmlLushu } from 'vue-baidu-map' import drugMarkerIcon from '@/assets/icons/map_marker_check.png' import { selectGpsOrbitList } from '@/api/dmp/industry/gps/orbit' import { listAllVehicle } from '@/api/basic/vehicle' export default { components: { BaiduMap, BmScale, BmNavigation, BmMarkerClusterer, BmMarker, BmInfoWindow, BmlLushu }, props: {}, data() { return { // 查询参数 queryParams: { vehicleLicence: undefined }, // 日期范围 dateRange: [new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 00:00:00', new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 23:59:59'], play: false, path: [], center: { lng: 116.984646, lat: 36.659686 }, startMark: {}, endMark: {}, icon: { url: drugMarkerIcon, size: { width: 32, height: 32 }, opts: { anchor: { width: 27, height: 13 } } }, content: undefined, speed: 20, pickerOptions: { disabledDate(time) { return time.getTime() > Date.now() - 8.64e6 } }, vehicleOptions: [] } }, methods: { reset() { this.play = false }, handleSearchComplete(res) { this.path = res.getPlan(0).getRoute(0).getPath() }, handleQuery() { const search = this.addDateRange(this.queryParams, this.dateRange) if (this.queryParams.vehicleLicence === undefined) { this.msgError('请选择车辆') return } if (search.beginTime === undefined || search.beginTime === '' || search.endTime === undefined || search.endTime === '') { this.msgError('请选择查询时间') return } selectGpsOrbitList(this.addDateRange(this.queryParams, this.dateRange)).then(response => { if (response.data.length <= 0) { this.msgError('未查询到该车辆的运行数据') return } let length = response.data.length let middle = -1 if (length % 2 === 0) { middle = length / 2 + 1 } else { middle = (length + 1) / 2 } response.data.forEach(item => { let obj = { lng: item.longitude, lat: item.latitude } this.path.push(obj) }) this.center = this.path[middle] this.startMark = this.path[0] this.endMark = this.path[this.path.length - 1] this.content = this.queryParams.vehicleLicence this.play = true }).catch(error => { }) }, /** 重置按钮操作 */ resetQuery() { this.resetForm('queryForm') this.dateRange = [new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 00:00:00', new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate() + ' 23:59:59'] } }, mounted() { listAllVehicle().then(response => { this.vehicleOptions = response.data }) } } </script> <style> </style> data关键参数说明:play:true或false,为true时,开始播放轨迹。path:数组,格式为[{lng: 116.984646,lat: 36.659686}]center:地图中心点,格式{lng: 116.984646,lat: 36.659686}startMark:轨迹开始经纬度,格式{lng: 116.984646,lat: 36.659686}endMark:轨迹终点经纬度,格式{lng: 116.984646,lat: 36.659686}icon:轨迹图标,如下演示的小车图标content:轨迹显示内容,如下按时的车牌speed:轨迹绘制的速度,这里通过el-slider实现滑动调整速度bm-polyline组件属性说明path:绘制曲线的经纬度数组bml-lushuplay:true或false,为true时,开始播放轨迹。infoWindow:true或false,为true时,显示窗口,及content内容。
2021年06月08日
3,138 阅读
4 评论
2 点赞
2021-06-07
Spring Boot注册拦截器Interceptor
Spring MVC提供了AOP风格的拦截器,拥有更加精细的拦截器处理能力。Spring Boot中拦截器的注册更加方便,步骤如下:spring-boot-starter-web创建拦截器,实现HandlerInterceptor配置拦截器,定义配置类进行拦截器的配置增加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>实现HandlerInterceptor代码如下:public class MyInterceptor implements HandlerInterceptor { private final Logger logger = LoggerFactory.getLogger(MyInterceptor.class); /** * 方法执行前运行 * 必须返回true,后面的方法才能执行 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("我先执行"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("我在controller方法之后执行"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("我最后执行"); } }配置拦截器@Configuration public class MyWebMvcConfig implements WebMvcConfigurer { /** * 拦截器 * * @param registry registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(1); } }测试定义controller测试我们的拦截器@RestController @RequestMapping("interceptor") public class InterceptorController { private final Logger logger = LoggerFactory.getLogger(InterceptorController.class); @GetMapping("/test") public String testInterceptor(){ logger.info("我是controller方法"); return "success"; } }通过调用http://localhost:8080/interceptor/test查看输出日志。温馨提示拦截器按照preHandle→Controller→postHandle→afterHandle的顺序执行。只有preHandle方法返回true时,后面的方法才会执行。当拦截器链内存在多个拦截器时,postHandle在拦截器链内所有拦截器返回成功时才会调用。当拦截器链内存在多个拦截器时,afterHandle在拦截器链内所有拦截器返回true时才会调用。当拦截器链内存在多个拦截器时,如果第一个拦截器的preHandle方法返回false,则后面的方法都不会执行。调用controller方法时,只要配置了拦截的路由,哪怕前端请求404,仍然会调用preHandle、postHandle及afterHandle的方法。如果我们设置了order,代码如下:/** * 拦截器 * * @param registry registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor2()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(200); registry.addInterceptor(new MyInterceptor1()) //配置拦截的路由 .addPathPatterns("/interceptor/**") //配置不拦截的路由 .excludePathPatterns("/demo/**") //配置顺序 .order(300); }
2021年06月07日
1,180 阅读
1 评论
1 点赞
2021-06-07
springboot 通过 DefaultErrorAttributes自定义错误信息
自定义error数据就是对返回的数据进行自定义。Spring Boot返回的Error信息一共有5条,分别是timestamp、status、error、path。在BasicErrorController的errorHtml()方法和error()方法,都是通过getErrorAttributes()方法获取Error信息的,该方法最终会调用DefaultErrorAttributes类的getErrorAttributes()方法,而DefaultErrorAttributes类是在ErrorMvcAutoConfiguration中默认提供的。当系统没有提供 errorAttributes 时才会采 DefaultErrorAttributes,因此自定义错误提示时,只需要自己提供一个ErrorAttributes即可,而DefaultErrorAttributes是ErrorAttributes的子类,因此只需要继承 DefaultErrorAttributes 即可。@Component public class MyErrorAttribute extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { Map<String, Object> map = super.getErrorAttributes(webRequest, options); map.put("errorMsg", "出错了"); return map; } }
2021年06月07日
2,117 阅读
0 评论
1 点赞
1
...
24
25
26
...
53