java 笔记

spring AOP 例子:

@Pointcut(切点) , @Aspect (切面) , @Around(环绕通知) , @Before(前置通知)

execution 表达式:
1.execution() (表达式的主题) , 2.第一个”public“符号 (返回值的类型为public,* 表示任意) , 3.com.aexpec.*.*.controller (包名) , 4.包名后面的”..“ (表示当前包及子包)
5.第二个”*“ (表示类名,*即所有类。此处可以自定义,下文有举例) , 6..*(..) (表示任何方法名,括号表示参数,两个点表示任何参数类型)

@Aspect
@Component
@Slf4j
public class RequestDigestLogAspect {

    @Value("${spring.application.name}")
    private String appName;

    @Pointcut("execution(public * com.aexpec.*.*.controller..*.*(..))")
    public void controllerPointCut() {
        //do nothing
    }

    @Around("controllerPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        return LogConstantsUtil.getPointCutResult(point, appName, log, true);
    }
}

------------------------------------------------------------------------------------------------------------------

java中isAssignableFrom()方法与instanceof关键字用法及通过反射配合注解为字段设置默认值
{父类.class.isAssignableFrom(子类.class)    ,    子类实例 instanceof 父类类型}

mybatis-plus ,如果操作同一个表的DO数据,可以直接用数据对象的属性名称赋值,反之,如果操作的不是数据库对象的DO,而且传入的也是对象,就需要对象.属性

MySQL 中, on duplicate key update的用法
说通俗点就是数据库中存在某个记录时,执行这个语句会更新,而不存在这条记录时,就会插入。

@Param注解的使用
在Insert 方法中,如果用了@param 注解,就不需要在xml中使用parameterType属性,可以直接接受@param传递回来的参数。

@ConfigurationProperties(prefix = "elp.order")  获取配置文件的信息,命名方式为驼峰命名,跟配置文件名一致。
激活方式为EnableConfigurationProperties,在启动类上标记(@EnableConfigurationProperties({OrderProperties.class)
使用 Spring Boot Configuration Processor 完成自动补全 可以补全配置记录
-----------如果不使用这个注解来注入到配置类上,需要定义一个普通的类,然后在进行一个@bean的注入,比较繁琐。

@Bean 这个注解添加在@Configuration 或 @SpringBootConfiguration注解类上,偶尔也可以添加在 @Conmponent 类上 ,他的作用就是定义一个bean。

@Value 相当于全局变量,定义在配置文件中 ,通过@Value 注解可以取出来 , class类需要交给spring 管理。 
它的好处不言而喻:
定义在配置文件里,变量发生变化,无需修改代码。
变量交给Spring来管理,性能更好


注入这个Environment对象
可以获取yml的配置文件以及preperties配置文件


拦截器 : 创建一个拦截器类:ApiInterceptor,并实现 HandlerInterceptor 接口:
public class ApiInterceptor implements HandlerInterceptor {    

//请求之前    
@Override    
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {        
System.out.println("进入拦截器");        
return true;    
}    

//请求时    
@Override    
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception 
{    
}    
//请求完成   
@Override    
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {    
}}


异常处理
需要写在类上@ControllerAdvice(优雅的处理异常)
@ExceptionHandler(Exception.class)    public String doError(Exception ex) throws Exception{        ex.printStackTrace();        return ex.getMessage();    }  

输入参数有误就是客户端
其他的错误就认为是服务端
客户端错误400开头,服务端500
比如:你的接口文档中说这个参数是必填的,服务端验证的时候发现没传就是客户端错误

使用JSON.parseObject(jsonStr, Model.class)是转简单的类型,而使用JSON.parseObject(jsonStr, new TypeReference<List<Model>>() {})可以转复杂的类型
List<Model> models = JSON.parseObject(jsonStr, new TypeReference<List<Model>>() {});
//        List<Model> models = JSON.parseObject(jsonStr, Model.class);这里就是用TypeReference的原因

@SuppressWarnings注解 作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。

springboot 自带打包,打成jar包后,需要把文件放入resources目录下,才能直接运行jar包读取配置文件

定时任务,使用@Scheduled(fixedRate = 60000 * 5) 注解,并在类上打compoent 交给spring管理,在启动类上打EnableSchdling注解

@PostConstruct 该注解可以在spring启动的时候,初始化类里面的内容

public interface ErrorMsgDaoManager extends IService<ErrorMsgDO>
public class ErrorMsgDaoManagerImpl extends ServiceImpl<ErrorMsgMybatis,ErrorMsgDO> implements ErrorMsgDaoManager ,使用(ErrorMsgDaoManager )接口的方法

resultMap 进行映射基本用于联表查询

springboot 异步处理消息  可以注入AsyncTaskExecutor , 使用orderAsyncExecutor.execute()方法。 类需要springboot 管理,打上component


@FunctionalInterface注解
Java8为函数式接口引入了一个新的注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器就会报错。

加不加@FunctionalInterface对于接口是不是函数式接口是没有影响的。该注解只是提醒编译器去检查该接口是否包含一个抽象方法。


@EnableAsync
threadPoolTaskExecutor.submit() 有返回结果,execute 是没有返回的结果  spring提供异步的方法。


项目启动,需要优先加载数据的可以有三种方式
1.@postconstruct 注解
2. class 类实现commandLineRunner重写run 方法,以及要打上component ,交给spring 管理
3. class 类实现ApplicaitonRunner,重写 run 方法,以及要打上component ,交给spring 管理
4. class 类实现InitializingBean ,重写 afterPropertiesSet, 以及要打上component ,交给spring 管理

配置文件
bootstrap 加载优先于 application 文件,一些加减密的东西,可能需要引入springcloud 才能使用bootstrap的配置文件。

ImmutableMap (Immutable ) 不可变的对象 
1)immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。

2)Immutable类的所有的属性都应该是final的。

