1、StringUtils
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.7version>
dependency>
package com.hfut;
import org.apache.commons.lang3.StringUtils;
public class test {
public static void main(String[] args) {
String nullStr = null;
String emptyStr = "";
String spacesStr = " ";
String tabStr = "\t";
String str = "123";
System.out.println(StringUtils.isBlank(nullStr)); //true
System.out.println(StringUtils.isBlank(emptyStr)); //true
System.out.println(StringUtils.isBlank(spacesStr)); //true
System.out.println(StringUtils.isBlank(tabStr)); //true
System.out.println(StringUtils.isBlank(str)); //false
}
}
import com.google.common.base.Joiner;
StringUtils.joinWith(",", myStr, myInteger, myLong)
List<Long> tag = Arrays.asList(1L,2L,3L);
String str = Joiner.on(",").join(tag);
2、Google Guava库
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>21.0version>
dependency>
1)参数校验
import com.google.common.base.Preconditions;
Preconditions.checkArgument(!CollectionUtils.isEmpty(positionList),"资源位置信息参数不能为空");
Preconditions.checkNotNull(userId, "获取用户开户信息请求参数用户Id不能为空");
3、aviator 表达式解析包
Aviator依赖了commons-beanutils, 使用Aviator可以添加下面的maven依赖:
<dependency>
<groupId>com.googlecode.aviatorgroupId>
<artifactId>aviatorartifactId>
<version>2.3.3version>
dependency>
import com.googlecode.aviator.AviatorEvaluator;
Map<String, Object> expressionParams = Maps.newHashMapWithExpectedSize(6);
Object expressionRet = AviatorEvaluator.execute(expression, expressionParams);
Java 进阶 & Aviator 表达式的使用
1、数组和List转换
要想把基本数据类型的数组转化为其包装类型的list,可以使用guava类库的工具方法,示例如下:
int[] intArray = {1, 2, 3, 4};
List<Integer> list = Ints.asList(intArray);
转换后的list为定长list,不能添加和删除会报错。
2、判断集合是否为空
其中空指null或empty
import org.springframework.util.CollectionUtils;
if (CollectionUtils.isEmpty(myList)) {
return Collections.emptyList();
}
判断Map是否为空:
import org.apache.commons.collections.MapUtils;
if (MapUtils.isNotEmpty(myMap))
3、判断两个集合是否有交集
CollectionUtils.containsAny(Collection<?> source, Collection<?> candidates)
4、Collectors.groupingBy
5、集合排序,先按…排后按…排
myList.sort(Comparator.comparing(ResourceDTO::getPosition)
.thenComparing()
.thenComparing(Comparator.comparing(...).reversed()));
6、返回空集合
mymap = Collections.emptyMap();
1、stream使用
mylist.stream().collect(Collectors.toMap(Mybo::getId, p -> p, (a,b) -> a));
Java 8 Stream
Java流(Stream)操作实例-筛选、映射、查找匹配
2、反射的应用
Field dataField = t.getField("data");
dataField.setAccessible(true);
dataField.set(response, data);
3、Java的三种标准注解,四种元注解:
@Inherited用来修饰注解:被元注解Inherited修饰的注解,只有作用在类上时,会被子类继承此自定义的注解,其余情况都不会继承
Java中三种标准注解和四种元注解
4、Predicate
Predicate详解
5、构造函数,@Autowired,@PostConstruct的执行顺序为:
构造函数 > @Autowired > @PostConstruct
6、Java8新特性:时间API
1、分页接口 pagehelper
import com.github.pagehelper
@Autowired
private IMService mService;
PageInfo<MDO> pageInfo = mService.queryListByPage(int id, int pageNum, int pageSize);
List<MDO> res = pageInfo.getList(); // 该List为最终的该页查询出的结果
IMService接口实现:
@Override
public PageInfo<MDO> queryListByPage(int id, int pageNum, int pageSize) {
PageMethod.startPage(pageNum, pageSize);
List<MDO> myList = myMapper.queryList(id);
return new PageInfo<>(myList);
}
2、主从复制、读写分离
读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
【mysql 读写分离】10分钟了解读写分离的作用
3、Mybatis常用语法
1)去除多余的逗号:
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="position != null">
#{position,jdbcType=INTEGER},
if>
<if test="title != null">
#{title,jdbcType=VARCHAR},
if>
trim>
此外
和
标签也可以自动去除多余的逗号、AND 、OR
2)循环语法
and `position` in
<foreach item="item" collection="posIdList" separator="," open="(" close=")" index="">
#{item}
foreach>
3)尽量用>或>=,不用转义。
原有符号 | mybatis中 |
---|---|
< | < |
> | > |
& | & |
’ | ' |
" | " |
!= | ]]> |
4)insert时使用自增主键:useGeneratedKeys=“true”
5)select时尽量用:resultMap=“BaseResultMap”
6)BaseResultMap、数据库所有列写法:
<resultMap id="BaseResultMap" type="com.sankuai.fortune.marketing.domain.UserMarketingDO">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="user_id" jdbcType="BIGINT" property="userId"/>
<result column="activity_id" jdbcType="BIGINT" property="activityId"/>
<result column="status" jdbcType="INTEGER" property="status"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
<result column="try_times" javaType="INTEGER" property="tryTimes"/>
resultMap>
<sql id="Base_Column_List">
id,
involved_in_id,
user_id,
activity_id,
status,
error_code,
create_time,
update_time,
try_times
sql>
搜索所有列是时可以:select
7)LIKE语法:
title like concat('%',#{title},'%')
8)if…else…语法:
<choose>
<when test="">
//...
when>
<otherwise>
//...
otherwise>
choose>
4、数据库建表时推荐字符集:
DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
5、生产全局唯一ID:Leaf
Leaf——美团点评分布式ID生成系统
美团Leaf源码——snowflake模式源码解析
1、Json -> Object
new Gson().fromJson(value, type);
Gson的fromJson()方法
2、GsonUtil.toJson(userTags)
public static String toJson(Object object) {
return (new GsonBuilder()).serializeNulls().disableHtmlEscaping().create().toJson(object);
}
3、获取类型
Type type = new TypeToken
>() {}.getType();
Type setType = new TypeToken
4、创建GsonBuilder
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
1、yml配置多个环境
SpringBoot多环境yml文件配置
2、实现自定义注解
Spring 自定义注解
深入理解Java:注解(Annotation)自定义注解入门
3、实现ApplicationContextAware接口
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以很方便地从已有的spring上下文取得已实例化的bean。换句话说,就是这个类可以直接获取spring配置文件中,所有引用到的bean对象。
ApplicationContextAware使用理解
4、利用注释和AOP实现日志打印:
根据切点获取注释:
private MonitorLog getMonitorLog(ProceedingJoinPoint pjp) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
//获取方法的自定义注释
MonitorLog monitorLog = method.getAnnotation(MonitorLog.class);
if (monitorLog != null) {
return monitorLog;
}
//获取类的自定义注释
MonitorLog classMonitorLog = pjp.getTarget().getClass().getAnnotation(MonitorLog.class);
if (classMonitorLog != null) {
return classMonitorLog;
}
return null;
}
printReqLog(pjp, monitorLog);
Object ret = pjp.proceed();
printResLog(pjp, monitorLog, ret);
return ret;
@Pointcut("@annotation(com.hfut.shuili.common.annotation.MonitorLog)")
public void pointcut() {
}
@Around("!(execution(* com.hfut.shuili.thrift..*.*(..))) && pointcut() && @annotation(monitorLog)")
public Object monitorProceeding(ProceedingJoinPoint pjp, MonitorLog monitorLog) throws Throwable {
String simpleName = pjp.getTarget().getClass().getSimpleName();
MethodSignature signature = (MethodSignature) pjp.getSignature();
try {
//方法执行前
printReqLog(pjp, monitorLog);
//执行方法
Object ret = pjp.proceed();
//方法执行后
printResLog(pjp, monitorLog, ret);
return ret;
} catch (Throwable e) {
Cat.logEvent("ServiceException", simpleName.concat(".").concat(signature.getName()));
throw e;
}
}
@Around简单使用示例——SpringAOP增强处理
5、定时任务
使用spring提供的@Scheduled实现 SpringBoot使用@Scheduled创建定时任务
1、long时间戳与Date转换
import java.util.Date;
public class DateUtil extends cn.hutool.core.date.DateUtil {
/**
* 时间戳转date
* @param date
* @return
*/
public static Date parseFortuneTime(long date){
return DateUtil.date(date);
}
/**
* 转时间戳(毫秒)
* @param date
* @return
*/
public static Long formatFortuneTime(Date date){
if (date != null){
return date.getTime();
}
return null;
}
//获取某天0点
public static Date getStartTimeOfDay(Date date) {
Calendar day = Calendar.getInstance();
day.setTime(date);
// 24小时制度
day.set(Calendar.HOUR_OF_DAY, 0);
day.set(Calendar.MINUTE, 0);
day.set(Calendar.SECOND, 0);
day.set(Calendar.MILLISECOND, 0);
return day.getTime();
}
//获取年月日
public static Date timeToDate(Date date) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = simpleDateFormat.format(date);
try {
return simpleDateFormat.parse(dateStr);
} catch (ParseException e) {
throw new ServiceException(ErrorEnum.SYSTEM_INNER_ERROR);
}
}
}
import cn.hutool.core.date.DateTime;
import java.util.Calendar;
DateTime now = DateUtil.date(); //获取当前时间
Calendar day = Calendar.getInstance();
day.setTime(date);
引入Hutool:
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>4.5.15version>
dependency>
毫秒时间戳(13位)转秒时间戳(10位)
long timeStampSec = System.currentTimeMillis()/1000;
String timestamp = String.format("%010d", timeStampSec);
如 2017-02-27 15:48:27 的13位时间戳为:1488181707000
System.out.println(String.format("%010d",1488181707000L/1000));
输出:1488181707
时间格式:
SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); //设置日期格式
System.out.println(df.format(new Date())); // new Date()为获取当前系统时间
某日加几天:
import org.apache.commons.lang.time.DateUtils;
//startDate + x 天
Date endDate = DateUtils.addDays(startDate, x);
Hutool 参考文档
时间戳与时间转换网站
1、查看日志的Linux指令
tail request.log -n 300 -f
cat error.log -n | grep "timed out"
sed -n '95,98p' error.log
grep -n -C10 'Exception' error.log | tail -n 20
grep -E "method | userid" request.log --color
grep -E "method.*userid" request.log --color
grep -c '1234' error.log
wc -l a.txt
linux查看日志方法
grep -v、-e、-E
1、Redis持久化策略:RDB(数据快照)、AOF(文件重写)。两者都开启的话会优先使用AOF,因为AOF的完整性更高。执行持久化时是通过主进程fork子进程的方式实现,然后把持久化的工作交给子进程,自己不会有相关的 I/O 操作。Linux fork 子进程采用的是 copy-on-write 的方式
2、常用数据结构:String、List、Hash、Set、SortedSet、HyperLogLog、位图、地理信息定位
3、大key拆分:
可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;或者转成hash存储。
hash, set,zset,list 中存储过多的元素。
以hash为例,原先的正常存取流程是 hget(hashKey, field) ; hset(hashKey, field, value)
现在,固定一个桶的数量,比如 10000, 每次存取的时候,先在本地计算field的hash值,模除 10000, 确定了该field落在哪个key上。
newHashKey = hashKey + ( hash(field) % 10000); hset (newHashKey, field, value) ; hget(newHashKey, field)
4、使用Redis实现分布式锁:
SET key value NX EX 30
分布式锁用 Redis 还是 Zookeeper? 什么是分布式锁
可以用Redission实现分布式锁,又看门狗功能,每10s检查下当前线程没执行完毕,就再把过期时间延长30s。
5、Redis删除过期键的策略:
常见的删除策略:
(1)立即删除。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。
(2)惰性删除。键过期了就过期了,不管。每次从dict字典中按key取值时,先检查此key是否已经过期,如果过期了就删除它,并返回nil,如果没过期,就返回键值。
(3)定时删除。每隔一段时间,对expires字典进行检查,删除里面的过期键。
redis使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。
1、使用场景:异步、削峰、解耦。
2、MQ可能产生的问题:重复消费、消息丢失、顺序消费
3、主流的消息队列中间件主要有:Kafka、RocketMQ、ActiveMQ、RabbitMQ
参考:敖丙:消息队列
4、zookeeper启动命令:
5、kafka启动命令:
//有几个broker就设置几个broker.id
broker.id=0
listeners = PLAINTEXT://localhost:9092
log.dirs=/Users/mac/Documents/kafka/log
zookeeper.connect=localhost:2181
jps -l
20438 org.apache.zookeeper.server.quorum.QuorumPeerMain
21725 sun.tools.jps.Jps
20446 kafka.Kafka
6、创建主题:
kafka-topics.sh --zookeeper localhost:2181 --create --topic topic-demo --replication-factor 1 --partitions 4
7、订阅消息
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic topic-demo
8、生产者发送消息
kafka-console-producer.sh --broker-list localhost:9092 --topic topic-demo
> hello,kafka
Topic: topic-demo PartitionCount: 4 ReplicationFactor: 1 Configs:
Topic: topic-demo Partition: 0 Leader: 0 Replicas: 0 Isr: 0
Topic: topic-demo Partition: 1 Leader: 0 Replicas: 0 Isr: 0
Topic: topic-demo Partition: 2 Leader: 0 Replicas: 0 Isr: 0
Topic: topic-demo Partition: 3 Leader: 0 Replicas: 0 Isr: 0
9、生产者投送保障:
1⃣️At most once:消息可能会丢,但绝不会重复传输,客户端收到消息后,在处理消息前自动提交,这样kafka就认为consumer已经消费过了,偏移量增加。
2⃣️At least one:消息绝不会丢,但可能会重复传输,这样就可能出现消息处理完了,在提交反馈前,网络中断或者程序挂了,那么kafka认为这个消息还没有被consumer消费,产生重复消息推送。
3⃣️Exactly once:每条消息肯定会被传输一次且仅传输一次;
10、为了使得 Kafka 的吞吐率可以线性提高,物理上把 Topic 分成一个或多个 Partition。即同一个partition内的消息只能被同一个组中的一个consumer消费。当消费者数量多于partition的数量时,多余的消费者空闲。
11、生产者产生消息的路由策略:
12、删除消息策略:
对于传统的 message queue 而言,一般会删除已经被消费的消息,而 Kafka 集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此 Kafka 提供两种策略删除旧数据。一是基于时间,二是基于 Partition 文件大小。例如可以通过配置 $KAFKA_HOME/config/server.properties,让 Kafka 删除一周前的数据,也可在 Partition 文件超过 1GB 时删除旧数据。这里要注意,因为 Kafka 读取特定消息的时间复杂度为 O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。
13、zookeeper:JavaEE进阶——ZooKeeper和Curator
14、Mafka是基础架构-MQ团队从2016年开始自研的消息队列产品,底层基于Apache Kafka,增加了自研的基于机房粒度的中心化调度、时间回溯、粘性分配、死信、延迟队列、适用于美团自用的同步/异步客户端、机房容灾等高阶特性,目前版本是3.0。
1、基本类型 (括号内为对应的Java类型)
注意,thrift不支持无符号整型,因为很多目标语言不存在无符号整型
2、定义结构体:
struct DictionaryRes{
/**
* @FieldDoc(description = "状态")
**/
1: required Common.Status status;
/**
* @FieldDoc(description = "错误描述信息")
**/
2: optional Common.ErrorDTO error;
/**
* @FieldDoc(description = "字典数据")
**/
3: optional list<DictionaryDTO> data;
}
3、判断thrift里的变量是否被设置了
public boolean isSetId() {
return __isset_bit_vector.get(__ID_ISSET_ID);
}
4、Thrift调用外部服务
remoteAppKey为服务的app.name
@ThriftClientProxy(remoteAppKey = "com.hfut.ms.product", filterByServiceName = true, enableSignHandler = true)
private IProductService.Iface productService;
1、Mockito
Mockito 简明教程
https://www.jianshu.com/p/eae0187900f8
2、Mock步骤:
import org.mockito.Mock;
import org.junit.Before;
@Mock
private IUserProfileService.Iface userProfileService;
@Before
public void before() {
ReflectionTestUtils.setField(userProfileRemoteService,"userProfileService",userProfileService);
}
private void mockGetUserProfileResult(String city) throws TException {
UserProfileResult userProfileResult = new UserProfileResult();
userProfileResult.setStatuCode(0);
BaseInfo baseInfo = new BaseInfo().setCity(city);
userProfileResult.setData(new ResultData().setBaseInfo(baseInfo));
//不管userProfileService.getUserProfileResult函数传入什么参数,一律都返回userProfileResult
Mockito.when(userProfileService.getUserProfileResult(Mockito.anyString(),Mockito.any(UserFields.class))).thenReturn(userProfileResult);
}
使用ReflectionTestUtils解决依赖注入
3、测试类上常用的注解:
@ActiveProfiles("local") //选定配置文件
@RunWith(SpringRunner.class) //让测试运行于Spring测试环境
@Rollback //回滚
@Transactional //spring事务
@SpringBootTest
4、使内存数据库:H2
<dependency>
<groupId>com.h2databasegroupId>
<artifactId>h2artifactId>
<version>1.4.200version>
<scope>testscope>
dependency>
test/resources/sql下有ms-ddl.sql、ms-dml.sql
@Configuration
@MapperScan("com.hfut.Msproject.mapper")
public class DbConfig {
@Bean(name="dataSource")
public DataSource dataSource() throws SQLException {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:sql/ms-ddl.sql")
.addScript("classpath:sql/ms-dml.sql")
.build();
}
}
1、策略模式
注解中的变量作为key,对应的策略作为value:
//获取<枚举常量值, 策略>
private Map<Integer, MyStrategy> myStrategyMap = Maps.newHashMap();
//获取<"策略名", 策略>
Map<String, MyStrategy> beansWithAnnotationMap = SpringUtil.getApplicationContext().getBeansOfType(MyStrategy.class);
Collection<MyStrategy> strategies = beansWithAnnotationMap.values();
for (MyStrategy strategy : strategies) {
MyAnnotation annotation = strategy.getClass().getAnnotation(MyAnnotation.class); //获取注释
Preconditions.checkArgument(annotation != null, strategy.getClass().getName() + "未至指定MyAnnotation注解");
MyEnum[] categories = annotation.categories(); //获取注释中的参数值
for (MyEnum categoryEnum : categories) {
myStrategyMap.put(categoryEnum.getValue(), strategy);
}
}
其中MyAnnotation为自定义的注释。myStrategyMap即为最终的策略对应map
1、idea 插件 free mybatis plugin
2、Lombok
3、Thrift support