开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Java反射机制的作用是什么?

发表于 2021-11-18

一、反射的概念:

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

二、反射机制的作用:

1,反编译:.class–>.java

2,通过反射机制访问java对象的属性,方法,构造方法等;

这样好像更容易理解一些,下边我们具体看怎么实现这些功能。

三、sun为我们提供了那些反射机制中的类:

java.lang.Class;

java.lang.reflect.Constructor; java.lang.reflect.Field;

java.lang.reflect.Method;

java.lang.reflect.Modifier;

很多反射中的方法,属性等操作我们可以从这四个类中查询。还是哪句话要学着不断的查询API,那才是我们最好的老师。

四、具体功能实现:

1,反射机制获取类有三种方法,我们来获取Employee类型

image.png

2,创建对象:获取类以后我们来创建它的对象,利用newInstance:

image.png

3,获取属性:分为所有的属性和指定的属性:

1),先看获取所有的属性的写法:

image.png

2),获取特定的属性,对比着传统的方法来学习:

image.png

4,获取方法,和构造方法,不再详细描述,只来看一下关键字:

image.png

这样我们就可以获得类的各种内容,进行了反编译。对于Java这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。

综上为,Java反射的再次学习,灵活的运用它,能够使我们的代码更加灵活,但是它也有它的缺点,就是运用它会使我们的软件的性能降低,复杂度增加,所以还要我们慎重的使用它。

五、Java学习视频:

image.png

【Java300集】全新的Java300集来啦!java零基础小白自学Java必备优质教程

【Java入门】Java零基础入门全套教程_适合0基础入门_JavaSE_Java基础教程

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Java IO 之缓存流

发表于 2021-11-18

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战。

上回说到,Java 常见流对象——文件字节流(缓存),尝试了两种用 byte[] 数组来进行缓存提高读写效率的方法。今天来说说如何用字节缓存流来进行图片的复制:

核心代码讲解

流程都是一样的:
IO流实例化——读——写——刷新——关闭流。

  • 先实例化一个空的文件对象,然后再把这个 new 好的作为整体,传入到 BufferedInputStream 和 BufferedOutputStream 中去;
    image.png
  • 在 while 的判断对象改为缓存流对象的 read() 方法;
  • while 循环内,只要没读到最后,就不停地调 .write();
  • 循环结束后,一次性地刷新一下 .flush();
  • 依次关闭流。

关闭细节

  • 关于处理流与节点流
    缓存流是一种处理流,处理流就是在节点流外面包裹的(装饰的)一层流。所以关闭的时候要先关处理流,再关一开始的 fis 流。如果写的时候,把 fis 和 fos 对应的new FileInputSteam和 new FileOutputStream 都放在了 bis 和 bos 里面,关闭的时候只关处理流,节点流也会被关闭:
    以 bos 为例,在 .close() 上进行源码的追踪:
    image.png
    image.png
    通过这里的 out 可以跳转到 OutputSteam 这里:
    image.png
    说明确实对输入流这个节点流也进行了关闭。
  • 关于输入输出流:
    如果从输入输出的角度来看,先有输出,后有输入,因此要先关闭输出流,再关闭输入流。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Java复制代码static void bufferedFS() {
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream("/Users/fang/Images/题目.png");
bis = new BufferedInputStream(fis);
fos = new FileOutputStream("/Users/fang/Images/题目 output4.png");
bos = new BufferedOutputStream(fos);
int temp = 0;
while((temp = bis.read())!=-1) {
bos.write(temp);
}
bos.flush();
} catch (Exception e) {
e.printStackTrace();

} finally {

try { // 后开先关
bos.close();
bis.close();
fos.close();
fis.close();

// if (bis!=null) {
// bis.close();
// }
// if (fis!=null) {
// fis.close();
// }
// if (bos!=null) {
// bos.close();
// }
// if (fos!=null) {
// fos.close();
// }

} catch (IOException e) {
e.printStackTrace();
}

}
}

运行结果

image.png

可以发现,和前面两种用法的效率差别不大。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Python实现过滤文本中表情符号

发表于 2021-11-18

背景

在项目中需要对文本进行过滤,文本中主要的表情主要是微信的表情,其形式主要是“[表情名称]”,由于都是字符串,所以我打算使用正则的方式进行匹配后替换。

步骤

搜索微信表情库

要替换表情,需要有微信表情库,所以第一步先找微信的表情都有哪些,经过搜索,发现了这个页面 微信(Wechat)表情符号列表 有微信表情的列表,但是其是页面,不可以直接复制粘贴,因此就得想办法将其表情名称给拿下来。

获取微信名列表

对于页面的内容,我们可以使用JavaScript来获取dom节点的值,打开控制台,查看其节点,根据其特点,简单写了一段打印表情名称的js代码。

1
2
3
4
5
6
7
8
9
10
JavaScript复制代码var doms = document.getElementsByClassName('emoji_card_list');
for(var i=0;i<doms.length;i++){
var tds = doms[i].getElementsByTagName('td');
for(var j=0;j<tds.length;j++){
var text = tds[j].innerText;
if(text.indexOf('[') === 0 || text.indexOf('/')===0) {
console.log(text);
}
}
}