3)对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。

4)对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。

5)如果类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身(该条可以归为第一条中的一个特例)

有时候你要实现的immutable类中可能包含mutable的类,比如java.util.Date,尽管你将其设置成了final的,但是它的值还是可以被修改的,
为了避免这个问题,我们建议返回给用户该对象的一个拷贝,这也是Java的最佳实践之一。

使用Immutable类的好处:
1)Immutable对象是线程安全的,可以不用被synchronize就在并发环境中共享

2)Immutable对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程间共享

3)Immutable对象提高了程序的性能,因为它减少了synchroinzed的使用

4)Immutable对象是可以被重复使用的,你可以将它们缓存起来重复使用,就像字符串字面量和整型数字一样。你可以使用静态工厂方法来提供类似于valueOf()这样的方法,
它可以从缓存中返回一个已经存在的Immutable对象,而不是重新创建一个。


springboot的三大核心注解
1.SpringBootConfiguration
2.EnableAutoConfiguration
3.ComponentScan

ApplicationContextAware 接口的作用 将 SpringContext 中的bean注入指定的 bean 对象中,如果当前 SpringContext 没有初始化完成,将等待完成后完成注入
需要重写setApplicationContext,以及注入bean的 方法 applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
解释:
(因为我们在做开发的时候,并不是说在每一个地方都能将属性注入到我们想要的地方去的,比如在Utils使用到dao,我们就不能直接注入了,
这个时候就是我们需要封装springContext的时候了,而ApplicationContextAware就起了关键性的作用。)
解释2:
(我们会经常使用注解的方式来加载我们需要的dao或者service,但是有些类我们没有让Spring帮我们自动加载,而我们需要在其中使用dao层中的方法或者service层中的方法)

@Configiration
配置类
 @Configuation等价于<Beans></Beans>
 @Bean等价于<Bean></Bean>
 @ComponentScan等价于<context:component-scan base-package="com.dxz.demo"/>


countdownlatch 是个计数器锁,相当于一个门栓,用户阻塞当前队列,等待其他线程都执行完毕在继续执行。初始化是需要传入一个int 类型的整数   CountDownLatch latch = new CountDownLatch(1);
作用: await()方法,阻塞线程,当线程都执行完毕的时候,计数器为0时,会唤醒await()方法中的等待线程会被执行。
       countDown()方法,计数器减一。

ReentrantLock 是悲观锁,隐式锁,通过锁获取方式以及释放锁,分为公平锁(fairSync)和非公平锁(NonfairSync) 。获取锁方法(try acquire) , 释放锁方法(try Release)。
Lock lock = new ReenTrantLock(); 
锁获取的公平性,即锁的获取按照先来先得的顺序,后来的不能抢先获取锁,非公平锁和公平锁也正是通过这个区别来实现了锁的公平性。
lockInterruptibly()方法,可中断的方式获取锁。
LockSupport.parkNanos()方法,在超时时间内没有被中断,那么线程会从超时等待状态转成了就绪状态,然后被CPU调度继续执行循环,而这时候线程已经达到超时等到的时间,返回false。


CAS (Compare and swap) 3个基本操纵数   内存地址(V) 旧的预期值(A) 新的预期值(B) ,内存地址值跟旧的预期值相等时,才会变为新的预期值。
volatile修饰可以保证线程之间的可见性
CAS 可能会出现ABA问题,可以利用版本号的比较来防止这个问题。

AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。

synchronized 锁升级,无锁-> 偏向锁 -> 轻量级锁 -> 重量级锁
线程在获取锁的时候,实际上就是获得一个监视器对象(monitor) ,而monitor是添加Synchronized关键字之后独有的。synchronized同步块使用了monitorenter和monitorexit
指令实现同步,线程执行到monitorenter指令时,会尝试获取对象所对应的monitor所有权,也就是尝试获取对象的锁,而执行monitorexit,就是释放monitor的所有权。

偏向锁的获取:一个线程多次获取锁,为了让线程获取锁的代价更低,引入了偏向锁,当一个线程访问了同步锁的代码块,会在对象头中存储当前线程的ID,
后续改线程进入和退出的时候就不需要加锁和释放锁,直接与对象头是否存储指向该线程的偏向锁,如果相等偏向锁就偏向该线程,就不需要尝试获取锁。


对象在内存中的布局
在 Hotspot 虚拟机中,对象在内存中的存储布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。一般而言,synchronized使用的锁
对象是存储在Java对象头里。它是轻量级锁和偏向锁的关键。

FreeMarker模板引擎动态生成html页面。

MockMvc单元测试
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,
这样可以使得请求的验证统一而且很方便。


类的生命周期 : 
加载 - > (验证 - > 准备 - > 解析) 连接 - > 初始化 - > 使用 - > 卸载


类加载器:
启动类加载器 - > 扩展类加载器 - > 应用类加载器 - > 自定义加载器 


类加载机制:双亲委派机制  , 向上委托


SpringCloud 可以识别bootstrap 包, springboot 可以识别application包。

--------------------------------------------------------------------------------------------------------
mysql 分库分表方案:
1.主从复制碰到的瓶颈
  
  Ⅰ 写入无法扩展 Ⅱ写入无法缓存  Ⅲ 复制延时   Ⅳ锁表率上升。  Ⅴ 表变大,缓存率下降 

优化的方案:
  Ⅰ mysql垂直分区  Ⅱ mysql水平分区
  
1.垂直分区:
    根据业务把数据切割,不同得业务放到不同的数据库服务器下,就算一台挂了,不影响其他服务器

2。水平分片:
    分为:单库单表,单库多表

mysql 存储引擎分为:1.inndb  2.MyISAM

存储文件不一致
    inndb 存储文件为:frm 表结构文件   idb 数据和索引文件    聚族索引  支持事务

    MyISAM 存储文件为:frm 表结构文件  myi 索引文件  myd 数据文件    非聚族索引    采用B+树。  不支持事务
MyISAM的索引文件仅存保存数据记录的地址。

B+树,都是非叶子节点保存指针,叶子节点保存数据