复制到控制台执行后,复制后得到这样格式的文本(只是部分内容):

1
2
3
4
5
6
7
8
9
10
11
text复制代码[让我看看] debugger eval code:7:21
[666] debugger eval code:7:21
[翻白眼] debugger eval code:7:21
/微笑 debugger eval code:7:21
/撇嘴 debugger eval code:7:21
/色 debugger eval code:7:21
/害羞 debugger eval code:7:21
/闭嘴 debugger eval code:7:21
/睡 debugger eval code:7:21
/旺柴 debugger eval code:7:21
/瓢虫

对于这样形式的内容,我们可以在文本编辑器中替换到后面内容,当然更可以使用Python来操作。

处理微信表情名称

在Python控制台中,我们可以很轻易的处理,通过下面的代码,可以获得最终的表情名称合集。

1
2
3
4
5
6
7
8
python复制代码data = """上面复制的大段内容,复制后回车"""
data_list = [x.split(' ')[0] for x in data.split('\n')]
emoji_list = []
for x in all_emoj:
if x[0] == '/':
emoji_list.append('[%s]' % x[1:])
else:
emoji_list.append(x)

正则匹配表情

有了表情内容,我们就使用正则对文本匹配了,匹配的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
python复制代码def remove_emoji(text):
"""
去除文本中的表情符号,表情符号都是"[表情名称]" 这样的形式
return: str
"""
if '[' not in text:
return text
reg_expression = '|'.join([x.replace('[', '\[').replace(']', '\]') for x in emoji_list])
pattern = re.compile(reg_expression)
matched_words = pattern.findall(text)
# 使用set去重,可以防止查找到多个的时候进行多次循环
for matched_word in set(matched_words):
text = text.replace(matched_word, '')
return text

测试结果如下,符合要求:

1
2
css复制代码>>> remove_emoji("[哈哈][闭嘴][闭嘴]")
'[哈哈]'

写在最后

因为我们项目中使用的是非标准Unicode表情,如果您的项目中使用的是标准的Unicode表情,可以使用Python的 emoji 包,里面有标准的表情列表,具体的可以参考网上的一篇文章使用python环境过滤文本当中的emoji表情。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

批量修改mysql数据库中的已有字段(日期格式) 批量修改数

发表于 2021-11-18

批量修改数据库中的日期格式

1.需求

数据库建好后发现日期格式出现混乱,库中的字段类型为字符串,并且已经保存了大量数据,需要批量的修改。

2.解决

  • 由于对数据库没啥研究,经过一番搜索,终于找到解决办法,只需要在数据库中运行一行sql语句即可轻松搞定。
  • 原始日期格式:7/23/20 10:00(这鬼格式导致后面用起来太麻烦,所以决定直接改掉数据库),目标格式:2020-07-23 10:00:00,使用的sql语句:UPDATE YOURTABLE SET time1=STR_TO_DATE(time,'%c/%d/%y %H:%i:%s'),当然可以直接更新time,我这里为了先测试下效果,新加了个time1字段,如果只想修改一部分数据的话再加个WHERE条件就好啦(^▽^)。
  • 使用的STR_TO_DATE和%c/%d/%y %H:%i:%s都是mysql基础方法啦,具体可以看下方资料链接。

3.参考资料

dev.mysql.com/doc/refman/…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring-以注解的方式玩IOC

发表于 2021-11-18

由XML转向注解开发!

1
复制代码  我们之前是使用XML的方式来让Spring帮我们创建对象的,这样看起来非常麻烦,那么我们有没有什么简单的方式呢,答案是有的。

接下来我来告诉大家注解是怎么一步一步的取代xml配置的

第一步:在xml文件中告诉Spring要扫描的包,这样注解才会生效

1
ini复制代码<context:component-scan base-package="com.albb"/>

第二步:Bean标签创建对象通过以下注解取代

1
2
3
4
5
6
7
8
9
10
less复制代码@Component 用在类上,相当于是Bean标签,id就是该类的类名首字母小写
@Controller 用在web层上,相当于是Bean标签
@Service 用在Service层上,相当于是Bean标签
@Repository 用在dao层,配置一个Bean

@Contorller,@Service,@Repository都相当于是@Component衍生出来的注解

@Scope:配置Bean的作用范围,相当于bean的scope属性,用在类上
@PostConstruct是指bean的初始化方法,用在初始化方法头上
@PreDestory是指定bean的销毁方法,用在销毁方法上

第三步:依赖注入的注解

1
2
3
4
5
6
7
8
less复制代码@AutoWired 相当于<property name="" ref="" ></property>
@Qualifer(要注入的bean的名称) 结合AutoWired使用,可以设置它的属性id,然后通过id来注入依赖
@Resource(要注入的bean的名称):相当于@autoWired+@Qualifer
@Value(普通值)
@Value("${properties里的key}")
相当于property标签的value,注入普通属性<Property name="" value=""> </Property>

使用注解注入时,不需要使用set方法

纯注解开发