为什么mysql使用的时B+树而不是其他的数据结构,比如 B树
    因为B树,不管叶子节点非叶子节点都会保存数据,这样会导致非叶子节点上保存的指针会非常的少,指针少的情况下要保存大量的数据,只能增加树的高度,导致IO操作变多,查询性能变低


-----------------------------------------------------------------------------------
redis 

3.4 集群模式
节点:一个 Redis 集群中会由多个节点组成,每个节点都是互相连接的,会保存自己与其它节点的信息。节点之间通过 gossip 协议交换互相的状态,以及保存新加入的节点信息。

哈希槽:每个节点都会负责一定数量的哈希槽slot,每个key会映射到一个具体的slot,通过这种方式就可能找到key具体保存在哪个节点上了。

数据 Sharding: Redis Cluster 的整个数据库被分为 16384 个哈希槽,数据库中的每个键都属于这 16384 个槽中的其中一个,集群中的每个节点可以存 0 个或者最多 16384 个槽。
计算哈希槽位置使用的是 CRC16 算法,对键值进行CRC16计算后再对 16383 取模得到最终所属插槽

数据分片:
Sharding 流程: 
当客户端发起对键值对的操作指令后,将任意分配给其中某个节点
节点计算出该键值所属插槽
判断当前节点是否为该键所属插槽
如果是则直接执行操作命令
否则向客户端返回 moved 错误,moved 错误中将携带正确的节点地址与端口,客户端收到后可以直接转向正确节点


部署总结:
为了解决单机故障引入了主从模式,但主从模式存在一个问题:master 节点故障后,需要手动将 slave 节点切换成为 master 节点后服务才能恢复。Redis 为解决这一问题又引入了哨兵模式,哨兵模式能在 master 节点故障时,自动将 slave 节点提升成 master 节点,不需要人工干预即可恢复服务可用。但是主从模式、哨兵模式都没有达到真正的数据 sharding 存储,每个 Redis 实例中存储的都是全量数据,所以 Redis cluster 就诞生了,实现了真正的数据分片存储。

Redis cluster的性能和高可用性均优于哨兵模式,主要是针对海量数据 + 高并发 + 高可用的场景,如果是海量数据场景,那么建议就用 Redis cluster,数据量不是很大时,使用 sentinel 就够了。

    主要方法:
    T t = blockingFairQueue.take();  获取队列,并移除redis中的队列。
    delayedQueue.offer(t, delay, timeUnit);  添加延迟队列的方法 
    RBlockingQueue<T> blockingFairQueue = redissonClient.getBlockingQueue(queueKey);    redis,获取阻塞队列
        RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);    redis,获取延迟队列
    
/**
     * 添加队列
     *
     * @param t        DTO传输类
     * @param delay    时间数量
     * @param timeUnit 时间单位
     * @param queueKey queueKey 队列名
     * @param <T>      泛型
     */

public <T> void addQueue(T t, String queueKey, long delay, TimeUnit timeUnit) {
        LogUtils.info(log, "addQueue, queueKey:{0}, delay:{1}, timeUnit:{2}",queueKey, delay, timeUnit);
        RBlockingQueue<T> blockingFairQueue = redissonClient.getBlockingQueue(queueKey);
        RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
        delayedQueue.offer(t, delay, timeUnit);
        LogUtils.info(log, "queueName: {0}, blockFairQueueSize: {1}, delayedQueueSize: {2}", queueKey,
                blockingFairQueue.size(), delayedQueue.size());
    }


 /**
     * 获取队列
     *
     * @param taskEventListener 任务回调监听
     * @param <T>               泛型
     * @param queueKey queueKey 队列名
     * @return
     */
    public <T> void registerListener(String queueKey, TaskEventListener<T> taskEventListener) {
        RBlockingQueue<T> blockingFairQueue = redissonClient.getBlockingQueue(queueKey);
        RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);

        //新建线程
        Thread thread = new Thread(() -> {
            LogUtils.info(log,"启动监听队列线程{0}", queueKey);

            while (true) {
                String jobContent = "";
                try {
                    T t = blockingFairQueue.take();
                    LogUtils.info(log, "queueName: {0}, blockingFairQueueSize: {1}, delayedQueueSize: {2}", queueKey,
                            blockingFairQueue.size(), delayedQueue.size());
                    jobContent = JSON.toJSONString(t);
                    LogUtils.info(log,"监听队列线程{0},获取到值:{1}", queueKey, jobContent);
                    taskEventListener.invoke(t);
                } catch (RedissonShutdownException e) {
                    LogUtils.info(log, "redission shutdown, queueKey:{0}", queueKey );
                } catch (Exception e){
                    LogUtils.error(log, e, "监听队列线程错误, msg:{0}, 任务丢弃:{1}", e.getMessage(), jobContent);
                    shutdownMethod();
                }
            }
        });
        thread.setName(queueKey);
        thread.start();
    }