1
2
3
4
5
kotlin复制代码1.创建一个配置类,打上@Configuration注解,
2.@CompoentScan(包名),用在配置类上,表示开启注解扫描
3.@ProperSource(配置文件),用在配置类上,加载properties文件,使用value属性指定properties的指定路径
4.@Import 用在配置类上,引入子配置类,用value属性指定子配置类的class
5.@Bean 用在配置类的方法上,把返回值声明为一个Bean,用name/value属性指定bean的id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
java复制代码  /*
3.使用@Bean注解标记的方法,方法内部需要用到spring工厂(容器)里面的某一个对象,怎么办?
3.1 此时可以在方法的参数上,写上想要的对象参数即可
a. 其实它就是在方法参数的前面,隐藏着一个注解: @Autowired
3.2 但是也要考虑出现极端的情况:如果在spring的容器里面,有多个对象满足这个参数的类型,咋办?
a. 搭配使用@Qualifier ,指定id值。
b. 投机取巧,把方法的参数名字,写成id的名字即可

*/
@Bean
public QueryRunner runner03(@Qualifier("dataSource") DataSource ds){
System.out.println("ds===" + ds);
return new QueryRunner(ds);
}

@Bean
public QueryRunner runner04(DataSource dataSource2){
System.out.println("dataSource2===" + dataSource2);
return new QueryRunner(dataSource2);
}

@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driverClass);
ds.setJdbcUrl(jdbcUrl);
ds.setUser(user);
ds.setPassword(password);
return ds;
}
@Bean
public DataSource dataSource2() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driverClass);
ds.setJdbcUrl(jdbcUrl);
ds.setUser(user);
ds.setPassword(password);
return ds;
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

JVM垃圾回收算法总结

发表于 2021-11-18

这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

一.GC算法

1.对象存活判断算法

引用计数算法和可达性分析算法

2. 判断对象是否存活

①.第一次标记:可达性分析算法中不可达的对象,被标记和筛选,筛选条件是此对象有必要执行finalize()方法,当未重写finalize()或方法已被执行过,则没必要执行

②.第二次标记:如果有必要执行finalize,此对象会被放在F-Queue中,由Finalizer线程执行。GC将在queue中第二次标记只要重新与引用链上的任一对象建立关联即可,例如把自己(this)赋值给某个类变量或者对成员变量,建立起引用即可。

③.需要注意的是,任一对象的finalize()只能被系统调用一次

3.垃圾收集算法

标记清除算法

标记所有需要回收的对象,完成后统一回收。效率低,会产生大量不连续的内存碎片

复制算法

内存一分为二,只用其中一块,此块用完后,将存活对象复制到另一块上,再将此块清理

标记-整理算法

标记后让存活的对象都向一端移动,清除端边界外的部分内存

分代收集算法

划分新生代和老年代。新生代每次都会都大量对象死去,就用复制算法;老年代对象存活率高,没有额外空间作为分配担保,就用标记算法

二.GC类型和参数

GC类型

JVM的gc类型主要有三种,新生代gc、老年代gc,和堆gc。

  1. Minor GC:新生代(Young)GC,无法为一个新的对象分配空间时会触发 ,所以此GC回收速度较快
  2. Major GC:老年代(Old)GC,经常会伴随至少一次的Minor GC,一般会比Minor慢10倍
  3. Full GC:堆空间GC,年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代永久代满时也会引发Full GC,会导致Class、Method元信息的卸载

Full GC触发条件:当system.gc()被显示调用,或者老年代的堆内存满时、

新生代的GC过程

在程序的运行过程中,Minor GC次数是最多的,通过jstat命令可以查看到。Young GC的过程如下;

  1. 当Eden满的时候第一次Minor GC,存活对象移动到S0,Eden清空
  2. Eden再满,触发GC,Eden和S0中的对象通过复制送入S1,S0和Eden清空
  3. 触发GC时,S1和S0互换角色,一对象被复制16次,将被送入老年代
  4. 两个survivor避免内存的碎片化,减少了老年代的GC、减少触发Full GC

gc启动参数

在启动时,我们需要添加jvm的启动参数,这样才能打印出我们想要看到的gc信息,常用于JVM问题的排查和优化。

  1. -verbose:gc:查看垃圾收集的过程
  2. -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log:指定gc的目录和文件名
  3. -XX:+PrintGCTimeStamps -XX:+PrintGCDetails:设置GC日志格式
  4. -XX:+PrintCommandLineFlags:查看虚拟机默认参数

新老代比例

通常,新生代、老年代和survivor区域的比例不需要进行设置,使用默认值即可。

默认值如下:

  1. –XX:NewRatio=2:新生代和老年代比例为1 : 2
  2. –XX:SurvivorRatio=8:意味着Eden:s1:s2 = 8:1:1

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

长春JAVA后端薪资水平大分析

发表于 2021-11-18

本文的时间节点是:2021.11.18。

以后每个季度都会推送一篇类似的数据分析,作为时代的记录者,感受时代的变迁。

背景介绍

上次分享了一次长春前端的待遇环境,有幸获得了掘金的推荐

image.png

实在是可喜可贺可喜可贺

那这次趁热打铁,我们来看看编程界的长青树,“Java后端”在长春的待遇怎么样。

之所以特意说“Java后端”,而不是全体“后端”,那是因为在长春,Java在后端的牌面那是老大级别。