---------------------------------------------------------------------------------------------
MySQL safe-updates模式

问题:mysql执行update语句时,如果不是主键where语句会报错
原因:MySql运行在safe-updates模式下,该模式会导致非主键条件下无法执行update或者delete命令

SHOW VARIABLES LIKE 'SQL_SAFE_UPDATES';   查看状态

SET SQL_SAFE_UPDATES = 0;  开启    1开启 /0 关闭


---------------------------------------------------------------------------------------------

linux 命令

查看磁盘内存:
df -a    ,
df -hT    ,
du -h --max-depth=0    ,
du -h --max-depth=1    ,
du -h --max-depth=2    ;


-----------------------------------------------------------------------------


AQS:

顺序执行
1.try Acquire 尝试获取锁
2.addWriter  添加到队列里面  compareAndSetTail  CAS设置双向链表尾节点
3.acquireQueued        compareAndSetWaitStatus    CAS设置双向队列中前一个节点的等待状态

//双向链表的头节点
private transient volatile Node head;
/**
* 双向链表的尾节点
*/
private transient volatile Node tail;
/**
* 共享变量,使用volatile修饰保证线程可见性
* 锁状态,加锁成功则为1,重入+1 解锁则为0
*/
private volatile int state;

static final class Node {
//共享模式
static final Node SHARED = new Node();
//独占模式
static final Node EXCLUSIVE = null;
//因为超时或中断,该线程已经被取消
static final int CANCELLED = 1;
//线程的后继线程正/已被阻塞,当该线程release或cancel时要重新unpark唤醒这个后继线程
static final int SIGNAL = -1;
//表明该线程被处于条件队列,就是因为调用了Condition.await而被阻塞
static final int CONDITION = -2;
//传播共享锁 表示当前场景下后续的acquireShared能够得以执行releaseShared应该被传播
到其他节点
static final int PROPAGATE = -3;
// 当前队列节点的等待状态,不同的状态有不同的含义,可能的状态可以上面的一些静态变量如
SIGNAL 等
volatile int waitStatus;
/**
* 前驱节点
*/
volatile Node prev;
/**
* 后继节点
*/
volatile Node next;
//与当前节点关联的线程
volatile Thread thread;
//表示下一个等待节点(这个是和 Condition 相关的,暂时不讨论)
Node nextWaiter;


关注点:10:进入shouldParkAfterFailedAcquire(p, node)方法
perd.waitStatus就是head.waitStatus,初始化值为0。t2线程直接进入cas操作, 将head的
waitStatus置为-1
返回false,结束方法,进入第二次循环,第二次自旋
(每次入队都会将上一个Node的waitStatus置为-1状态,目的也是为了多自旋一次)
为什么阻塞状态ws=-1要让下一个队列线程来修改?(入队的时候会把上一个线程状态ws改为-1)
1、因为线程阻塞park了,自己不能修改ws状态,不能之前修改,怕出异常了
2、睡眠了是让队列下一个线程看到的,自己看不看无关紧要、


重点:parkAndCheckInterrupt() 阻塞park队列中的t2线程,在这个位置阻塞,下次唤醒也是这
个位置,记住了
LockSupport.park(this) 阻塞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值