任你外面PHP,GO,NodeJS风怎么刮,我Java就是大旗就是不倒。为了给老大牌面,给Java单开一篇,其他语言咱们以后再表。

image.png

数据分析

数据来源和采集方法

这次的数据来源依旧是“BOSS直聘”,每次5-10秒爬取一个页面,对服务器损耗并不大,望BOSS直聘理解。

所有的工作都是通过JavaScript进行的,以下是技术清单:

1
2
3
4
5
6
markdown复制代码* 数据是通过headless浏览器来采集(BOSS直聘网站做了预处理,不能直接通过get请求获取页面)
* 数据处理:大部分直接使用了js原生功能,使用了少量Lodash的分类功能
* 可视化视图 Echarts
* 词云使用 https://wordart.com/create
* 无头浏览器操作框架是puppeteer
* 页面解析cheerio

所有的代码和数据都已开源,项目地址在。

注:希望大家在使用代码的时候尽量考虑服务器并发,不要给官方造成困扰。

https://gitee.com/CrazyPeter/grab-boss

image.png

薪资待遇

数据总览

image.png

image.png

将近年末,现在总的岗位需求量并不大,不到300个的岗位展示,“Java”以一己之力,直追“前端”的职位总数。

有这么几个点挺让人注意的:

  • 平均薪资比前端多2K,从比例上看,Java薪资比前端高15%左右
  • 薪资分布比较平均,各个阶段都有
  • 高薪的比例比前端要多,看来Java的天花板比前端要高一些

以上可以看做是Java的优势,从工作付出,责任担当来看,这个薪资还是合理的。

我本来是全栈,后来主攻前端的原因,也是因为后端的责任压力太大了。

image.png

地区分布

地图影响力的权重设置如下:\

image.png

1
2
3
4
复制代码职位工资>10k 权重1.2
职位工资<10k 权重1
职位工资>15 权重1.5
各地区权重值总加和。

因为各个区的给高新企业的扶植政策不同,IT产业有着明显的集聚现象。

大家如果要找工作的话,还是需要考虑通勤,综合考虑。

另外,左侧一汽汽车厂区,对Java的需求并没有前端那么多,这点倒是挺超乎想象的。

公司行业类型

image.png
行业分类和其他地区比起来相对单一一些。

因为长春的互联网公司很少,IT开发基本都是软件服务为主。所以数据看起来比较极端。

公司福利

image.png
这里是公司给出的福利标签做的统计,不以实际情况为主。

就IT行业的加班情况,敢在公司详情页说明“偶尔加班”的,就算是良心了。

和前端对比,定期体检的比例竟然这么高,希望各位业界同仁注意身体。

image.png

技能要求

image.png
说道Java后端,那必须是Spring的天下,服务器也大都是MySql为主。

余下的就是spring系列全家桶:springBoot,springCloud之类的。

基本上Java基础过关,MySQL优化经验丰富,学好Spring全家桶的框架原理,就能冲击长春高薪了。

毕竟这里不是北上广深,还没卷到算法考试上来。

简短总结

数据上就是这样了,希望大家能对市场有个大致的了解。

新人不易,欢迎大家强势关注,长春本地技术公众号 - 阿丰在长春━(`∀´)ノ亻!

image.png

最近天气反复无常,大家注意增减衣服哦。

如果你还想看看前端的薪资待遇,可以点击这里观看

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

初识Spring(剑法第一剑之无中生有) Spring---

发表于 2021-11-18

Spring—–春天

1
2
3
4
5
6
7
8
9
10
markdown复制代码什么是spring?
简单介绍一下就是一个开源的,轻量级的,面向切面的,简化企业开发的复杂性的框架。
spring有哪些优点呢?
1.方便解耦,简化开发
通过spring提供的IOC容器,可以将对象间的依赖关系交由spring进行控制,避免硬编码所照成的过度程序耦合,从此我们就可以专注于写代码而不需要考虑其他的事情
2.spring的特性aop可以方便我们面向切面编程,从而实现程序的扩展
3.声明式事务的支持,可以让我们不在依赖于以往的编程式事务,通过声明式的方式可以更灵活的进行事务的管理,提高开发效率和质量
4.方便集成各种优秀的框架
5.降低对javaEE api的使用难度
6.Spring源码使用了大量的设计模式,设计巧妙,结构清晰,匠心独运,它的源码可以称的上是一个模板,非常值得我们进行学习。

Spring的体系结构

image.png

spring的核心之一:IOC

1
2
复制代码IOC翻译过来就是控制反转的意思,那么什么是控制反转呢,大致的意思就是说原来程序是由程序控制的,后来控制权变了,这就是控制反转。
首先我们来看一段代码

image.png

image.png

image.png

从上面的代码来看,我们有没有看出一些问题,相信有一定技术水平的已经看出来了吧。

1
2
3
ini复制代码    UserService userservice=new UserServiceImpl();
关键就在这行代码,如果我们在dao层加一个实现类,想要调用mysql数据库怎么办
是不是就要把newUserServiceImpl()给改一下,这种硬编码造成的代码耦合性太强,我们就无法灵活的切换数据库,控制权始终在程序的手里。

如何进行改进呢

image.png

image.png

看出来了吗,在service加一个set方法,在测试类中用户就可以通过这个set方法来选择它想要调用哪个数据库,我们就不用再去改代码了,耦合性降低,最终控制权由程序转向了用户。我们通过这个小例子简单理解一下控制反转。

Ioc快速入门

1
2
3
4
复制代码1.创建Maven项目,导入依赖坐标,Spring的依赖坐标
2.编写Dao接口UserDao以及实现UserDao接口
3.创建Spring核心配置文件,并配置UserDaoImpl
4.测试:使用Spring的APi,获取Bean的实例对象
1
2
3
4
5
6
7
8
9
10
11
12
xml复制代码<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
1
2
3
csharp复制代码    public interface UserDao {
void add();
}
1
2
3
4
5
6
7
csharp复制代码public class UserDaoImpl implements UserDao {


public void add() {
System.out.println("调用了UserDaoImpl的add方法~!~");
}
}

创建核心Spring配置文件,配置文件名称通常交applicationContext.xml

1
2
3
4
5
6
7
8
9
10
xml复制代码<!--
在这里告诉spring要创建哪个类的对象,并且给这个对象起一个别名

bean标签:
作用:用来托管(类)对象
属性:
id:是唯一标识,不能出现重复
class:托管类的全路径
-->
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl" />

接下来我们进行测试

image.png

spring核心配置文件详解

1
2
3
4
5
6
7
8
9
10
sql复制代码    scope:范围的意思,它的值主要用到的有2个
value:singleton单例的,我们无论找工厂要多少次对象,永远是它
prototype多例的,我们找工厂要对象,每次的对象都不一样
init-method:初始化方法,一般在对象创建的时候执行
destory-method:销毁方法,在对象销毁的时候执行

sping创建对象时默认调用的是无参构造
<bean id="ud" class="com.itheima.dao.impl.UserDaoImpl"
scope="singleton" init-method="" destroy-method="">
</bean>

依赖注入

1
2
lua复制代码刚才说了Spring可以为我们创建对象,那么属性该如何帮我们赋值呢
没错----就是通过依赖注入
第一种方式set方式注入

在bean标签中使用property标签
通过name
value
ref
来对属性赋值

1
2
3
4
ini复制代码<bean id="" class="">
<property name="属性名" value="属性值"></property>
<property name="属性名" ref="bean的id"></property>
</bean>
第二种方式构造方法注入
1
2
3
4
ini复制代码 <bean id="" class="">
<constructor-arg name="构造参数名称" value="构造参数的值"></constructor-arg>
<constructor-arg name="构造参数名称" ref="bean的id"></constructor-arg>
</bean>
第三种方式P名称空间注入(本质上就是set注入)
1
bash复制代码 <bean id="" class="" p:属性名="简单值" p:属性名-ref="bean的id"></bean>

引入外部配置文件

1
2
3
4
5
6
7
8
9
10
xml复制代码 <!--导入进来db.properties-->
<context:property-placeholder location="db.properties"/>

<!--在下面使用 ${}来取值-->
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
</bean>

分模块引入配置文件

1
2
ini复制代码    <import resource="classpath:applicationContext-service.xml"/>
<import resource="classpath:applicationContext-dao.xml"/>

Spring整合Juint

1
2
3
4
less复制代码@Runwith:用在测试类上,用于声明不在使用junit,而是使用spring运行环境
@ContextConfiguration:用在测试类上,用于指定spring配置类或配置文件,可以加载spring工厂

要使用以上注解要导入一下依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
xml复制代码<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
less复制代码       @RunWith
1. 表示使用的单元测试的环境,不再是以前的Junit的测试环境,而是spring针对Junit提供的测试环境
2. 这套spring提供的测试环境在背后会帮助我们创建工厂。
@ContextConfiguration
1. 告诉spring的测试环境,配置文件在哪里?
2. classpath: 这是固定写法,表示要在类路径底下找配置文件。
*/

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserServiceImpl02 {

//让spring把想要用到的对象给注入进来!
// 这个注解是自动注入的意思,让spring把对象注入进来。
@Autowired
private UserService us;

@Test
public void testAdd(){

/*ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserService us = (UserService) context.getBean("us");
*/
us.add();

}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

dart系列之 dart中的异步编程 简介 为什么要用异步编

发表于 2021-11-18

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战」

简介

熟悉javascript的朋友应该知道,在ES6中引入了await和async的语法,可以方便的进行异步编程,从而摆脱了回调地狱。dart作为一种新生的语言,没有理由不继承这种优秀的品质。很自然的,dart中也有await和async语言,一起来看看吧。

为什么要用异步编程

那么为什么要用异步编程呢? 只用同步不能够解决吗?

其实大多情况下同步已经够用了,但是在下面的几种情况下,同步的场景还是有缺陷的。

  1. 需要花很长时间从网络上下载数据的情况。
  2. 读取数据库的耗时情况。
  3. 从文件读取数据的情况。

总结而言,如果某些操作需要花费大量的时间,那么就可以用到异步编程了。

怎么使用

async是方法的描述符,如果要使用await,则必须配合async一起使用:

1
2
3
4
csharp复制代码Future<void> checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}

注意,await后面一般接着的是Future对象。

先看一个错误使用异步编程的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
csharp复制代码
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}

Future<String> fetchUserOrder() =>
Future.delayed(
const Duration(seconds: 2),
() => 'Order one!',
);

void main() {
print(createOrderMessage());
}

上面的代码本意是打印出从数据库耗时取出的数据,但是结果并不是想象的那样,其原因就是fetchUserOrder方法是一个异步方法,所以不会立即返回,从而导致结果打印失败。

将上面的代码用async改写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
csharp复制代码Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}

Future<String> fetchUserOrder() =>
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);

Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}

Future

上面我们在使用async和await的过程中使用到了Future。在java中Future表示的是线程的执行结果。在dart中Future表示的是一个异步执行的结果。

Future有两种状态:uncompleted 或者 completed。

当最开始执行一个异步函数的时候,会返回一个未完成的Future。这个未完成的Future会等等异步执行的完成或者失败。

不管异步程序是成功还是失败,最终都会返回一个完成状态。

async返回的Future可以接泛型,表示的时候返回的具体类型,比如Future 表示的是返回一个字符串,而 Future表示不返回任何值。

下面是两个不同返回的例子:

1
2
3
4
5
6
7
csharp复制代码Future<String> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
}

Future<void> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}

下面是一个异常的例子:

1
2
3
4
csharp复制代码Future<void> fetchUserOrder() {
return Future.delayed(const Duration(seconds: 2),
() => throw Exception('Logout failed: user ID is invalid'));
}

异步异常处理

在async的函数中,对await的异步方法中抛出的异常,可以直接是用try catch来进行异常的捕获:

1
2
3
4
5
6
dart复制代码try {
print('Awaiting user order...');
var order = await fetchUserOrder();
} catch (err) {
print('Caught error: $err');
}

在同步函数中调用异步函数

上面介绍的fetchUserOrder()返回的是一个Future,表示的是一个异步执行的过程。

那么如果是一个同步的方法,比如main()函数中,如何去调用异步方法,并且得到返回值呢?

await肯定是不行的,因为await只能在async的方法中调用。这个时候就可以用到then语句:

1
ini复制代码fetchUserOrder().then(order=>'do something');

then语句会等待异步执行返回结果,然后对结果进行处理,实际上就等同于javascript中的回调。

总结

以上就是dart中async和await的用法。

本文已收录于 www.flydean.com/12-dart-asy…

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Springboot的拦截过滤与监听 一、监听器 二、过滤器

发表于 2021-11-18

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战」

一、监听器

1.1 定义

Servlet 监听器是 Servlet 规范中定义的一种特殊类,用于监听 ServletContext、HttpSession 和 ServletRequest 等域对象的创建与销毁事件,以及监听这些域对象中属性发生修改的事件。监听器是观察者模式的应用,它关注特定事物,并伺机而动,所以监听器具有异步的特性。

Servlet Listener 监听三大域对象的创建和销毁事件,三大对象分别是:

  1. ServletContext:application 级别,整个应用只存在一个,可以进行全局应用配置。
  2. HttpSession:session 级别,针对每一个对话,如统计会话总数。
  3. ServletRequest:request 级别,针对每一个客户请求,

1.2 使用场景

Servlet 规范设计监听器的作用是在事件发生前、发生后进行一些处理,一般可以用来统计在线人数和在线用户、统计网站访问量、系统启动时初始化信息等。我们可以在容器启动时初始化 Log4j 信息,添加自己对容器状态的监控,初始化 Spring 组件等。

1.3 监听器的实现

创建一个ServletRequest监听器(其他监听器类似创建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码@WebListener
@Slf4j
public class Customlister implements ServletRequestListener{
​
   @Override
   public void requestDestroyed(ServletRequestEvent sre) {
       log.info(" request监听器:销毁");
  }
​
   @Override
   public void requestInitialized(ServletRequestEvent sre) {
       log.info(" request监听器:可以在这里记录访问次数哦");
  }
​
}

在启动类中加入@ServletComponentScan进行自动注册即可。

二、过滤器

2.1 定义

过滤器是一种可重用的代码,可以转换 HTTP 请求、响应和头信息,通俗来说就是过滤器可以在请求到达服务器之前,对请求头进行预先处理,在响应内容到达客户端之前,对服务器做出的响应进行后置处理。根据这个定义,我们就不难理解为什么它是位于 Listener 的下游,Servlet 的上游了?过滤器必须是在容器启动之后,接收到请求后开始处理,所以它是在 Listener 执行之后;在请求到达 Servlet 之前进行预处理,所以它又处于 Servlet 之前的位置。从命名上理解,它就是对请求和响应的数据内容进行过滤处理的,并非前端传过来的数据都全单接收,而是有原则地进行过滤处理,以保证后端服务器业务的安全。

2.2 使用场景

Servlet 3.1 中定义了几种常见的过滤器组件:

过滤器 作用
Authentication filters: 授权类,如用户登陆会话校验;
Logging and auditing filters: 日志和安全审计类;
Image conversion filters: 图片转换;
Data compression filters: 数据压缩;
Encryption filters: 加密、解密类;
Tokenizing filters: 词法类;
Filters that trigger resource access events: 触发资源访问事件类;
XSL/T filters that transform XML content: XML文件转换类;
MIME-type chain filters: MIME文件;
Caching filters: 缓存类;

或者我们社交应用经常需要的敏感词过滤,都可以使用过滤器。过滤器主要的特点在于,它能够改变请求内容。

2.3 过滤器的实现

过滤器Filter,是Servlet的的一个实用技术了。可通过过滤器,对请求进行拦截,比如读取session判断用户是否登录、判断访问的请求URL是否有访问权限(黑白名单)等。主要还是可对请求进行预处理。接下来介绍下,在springboot如何实现过滤器功能。

1.1 实现方式一:利用WebFilter注解配置

@WebFilter是Servlet3.0新增的注解,原先实现过滤器,需要在web.xml中进行配置,而现在通过此注解,启动启动时会自动扫描自动注册。

编写Filter类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
java复制代码//注册器名称为customFilter,拦截的url为所有
@WebFilter(filterName="customFilter",urlPatterns={"/*"})
@Slf4j
public class CustomFilter implements Filter{
​
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
       log.info("filter 初始化");
  }
​
   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
           throws IOException, ServletException {
       log.info("customFilter 请求处理之前");
       //对request、response进行一些预处理
       // 比如设置请求编码
       // request.setCharacterEncoding("UTF-8");
       // response.setCharacterEncoding("UTF-8");
​
       //链路 直接传给下一个过滤器
       chain.doFilter(request, response);
​
       log.info("customFilter 请求处理之后");
  }
​
   @Override
   public void destroy() {
       log.info("filter 销毁");
  }
}

然后在启动类加入@ServletComponentScan注解即可。使用这种方法,当注册多个过滤器时,无法指定执行顺序的,原本使用web.xml配置过滤器时,是可指定执行顺序的,但使用@WebFilter时,没有这个配置属性的(需要配合@Order进行),所以接下来介绍下通过FilterRegistrationBean进行过滤器的注册。

–小技巧–通过过滤器的java类名称,进行顺序的约定,比如LogFilter和AuthFilter,此时AuthFilter就会比LogFilter先执行,因为首字母A比L前面。

1.2.FilterRegistrationBean方式FilterRegistrationBean是springboot提供的,此类提供setOrder方法,可以为filter设置排序值,让spring在注册web filter之前排序后再依次注册。首先要改写filter, 其实就除了@webFilter注解即可。其他的都没有变化。启动类中利用@bean注册FilterRegistrationBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码@Configuration
public class FilterRegistration {
   @Bean
   public FilterRegistrationBean filterRegistrationBean() {
       FilterRegistrationBean registration = new FilterRegistrationBean();
       //当过滤器有注入其他bean类时,可直接通过@bean的方式进行实体类过滤器,这样不可自动注入过滤器使用的其他bean类。
       //当然,若无其他bean需要获取时,可直接new CustomFilter(),也可使用getBean的方式。
       registration.setFilter(customFilter());
       //过滤器名称
       registration.setName("customFilter");
       //拦截路径
       registration.addUrlPatterns("/*");
       //设置顺序
       registration.setOrder(10);
       return registration;
  }
​
   @Bean
   public Filter customFilter() {
       return new CustomFilter();
  }
}

注册多个时,就注册多个FilterRegistrationBean即可,启动后,效果和第一种是一样的。

三、servlet

3.1定义:

在java程序员10年以前做web开发的时候,所有的请求都是由servlet来接受并响应的。每一个请求,就要写一个servlet。这种方式很麻烦,大家就想能不能根据请求的路径以及参数不同,映射到不同的方法上去执行,这样就可以在一个servlet类里面处理多个请求,每个请求就是一个方法。这个思想后来就主键发展为structs、SpringMVC。

3.2使用场景

目前来看,servlet使用的场景已经被springMVC架构全面覆盖。但是不排除,老项目向spring boot项目迁移,需要支持servlet的情况。

3.3 实现

下面我们就看一下,在spring boot里面如何实现servlet。

1
2
3
4
5
6
7
8
9
java复制代码@WebServlet(name = "firstServlet", urlPatterns = "/firstServlet") //标记为servlet,以便启动器扫描。
public class FirstServlet extends HttpServlet {
​
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.getWriter().append("firstServlet");
  }
​
}

然后在启动类加入@ServletComponentScan注解即可。

四、拦截器

4.1定义

在 Servlet 规范中并没有拦截器的概念,它是面向切面编程的一种应用:在需要对方法进行增强的场景下,例如在方法调用前执行一段代码,或者在方法完成后额外执行一段操作,拦截器的一种实现方式就是动态代理。把过滤器和拦截器放在一起比较,我觉得是没有意义的,本质上是不同概念,并没有可比性,它位于过滤器的下游,是面向 Servlet 方法的。

4.2使用场景

AOP 编程思想面对的是横向的切面,而非纵向的业务。举个简单的例子,每个方法处理过程中,除了业务逻辑外,我们都会有一些相同的操作:参数校验,日志打印等,虽然这些处理代码并不多,但是每个方法都要写这类东西,工作量就不小了。能否使用程序来统一加入这类操作,而不用程序员自己手写呢?这就是切面编程思想的应用,利用 Java 的代理,在调用真正的方法之前或者之后,添加一些额外的增强功能。

4.3拦截器的实现

以上的过滤器、监听器都属于Servlet的api,我们在开发中处理利用以上的进行过滤web请求时,还可以使用Spring提供的拦截器(HandlerInterceptor)进行更加精细的控制。

编写自定义拦截器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码@Slf4j
public class CustomHandlerInterceptor implements HandlerInterceptor{
​
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  throws Exception {
 log.info("preHandle:请求前调用");
 //返回 false 则请求中断
 return true;
}
​
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  ModelAndView modelAndView) throws Exception {
 log.info("postHandle:请求后调用");
​
}
​
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
  throws Exception {
 log.info("afterCompletion:请求调用完成后回调方法,即在视图渲染完成后回调");
​
}
​
}

通过继承WebMvcConfigurerAdapter注册拦截器。WebMvcConfigurerAdapter类已经被废弃,请实现WebMvcConfigurer接口完成拦截器的注册。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码@Configuration
//废弃:public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter{
public class MyWebMvcConfigurer implements WebMvcConfigurer
@Override
 public void addInterceptors(InterceptorRegistry registry) {
  //注册拦截器 拦截规则
 //多个拦截器时 以此添加 执行顺序按添加顺序
 registry.addInterceptor(getHandlerInterceptor()).addPathPatterns("/*");
}

@Bean
public static HandlerInterceptor getHandlerInterceptor() {
 return new CustomHandlerInterceptor();
}
}

五、自定义事件和自定义监听器类

自定义事件和自定义监听器类的实现方式自定义事件:继承自ApplicationEvent抽象类,然后定义自己的构造器自定义监听:实现ApplicationListener接口,然后实现onApplicationEvent方法

springboot进行事件监听有四种方式1.手工向ApplicationContext中添加监听器2.将监听器装载入spring容器3.在application.properties中配置监听器4.通过@EventListener注解实现事件监听

5.1、代码实现

下面讲下4种事件监听的具体实现

方式1.

首先创建MyListener1类

1
2
3
4
5
6
java复制代码@Slf4j
public class MyListener1 implements ApplicationListener<MyEvent> {
   public void onApplicationEvent(MyEvent event) {
       log.info(String.format("%s监听到事件源:%s.", MyListener1.class.getName(), event.getSource()));
  }
}

然后在springboot应用启动类中获取ConfigurableApplicationContext上下文,装载监听

1
2
3
4
5
6
7
8
9
java复制代码@SpringBootApplication
public class BootLaunchApplication {
​
   public static void main(String[] args) {
       ConfigurableApplicationContext context = SpringApplication.run(BootLaunchApplication.class, args);
       //装载监听
       context.addApplicationListener(new MyListener1());
  }
}

方式2(推荐).

创建MyListener2类,并使用@Component注解将该类装载入spring容器中

1
2
3
4
5
6
7
8
9
java复制代码@Component
@Slf4j
public class MyListener2 implements ApplicationListener<MyEvent> {
​
   public void onApplicationEvent(MyEvent event) {
       log.info(String.format("%s监听到事件源:%s.", MyListener2.class.getName(), event.getSource()));
  }
​
}

方式3.

首先创建MyListener3类

1
2
3
4
5
6
java复制代码@Slf4j
public class MyListener3 implements ApplicationListener<MyEvent> {
   public void onApplicationEvent(MyEvent event) {
       log.info(String.format("%s监听到事件源:%s.", MyListener3.class.getName(), event.getSource()));
  }
}

然后在application.yml中配置监听

1
2
3
yml复制代码context:
listener:
  classes: club.krislin.customlistener.MyListener3

方式4(推荐).

创建MyListener4类,该类无需实现ApplicationListener接口,使用@EventListener装饰具体方法

1
2
3
4
5
6
7
8
java复制代码@Slf4j
@Component
public class MyListener4 {
   @EventListener
   public void listener(MyEvent event) {
       log.info(String.format("%s监听到事件源:%s.", MyListener4.class.getName(), event.getSource()));
  }
}

自定义事件代码如下:

1
2
3
4
5
6
7
8
java复制代码@SuppressWarnings("serial")
public class MyEvent extends ApplicationEvent
{
public MyEvent(Object source)
{
 super(source);
}
}

5.2、测试监听事件发布

有了applicationContext,想在哪发布事件就在哪发布事件

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomListenerTest {
​
   @Resource private
   ApplicationContext applicationContext;
​
   @Test
   public void testEvent(){
       applicationContext.publishEvent(new MyEvent("测试事件."));
  }
}

启动后,日志打印如下。(下面截图是在启动类发布事件后的截图,在单元测试里面监听器1监听不到,执行顺序问题):

image.png
由日志打印可以看出,SpringBoot四种事件的实现方式监听是有序的。无论执行多少次都是这个顺序。

小结

以上介绍了监听器,过滤器,servlet,拦截器,以及# 自定义事件和自定义监听器类的过程,通过对以上操作的学习,可以完成一些特殊场景下的功能。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…292293294…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%