随风

随风 查看完整档案

石家庄编辑  |  填写毕业院校  |  填写所在公司/组织 segmentfault.com/ 编辑
编辑

任重而道远

个人动态

随风 发布了文章 · 9月17日

Spring AOP事务管理

1 Spring中AOP事务简介

1.1 事务

事务(Transaction)是一个业务,是一个不可分割的逻辑工作单元,基于事务可以更好的保证业务的正确性。

1.2 事务特性: (ACID特性)

原子性(Atomicity):一个事务中的多个操作要么都成功要么都失败
一致性(Consistency): 操作前后总数应该是一致的。
隔离性(Isolation):事务与事务之间是相互隔离的
持久性(Durability):事务一旦提交数据会持久保存到数据库

2 Spring中事务管理

2.1spring中事务方式概述

Spring框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦。也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制。

在SpringBoot项目中,其内部提供了事务的自动配置,当我们在项目中添加了指定依赖spring-boot-starter-jdbc时,框架会自动为我们的项目注入事务管理器对象,最常用的为DataSourceTransactionManager对象。

2.2实际项目中最常用的注解方式的事务管理

基于@Transactional 注解进行声明式事务管理的实现步骤

1) 启用声明式事务管理,在项目启动类上添加@EnableTransactionManagement,咱们新版本中也可不添加(例如新版Spring Boot项目)。

2) 将@Transactional注解添加到合适的业务类或方法上,并设置合适的属性信息。

2.3 AOP 编程中基于注解方式的事务控制@Transactional

2.3.1@Transactional描述类表示类中所有方法都要进行事务控制,假如方法上也有该注解则方法上的事务注解特性优先级比较高
2.3.2 readOnly属性 含义:是否为只读事务(只允许查询操作 其他操作不允许) 默认值是false。readOnly=false所注解的方法或类代表增加,删除,修改业务.readOnly=true代表读取数据
2.3.3 rollbackFor属性含义是(什么异常回滚事务) 默认值是(RuntimeException与Error 出现这俩个都会回滚事务 但是检查异常(IllegalAccessException)不回滚)
2.3.4 noRollbackFor属性 含义是什么(什么情况下不回滚),没有默认值
2.3.5 timeout 属性
含义为是否支持事务超时,默认没有值,若为-1默认不支持事务超时,我们可以定义超时时间。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务开启以后到sql语句执行之前。
2.3.6 isolation=Isolation.READ_COMMITTED (默认写成它就行 不出现脏读),把事务隔离级别设置为它,不允许出现脏读(事务隔离级别较低)。隔离级别越高并发就会越小,性能越差,但更安全。

事务控制过程分析

image
Spring事务管理是基于接口代理(JDK)或动态字节码(CGLIB)技术,然后通过AOP实施事务增强的。当我们执行添加了事务特性的目标方式时,系统会通过目标对象的代理对象调用DataSourceTransactionManager对象,在事务开始的时,执行doBegin方法,事务结束时执行doCommit或doRollback方法。

3 Spring 中事务传播特性

事务传播(Propagation)特性指"不同业务(service)对象"中的事务方法之间相互调用时,事务的传播方式。

3.1常用事务传播方式

3.1.1@Transactional(propagation=Propagation.REQUIRED)
如果没有事务创建新事务, 如果当前有事务参与当前事务, Spring 默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。

//代码示例
@Transactional(propagation = Propagation.REQUIRED)

 @Override

 public List<Node> findZtreeMenuNodes() {

 return sysMenuDao.findZtreeMenuNodes();

 }

当有一个业务对象调用如上方法时,此方法始终工作在一个已经存在的事务方法,或者是由调用者创建的一个事务方法中。
3.1.2@Transactional(propagation=Propagation.REQUIRES_NEW)
必须是新事务, 如果有当前事务, 挂起当前事务并且开启新事务。

//代码示例
@Transactional(propagation = Propagation.REQUIRES_NEW)

 @Override

 public void saveObject(SysLog entity) {

 sysLogDao.insertObject(entity);

 }

当有一个业务对象调用如上业务方法时,此方法会始终运行在一个新的事务中。

查看原文

赞 0 收藏 0 评论 0

随风 发布了文章 · 9月15日

Mybatis中pagehelper分页插件

1.PageHelper依赖查找.

image

2.分页插件应用步骤示例

image

查看原文

赞 0 收藏 0 评论 0

随风 发布了文章 · 9月15日

Mybatis中的一级缓存、二级缓存

部分内容

mybatis中一级缓存应用位置
image
mybatis中二级缓存应用位置
image
MyBatis中二级缓存readOnly属性分析
image

1.当readOnly为true时,sqlSession对象会将查询结果直接存储到cache,在cache中是一个Map集合,最终结果指向用一个对象,所以result1==result2,地址值比较为true
result1.equals(result2) 属性值比较也为true
2当readOnly为false时,会将对象转化为字节(序列化)存储到cache,在cache中是一个Map集合,接着会进行反序列化操作,所以指向的是不用对象但内容相同。result1==result2,地址值比较为false
result1.equals(result2) 属性值为true

Mapper文件中readOnly的添加位置
image

查看原文

赞 2 收藏 2 评论 0

随风 赞了文章 · 9月15日

本地缓存(Cache)系统简易设计

本地Cache系统简易设计

为什么使用缓存?

  • 降低数据库的访问压力。
  • 提高查询效率。
  • 改善用户体验。

你都了解哪些缓存?

  • 数据库内置缓存(DBA修改)。
  • 数据层缓存(由持久层框架决定,例如mybatis)
  • 业务层缓存(由业务层框架以及第三缓存产品决定:本地缓存+分布式缓存)
  • 浏览器缓存(Cache-Control)

设计缓存都应该考虑什么问题?

  • 存储结构:使用什么结构存储数据?(数组,链表,散列存储-哈希存储)
  • 淘汰算法:有限容量(LRU,FIFO,.....),不限容量(GC)
  • 并发安全:保证线程安全。
  • 任务调度:每隔多长时间清理一下缓存。
  • 日志记录:是否命中?(命中率)

缓存系统设计基础

缓存标准定义

package com.cy.java.cache;  
/\*\* Cache接口设计\*/  
public interface Cache {  
 public void putObject(Object key,Object value);  
 public Object getObject(Object key);  
 public Object removeObject(Object key);  
 public void clear();  
 public int size();  
}

简易Cache实现

场景应用:
  • 存储数据量比较小(因为没有考虑淘汰机制)
  • 没有线程共享(一个线程的内部缓存)
  • 缓存对象生命周期比较短
package com.cy.java.cache;  
import java.util.HashMap;  
import java.util.Map;  
/**负责真正存储数据的一个对象,将数据存储到一个map中*/  
public class PerpetualCache implements Cache {  
/** 特点:线程不安全,key不允许重复,不能保证key的顺序  */  
private Map<Object,Object> cache=new HashMap<>();  
@Override  
public void putObject(Object key, Object value) {  
cache.put(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.get(key);  
}  
@Override  
public Object removeObject(Object key) {  
return cache.remove(key);  
}  
@Override  
public void clear() {  
 cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
 Cache cache=new PerpetualCache();  
 cache.putObject("A", 100);  
 cache.putObject("B", 200);  
 cache.putObject("C", 300);  
 System.out.println(cache);  
 cache.removeObject("D");  
 cache.clear();  
 System.out.println(cache.size());  
}  
}

构建线程安全Cache对象

场景应用:并发环境
package com.cy.java.cache;  
/**线程安全的cache对象*/  
public class SynchronizedCache implements Cache{  
private Cache cache;  
public SynchronizedCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public synchronized void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public synchronized Object getObject(Object key) {  
// TODO Auto-generated method stub  
return cache.getObject(key);  
}  
@Override  
public synchronized Object removeObject(Object key) {  
// TODO Auto-generated method stub  
return cache.removeObject(key);  
}  
@Override  
public synchronized void clear() {  
cache.clear();  
}  
@Override  
public synchronized int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=

 new SynchronizedCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
}  
  
}

思考:对于SynchronizedCache 有什么优势,劣势?

支持日志记录的Cache实现

package com.cy.java.cache;  
/** 用于记录命中率的日志cache*/  
public class LoggingCache implements Cache {  
private Cache cache;  
/**记录请求次数*/  
private int requests;  
/**记录命中次数*/  
private int hits;  
public LoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
Object obj=cache.getObject(key);  
if(obj!=null)hits++;  
System.out.println("Cache hit Ratio : "+hits\*1.0/requests);  
return obj;  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new PerpetualCache()));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
System.out.println(cache);  
cache.getObject("D");  
cache.getObject("A");  
}  
  
}

思考:你觉得LoggingCache记录日志的方式有什么不好的地方?(信息的完整性,同步问题)

LruCache实现

应用场景:基于LRU算法的的基本实现
package com.cy.java.cache;  
import java.util.LinkedHashMap;  
import java.util.Map;  
  
/** 缓存淘汰策略:LRU(最近最少使用算法)*/  
public class LruCache implements Cache {  
 private Cache cache;  
 /**通过此属性记录要移除的数据对象*/  
 private Object eldestKey;  
 /**通过此map记录key的访问顺序*/  
 private Map<Object,Object> keyMap;  
 @SuppressWarnings("serial")  
 public LruCache(Cache cache,int maxCap) {  
 this.cache=cache;  
//LinkedHashMap可以记录key的添加顺序或者访问顺序  
 this.keyMap=

 new LinkedHashMap<Object,Object>(maxCap, 0.75f, true)

 {//accessOrder  
//此方法每次执行keyMap的put操作时调用  
@Override  
protected boolean removeEldestEntry

 (java.util.Map.Entry<Object, Object> eldest) {  
boolean isFull=size()>maxCap;  
if(isFull)eldestKey=eldest.getKey();  
return isFull;  
}  
 };  
}  
@Override  
public void putObject(Object key, Object value) {  
//存储数据对象  
cache.putObject(key, value);  
//记录key的访问顺序,假如已经满了,就要从cache中移除数据  
keyMap.put(key, key);//此时会执行keyMap对象的removeEldestEntry  
if(eldestKey!=null) {  
cache.removeObject(eldestKey);  
eldestKey=null;  
}  
}  
@Override  
public Object getObject(Object key) {  
keyMap.get(key);//记录key的访问顺序  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
  
@Override  
public void clear() {  
cache.clear();  
keyMap.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
SynchronizedCache cache=  
 new SynchronizedCache(  
new LoggingCache(  
new LruCache(new PerpetualCache(),3)));  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.getObject("A");  
cache.getObject("C");  
cache.putObject("D", 400);  
cache.putObject("E", 500);  
System.out.println(cache);  
  
}  
}

设置Cache淘汰算法:FIFO算法

package com.cy.java.cache;  
import java.util.Deque;  
import java.util.LinkedList;  
/**  
* FifoCache :基于FIFO算法(对象满了要按先进先出算法移除对象)实现cache对象  
*/  
public class FifoCache implements Cache{  
/**借助此对象存储数据*/  
private Cache cache;  
/**借助此队列记录key的顺序*/  
private Deque<Object> keyOrders;  
/**通过此变量记录cache可以存储的对象个数*/  
private int maxCap;  
public FifoCache(Cache cache,int maxCap) {  
this.cache=cache;  
keyOrders=new LinkedList<>();  
this.maxCap=maxCap;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.记录key的顺序(起始就是存储key,添加在队列最后位置)  
keyOrders.addLast(key);  
//2.检测cache中数据是否已满,满了则移除。  
if(keyOrders.size()>maxCap) {  
Object eldestKey=keyOrders.removeFirst();  
cache.removeObject(eldestKey);  
}  
//3.放新的对象  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
return cache.getObject(key);  
}  
  
@Override  
public Object removeObject(Object key) {  
Object obj=cache.removeObject(key);  
keyOrders.remove(key);  
return obj;  
}  
@Override  
public void clear() {  
cache.clear();  
keyOrders.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=

 new SynchronizedCache(  
new LoggingCache(  
 new FifoCache(  
  new PerpetualCache(),3)));  
cache.putObject("A",100);  
cache.putObject("B",200);  
cache.putObject("C",300);  
cache.getObject("A");  
cache.putObject("D",400);  
cache.putObject("E",500);  
System.out.println(cache);  
}   
}

序列化Cache的实现

场景:存储到cache的是对象的字节
package com.cy.java.cache;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class SerializedCache implements Cache {  
  
private Cache cache;  
public SerializedCache(Cache cache) {  
this.cache=cache;  
}  
/**序列化*/  
private byte\[\] serialize(Object value) {  
//1.构建流对象  
ByteArrayOutputStream bos=null;  
ObjectOutputStream oos=null;  
try {  
//1.2构建字节数组输出流,此流对象内置可扩容的数组。  
bos=new ByteArrayOutputStream();  
//1.3构建对象输出流  
oos=new ObjectOutputStream(bos);  
//2.对象序列化  
oos.writeObject(value);

 //此时对象会以字节的方式写入到字节数组输出流  
oos.flush();  
return bos.toByteArray();  
}catch (Exception e) {  
 throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bos!=null)

 try{bos.close();bos=null;}catch(Exception e) {}  
if(oos!=null)

 try{oos.close();oos=null;}catch (Exception e2) {}  
}  
}  
/**反序列化*/  
public Object deserialize(byte\[\] value) {  
//1.创建流对象  
ByteArrayInputStream bis=null;  
ObjectInputStream ois=null;  
try {  
//1.1构建字节数组输入流,此对象可以直接读取数组中的字节信息  
bis=new ByteArrayInputStream(value);  
//1.2构建对象输入流(对象反序列化)  
ois=new ObjectInputStream(bis);  
//2.反序列化对象  
Object obj=ois.readObject();  
return obj;  
}catch(Exception e) {  
throw new RuntimeException(e);  
}finally {  
//3.关闭流对象  
if(bis!=null)

 try{bis.close();bis=null;}catch(Exception e) {}  
if(ois!=null)

 try{ois.close();ois=null;}catch (Exception e2) {}  
}  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, serialize(value));  
}  
@Override  
public Object getObject(Object key) {  
return deserialize((byte\[\])cache.getObject(key));  
}  
@Override  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new SerializedCache(new PerpetualCache());  
cache.putObject("A", 200);  
cache.putObject("B", 300);  
Object v1=cache.getObject("A");  
Object v2=cache.getObject("A");  
System.out.println(v1==v2);  
System.out.println(v1);  
System.out.println(v2);  
}  
}

软件引用Cache实现

应用场景:内存不足时淘汰缓存中数据
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.SoftReference;  
/**软引用*/  
public class SoftCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public SoftCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new SoftEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
SoftEntry softEntry=(SoftEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
SoftEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
 while((softEntry=

 (SoftEntry)garbageOfRequenceQueue.poll())!=null){  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/\*\*定义软引用类型\*/  
private static class SoftEntry extends SoftReference<Object\>{  
private final Object key;  
public SoftEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
// TODO Auto-generated method stub  
return cache.toString();  
}  
  
public static void main(String\[\] args) {  
Cache cache=new SoftCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

弱Cache对象实现

应用场景:GC触发清除缓存对象
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.WeakReference;  
/\*\*弱引用\*/  
public class WeakCache implements Cache {  
private Cache cache;  
private ReferenceQueue<Object> garbageOfRequenceQueue=

 new ReferenceQueue<>();  
public WeakCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.将对象存储到cache(key不变,Value为为soft引用对象)  
cache.putObject(key, 

 new WeakEntry(key, value, garbageOfRequenceQueue));  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取软引用对象并判断  
WeakEntry softEntry=(WeakEntry)cache.getObject(key);  
if(softEntry==null)return null;  
//2.基于软引用对象获取它引用的对象并判断  
Object target = softEntry.get();  
if(target==null)cache.removeObject(key);  
return target;  
}  
  
@Override  
public Object removeObject(Object key) {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.从cache中移除对象  
Object removedObj=cache.removeObject(key);  
return removedObj;  
}  
  
@Override  
public void clear() {  
//1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
removeGarbageObjects();  
//2.清空cache  
cache.clear();  
}  
  
@Override  
public int size() {  
removeGarbageObjects();  
return cache.size();  
}  
private void removeGarbageObjects() {  
WeakEntry softEntry=null;  
//1.从引用队列中获取已经被GC的一些对象的引用  
while((softEntry=

 (WeakEntry)garbageOfRequenceQueue.poll())!=null) {  
//softEntry不为null表示softEntry引用的对象已经被移除  
//2.从cache中将对象引用移除。  
cache.removeObject(softEntry.key);  
}  
}  
/**定义软引用类型*/  
private static class WeakEntry extends WeakReference<Object\>{  
private final Object key;  
public WeakEntry(Object key,

 Object referent, ReferenceQueue<? super Object> rQueue) {  
super(referent, rQueue);  
this.key=key;  
}  
}  
@Override  
public String toString() {  
return cache.toString();  
}  
public static void main(String\[\] args) {  
Cache cache=new WeakCache(new PerpetualCache());  
cache.putObject("A", new byte\[1024\*1024\]);  
cache.putObject("B", new byte\[1024\*1024\]);  
cache.putObject("C", new byte\[1024\*1024\]);  
cache.putObject("D", new byte\[1024\*1024\]);  
cache.putObject("E", new byte\[1024\*1024\]);  
cache.putObject("F", new byte\[1024\*1024\]);  
cache.putObject("G", new byte\[1024\*1024\]);  
System.out.println(cache.size());  
System.out.println(cache);  
}  
  
}

缓存系统设计进阶

缓存应用需求升级

  • 缓存系统既要保证线程安全又要保证性能。
  • 缓存日志的记录要写到文件,而且是异步写
  • 向缓存中写数据时要提高序列化性能。

缓存对象读写锁应用

package com.cy.java.cache;  
  
import java.util.concurrent.locks.ReentrantReadWriteLock;  
  
/**  
* 构建线程安全对象,基于ReentrantReadWriteLock对象实现读写锁应用。  
* @author qilei  
*/  
public class ReentrantLockCache implements Cache {  
  
private Cache cache;  
/**  
* 此对象提供了读锁,写锁应用方式.  
* 1)写锁:排他锁  
* 2)读锁:共享锁  
* 说明:读写不能同时执行。  
*/  
private final ReentrantReadWriteLock readWriteLock =  
new ReentrantReadWriteLock();  
public ReentrantLockCache(Cache cache) {  
this.cache=cache;  
// TODO Auto-generated constructor stub  
}  
@Override  
public void putObject(Object key, Object value) {  
readWriteLock.writeLock().lock();  
try {  
cache.putObject(key, value);  
}finally {  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public Object getObject(Object key) {  
readWriteLock.readLock().lock();  
try {  
 Object object=cache.getObject(key);  
 return object;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
  
@Override  
public Object removeObject(Object key) {  
readWriteLock.writeLock().lock();  
try {  
 Object object=cache.removeObject(key);  
 return object;  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public void clear() {  
readWriteLock.writeLock().lock();  
try {  
 cache.clear();  
}finally{  
readWriteLock.writeLock().unlock();  
}  
}  
  
@Override  
public int size() {  
readWriteLock.readLock().lock();  
try {  
 int size=cache.size();  
 return size;  
}finally{  
readWriteLock.readLock().unlock();  
}  
}  
}

异步日志Cache实现

第一步:添加依赖
` <dependency>  
 <groupId>ch.qos.logback</groupId>  
 <artifactId>logback-classic</artifactId>  
 <version>1.2.3</version>  
 </dependency> `
第二步:添加配置文件logback.xml (参考项目代码)
 
<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
  
<logger name="com.cy" level="TRACE" />  
<appender name="FILE"  
class\="ch.qos.logback.core.rolling.RollingFileAppender"\>  
<rollingPolicy  
class\="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"\>  
<!--文件路径,定义了日志的切分方式----把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间 -->  
<fileNamePattern>logs/context-log.%d{yyyy-MM-dd}.log  
</fileNamePattern>  
<!--只保留最近30天的日志 -->  
<maxHistory>30</maxHistory>  
</rollingPolicy>  
<encoder charset="UTF-8"\>  
<pattern>\[%-5level\] %date --%thread-- \[%logger\] %msg %n</pattern>  
</encoder>  
</appender>  
  
<appender name="ASYNC\_FILE"  
class\="ch.qos.logback.classic.AsyncAppender"\>  
<discardingThreshold>0</discardingThreshold>  
<queueSize>256</queueSize>  
<appender-ref ref="FILE" />  
</appender>  
  
<root level="debug"\>  
<appender-ref ref="ASYNC\_FILE" />  
</root>  
  
</configuration>
第三步:构建AsyncLoggingCache
package com.cy.java.cache;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
/**  
* 通过此对象异步记录查询操作的命中率  
* 1)选择日志库  
* 2)执行异步写操作。  
*/  
public class AsyncLoggingCache implements Cache {  
 //日志门面应用  
 private static Logger log=LoggerFactory.getLogger(LoggingCache.class);  
private Cache cache;  
/**表示请求次数*/  
private int requests;  
/**命中次数(命中表示从缓存中取到数据了)*/  
private int hits;  
public AsyncLoggingCache(Cache cache) {  
this.cache=cache;  
}  
@Override  
public void putObject(Object key, Object value) {  
cache.putObject(key, value);  
}  
@Override  
public Object getObject(Object key) {  
requests++;  
 Object obj=cache.getObject(key);  
 if(obj!=null)hits++;  
 //记录日志耗时  
 log.info("Cache hit Ratio:{}",hits\*1.0/requests);  
return obj;  
}  
@Override  
  
public Object removeObject(Object key) {  
return cache.removeObject(key);  
}  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache= 

 new AsyncLoggingCache(new PerpetualCache());  
cache.putObject("A", 100);  
cache.putObject("B", 200);  
cache.putObject("C", 300);  
cache.putObject("D", 400);  
//System.out.println(cache);  
cache.getObject("E");  
cache.getObject("A");  
cache.getObject("B");  
}  
  
}

Kryo构建序列化Cache

第一步:添加依赖
<dependency>  
 <groupId>com.esotericsoftware</groupId>  
 <artifactId>kryo</artifactId>  
 <version>5.0.0-RC5</version>  
</dependency>
第二步:构建项目工具类
public class KryoUtils {  
  
/**  
* 多线程并发执行时,可能会出现线程不安全,具体原因是什么?  
* 1)多个线程的并发  
* 2)多个线程有数据共享  
* 3)多个线程在共享数据集上的操作不是原子操作  
*  
* 分析:当出现了线程不安全,如何进行修改来保证线程安全  
* 1)将多线程改为单线程。  
* 2)取消共享 (例如在当前应用中我们一个线程一个Kryo对象)  
* 3)加锁+CAS  
*  
* ThreadLocal提供了这样的一种机制:  
* 1)可以将对象绑定到当前线程(其实是将对象存储到当前线程的map中)  
* 2)可以从当前线程获取绑定的对象(从当前线程的map中获取对象)  
*/  
static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {  
  protected Kryo initialValue() {  
  Kryo kryo = new Kryo();  
  // Configure the Kryo instance.  
  kryo.setRegistrationRequired(false);  
  //....  
  return kryo;  
  };  
 };  
 public static Object deserialize(byte\[\] array){  
Kryo kryo=kryos.get();  
 Input input = new Input(new ByteArrayInputStream(array));  
 Object obj=kryo.readClassAndObject(input);  
return obj;  
 }  
 public static byte\[\] serialize(Object object){  
 //从当前线程获取kryo对象,当前线程没有会调用ThreadLocal的initialValue方法创建对象并绑定线程  
 Kryo kryo=kryos.get();  
 ByteArrayOutputStream bos=new ByteArrayOutputStream();  
 Output output = new Output(bos);  
 kryo.writeClassAndObject(output, object);  
  output.close();  
 return bos.toByteArray();  
 }  
 }

> 构建高性能序列化Cache

public class KryoSerializedCache implements Cache {  
  
private Cache cache;  
public KryoSerializedCache(Cache cache) {  
this.cache=cache;  
}  
  
@Override  
public void putObject(Object key, Object value) {  
//1.将对象序列化  
byte\[\] array=KryoUtils.serialize(value);  
//2.将序列化后的字节数组引用存储到cache  
cache.putObject(key,array);  
}  
  
@Override  
public Object getObject(Object key) {  
//1.基于key获取缓存中的字节数组引用  
byte\[\] array=(byte\[\])cache.getObject(key);  
//2.将字节数组反序列化为对象  
return KryoUtils.deserialize(array);  
}  
  
@Override  
public Object removeObject(Object key) {  
return KryoUtils.deserialize((byte\[\])cache.removeObject(key));  
}  
  
@Override  
public void clear() {  
cache.clear();  
}  
@Override  
public int size() {  
return cache.size();  
}  
public static void main(String\[\] args) {  
Cache cache=new KryoSerializedCache(new PerpetualCache());  
cache.putObject("A", 500);  
Object a1=cache.getObject("A");  
Object a2=cache.getObject("A");  
System.out.println("a1="+a1);  
System.out.println("a2="+a2);  
System.out.println(a1==a2);//false  
}  
  
}
查看原文

赞 35 收藏 12 评论 4

随风 发布了文章 · 9月15日

jqyert中操作属性的函数

jqyert中操作属性的函数 prop() attr()
1.prop()用于获取dom对象属性值或赋值,只能拿到标准属性,不能拿到某些自定义属性,基本语法prop(属性名,[属性值])属性值可写可不写
2attr()可以拿到标准属性,也能拿到自定义属性

jquery中的data函数用于在指定dom对象上存储数据,取数据
基本语法data(key[,value]);假如只有key则表示取值,有key value表示赋值,只有先赋了值才能取到值。

查看原文

赞 0 收藏 0 评论 0

随风 发布了文章 · 9月10日

如何将服务器端拿到的数据呈现到页面的流程总结

首先控制层代码
@ResponseBody注解告诉spring 框架将返回值对象转换为json格式字符串

@Controller
@RequestMapping("/activity/")
public class ActivityController {

     @Autowired
     private ActivityService activityService;
     /**
      *     基于此方法返回activity页面
      * @return
      */
     @RequestMapping("doActivityUI")
     public String doActivityUI() {
         return "activity01";
     }
     
      @RequestMapping("doFindActivitys")
     @ResponseBody //告诉spring 框架将返回值对象转换为json格式字符串
     public List<Activity> doFindActivitys()throws Exception{
          Thread.sleep(5000);
         return activityService.findActivitys();
     }
     }

客户端
首先要在<body>标签最下方引入jquery.min.js文件,因为要使用jQuery

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div>
    <h1>The Activity page01</h1>
    
    <table class="table">
            <thead>
                <tr>
                    <th>id</th>
                    <th>title</th>
                    <th>category</th>
                    <th>start time</th>
                    <th>end time</th>
                    <th>state</th>
                    <th>createdTime</th>
                </tr>
            </thead>
            <tbody id="tbodyId">
                <tr>
                    <td colspan="7">数据正在积极的加载中......</td>
                </tr>
            </tbody>
        </table>
        </div>
     <script type="text/javascript" data-original="/jquery/jquery.min.js"></script>
        <script type="text/javascript">
        //首先是函数的自调用
        (function(){
            //1首先定义url
            let url="/activity/doFindActivitys";
            //2定义参数,这里为空
            let params={};
            //3发送异步请求,这里是采用的是$.ajax(借助jquery中的ajax函数,向服务端发送异步请求获取活动信息)
            $.ajax({
                url:url,//url
                data:params,//参数
                dataType:"json",//字符串格式
                success:function(result){
                    doHandleQueryResult(result);//要将取到的result结果呈现到页面上
                }
                
            });
        })()
        function doHandleQueryResult(result){
             //处理服务端返回的活动信息,迭代result,并将result内容填充tbody位置
             //1.获取tbody对象
            var tBody=$("#tbodyId");
             
            tBody.empty();//并清空原有内容
            //2.迭代result,将活动信息追加到tbody中   
            
            result.forEach(item=>{//这里的item为一个变量,代表数组中某一个元素
                //为tBody中追加新的内容
                tBody.append(
                    `<tr>
                    <td>${item.id}</td>
                    <td>${item.title}</td>
                    <td>${item.category}</td>
                    <td>${item.startTime}</td>
                    <td>${item.endTime}</td>
                    <td>${item.state==1?'有效':'已结束'}</td>
                    <td>${item.createdTime}</td>
                    </tr>`    
                        
            );
                
                
            });
            
        }
        
        </script>
</body>
</html>
查看原文

赞 0 收藏 0 评论 0

随风 赞了文章 · 9月10日

Spring Boot 应用加强实现--健康检查--热部署--Lombok插件应用

一、健康检查配置及测试

1、直接在项目中添加依赖

image

2、健康检查分析:

在浏览器中输入如下地址:
http://localhost/actuator/health

假如希望查看更多actuator选项,可以在spring boot中配置文件
application.properties中添加如下语句:(生产环境不加)

management.endpoints.web.exposure.include=*

此时在浏览器地址栏可以输入.http://localhost/actuator/beans 查看所有的
spring 容器中的bean信息。

说明:当需要以一种更好的结构化方式查看bean相关信息,可以对Google浏览器安装jsonView插件,还可以直接在sts工具的Boot Dashboard中选中项目,查看其属性(show properties),这种方式重点掌握。

二、热部署配置及实现

基于SpringBoot的Web项目,修改了某个类以后,默认不会自动重新部署和加载,需要我们手动重启服务器。假如我们希望项目可以自动部署,可以添加如下依赖,进行热部署实现。
image
说明:当我们修改了src/main/java目录下的java文件或修改了src/main/resources目录下的配置文件时,默认都会重启你的web服务器,但是修改了测试类或html文件不会自动重启和部署。

三、Lombok插件应用

Lombok是一个第三库,可以基于lombok中提供的API,在程序编译时自动织入一些方法。这样做的话可以简化我们对某些方法的编写,例如,省略pojo类中的set方法,get方法,toString等方法的编写。我们写的java程序可以在编译时,通过lombok自动将这些方法添加到.class文件中,其应用步骤如下:
第一步:添加依赖。
image
第二步:找到下载的lombok-xxx.jar依赖所在的位置。
第三步:启动操作系统命令行客户端,并切换到lombok-xxx.jar文件所在的目录,然后,利用java –jar 指令启动lombok插件,如图所示:
image
回车执行如上命令,会弹出选择安装对话框,如图所示:
image
然后在弹出的框中选择sts启动执行文件(SpringToolSuite4.exe)进行安装,
安装成功以后会出现如下弹出框,如图所示:
image
安装成功以后可在STS工具的根目录看到一个lombok.jar文件,同时会在SpringToolSuite4.ini文件的尾部添加lombok.jar文件所在的具体路径,例如
-javaagent:E:softwarests-4.5.3.RELEASElombok.jar。
其中lombok.jar会默认创建在sts软件的根目录,如图所示:
image
说明:安装成功以后重启sts工具.(假如安装成功以后再次启动sts,此时无法启动,可能与STS所在目录有关系,可以将安装目录下的lombok.jar文件拷出到特定目录,然后修改SpringToolSuite4.ini文件的最后一行的lombok.jar路径即可,同时在配置文件中也不能有空格)。

Lombok 常用注解分析:

@Setter 用于为描述的类生成setter方法,不包含final修饰属性。
@Getter 用于为描述的类生成getter方法。
@ToString 用于为描述的类添加toString方法。
@EqualsAndHashCode 用于为描述的类,生成hashCode和equals方法。
@NoArgsConstructor 用于为描述的类生成无参的构造方法。
@AllArgsConstructor 用于为描述的类生成包含类中所有字段的构造方法。
@Data用于为描述的类生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Slf4J 用于为描述的类添加一个日志属性对象
private static final Logger log
=LoggerFactory.getLogger(Xxx.class);

查看原文

赞 1 收藏 0 评论 0

随风 发布了文章 · 9月10日

Ajax技术在Jquery中的应用

jQuery中的常用的ajax函数

ajax(...)
get(...)
post(...)
getJSON(...)

jQuery中常用的Ajax函数应用案例

首先在客户端,新构建一个页面,在<body>中引入jquery-min.js文件

<fieldset>
    <legend>Jquery Ajax function</legend>
    <button onclick="doGet()">$.get(..)</button>
    <button onclick="doPost()">$.post(..)</button>
    <button onclick="doAjax()">$.ajax(..)</button>
    <button onclick="doLoad()">$.load(..)</button>
    <span id="resultId"></span>
    </fieldset>
    <script type="text/javascript" data-original="/js/jquery.min.js"></script>
    

1.get函数应用

客户端

function doGet(){
 1.请求url
 let url ="/jquery/doAjaxGet";
 2.请求参数
 let params="msg= hello everyone jiayouya"
 3发送异步请求
 $.get(url,params,(rusult)=>{
    $("#resultId").html(result);
 });
}

服务端

@Controller
@RequestMapping("/jquery")
public class JQueryController {
    @RequestMapping("/doAjaxGet")
    @ResponseBody
    public String doAjaxGet(String msg) {
        //将客户端传到服务端的字符串转换为大写,然后以字符串的形式响应给客户端
        return "Jquery get request result" + msg.toUpperCase();
        
    }
    }

2.post函数应用

客户端

function doPost(){
 1.请求url
 let url ="/jquery/doAjaxPost";
 2.请求参数
 let params="title=AAA&&id=10"
 3发送异步请求
 $.post(url,params,(rusult)=>{
    $("#resultId").html(result);
 });
}

服务端



    @RequestMapping("/doAjaxPost")
    @ResponseBody
    public String doAjaxGet(String title,Integer id) {
        
        return "Jquery get request result" +title+"/"+id ;
        
    }

3.ajax函数应用

客户端

function ajax(){
        //1 请求url
        let url ="/jquery/doAjaxPost";
        //2请求参数
        let params="id=10&&title=AAA&&age=23";
        //3发送异步请求
        $.ajax({
        url:url,
        data:params,
        dataType:"json",
        async:true,
        success:(result)=>{
            $("#resultId").html(result);
        },
        error:function(xhr){
                console.log(xhr.statusText);
                $("#resultId").html(xhr.statusText)
        }
        })
}

4 load函数应用

客户端

function doLoad(){
//1 请求url
        let url ="/jquery/doAjaxGet";
        //2请求参数
        let params="msg= hello cgb teacher ";
        //3发送异步
        $("#resultId").load(url,params);
        }

image

查看原文

赞 0 收藏 0 评论 0

随风 发布了文章 · 9月10日

JS获取元素

1 document.getElementById

document是一个js对象,用于表示当前html网页,当浏览器加载完整个html网页后,会用document对象表示整个html网页

document.getElementById(id值)---通过元素的id值,获取一个元素,返回的是表示该元素的js对象

document.getElementsByTagName( 元素名 ) -- 通过元素名称获取当前文档中的所有指定名称的元素,返回的是一个数组,其中包含了所有指定名称的元素。

document.body -- 获取当前文档中的body元素

element.parentNode -- 获取当前元素的父元素。element表示当前元素

document.getElementById("id1").innerHTML获取id为id1所指代表元素的内容

<div id="div1">
    这是一个div元素...
    <span>这是一个span元素</span>
</div>
//获取div元素
var oDiv1 = document.getElementById("div1");
oDiv1.innerHTML;//获取div元素的内容

jQuery

JS获取id为div1的元素: document.getElementById("div1")
jquery获取id为div1的元素:$("#div1")

文档就绪事件函数

1 将获取元素的代码放在文档就绪事件函数中,文档就绪事件函数会在浏览器加载完所有的html元素后(也就是加载完最后一个html元素时)立即执行。由于当前网页中的所有元素都被加载了,h1元素肯定也被加载了,此时再获取h1元素就一定能获取到。

<head>
<meta charset="UTF-8">
<!-- 在使用jQuery之前,必须先引入jQuery的函数库文件 -->
<script data-original="js/jquery-1.8.3.js"></script>
<script>
    $(function(){
        //1.获取id为demo的h1元素
        var h1 = document.getElementById( "demo" );
        //2.获取h1元素中的内容( innerHTML )
        alert( h1.innerHTML );
    });
</script>
</head>
<body>
    <h1 id="demo">jQuery的引入示例...</h1>
</body>

2将获取元素的代码放在一个自定义的函数中,并将该函数绑定在h1元素的点击事件上,当我们点击h1元素时会执行自定义的函数,函数执行时才获取h1元素,此时就能够获取到h1元素。

<head>
<meta charset="UTF-8">
<!-- 在使用jQuery之前,必须先引入jQuery的函数库文件 -->
<script data-original="js/jquery-1.8.3.js"></script>
<script>
    function fn1(){
        //1.获取id为demo的h1元素
        var h1 = document.getElementById( "demo" );
        //2.获取h1元素中的内容( innerHTML )
        alert( h1.innerHTML );
    }
</script>
</head>
<body>
    <h1 id="demo" onclick="fn1()">jQuery的引入示例...</h1>
</body>

什么时候该使用文档就绪事件函数

1可以将获取元素的代码放在文档就绪事件函数中,等浏览器加载完整个网页后,文档就绪事件函数才会执行,此时所有的元素都被加载了,再获取任何元素都能获取到!
*jQuery提供的文档就绪事件函数(简写形式):

<script>
    $(function(){
        //在浏览器加载完整个html网页后立即执行
    });
</script>
其完整写法为:
<script>
    $(document).ready(function(){
        //在浏览器加载完整个html网页后立即执行
    });
</script>
  • js提供的文档就绪事件函数

*

<script>
    window.onload = function(){
        //在浏览器加载完整个html网页后立即执行
    }
</script>

JQuery中的选择器

1基本选择器

(1)元素名选择器
$("div") -- 选中所有的div元素
$("span") -- 选中所有的span元素

(2)class/类选择器
$(".s1") -- 选中所有class值为s1的元素(class值为s1的元素可能是任何元素)
$("span.s1") -- 选中所有class值为s1的span元素

(3)id选择器
$("#one") -- 选中id为one的元素

(4)多元素选择器
$("div,span,.s1,#one") -- 选中所有的div元素,以及所有的span元素,以及所有class值为s1的元素,以及id为one的元素

2层级选择器

$("div span") -- 选中所有div内部的所有span元素
$("#one span") -- 选中id为one的元素内部的所有span元素

$("#two+span") -- 选中id为two的元素后面紧邻的span兄弟元素
$("#two").next("span") -- 选中id为two的元素后面紧邻的span兄弟元素
$("#two").prev("span") -- 选中id为two的元素前面紧邻的span兄弟元素

$("#two~span") -- 选中id为two的元素后面所有的span兄弟元素
$("#two").nextAll("span") -- 选中id为two的元素后面所有的span兄弟元素
$("#two").prevAll("span") -- 选中id为two的元素前面所有的span兄弟元素

$("#two").siblings("span") -- 选中id为two的元素前、后所有的span兄弟元素

3基本选择过滤器

(1) 选中第一个div元素
$("div:first")
$("div:eq(0)")
$("div").eq(0)

(2) 选中最后一个div元素
$("div:last")
$("div:eq(-1)")
$("div").eq(-1)

(3) 选中第n+1个div元素(n从零开始)
$("div:eq(n)")
$("div").eq(n)
查看原文

赞 0 收藏 0 评论 0

随风 发布了文章 · 9月8日

时间控件bootstrap-datetimepicker的应用

1导入文件

image

2<head中引入>

image

3<body中引入>

image

 $('.form_datetime').datetimepicker({
     language:'zh-CN',
    format:'yyyy/mm/dd hh:ii',
    autoclose:true
  })

4<修改使用位置>

在class中加入 form_datetime
image

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 5 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 8月25日
个人主页被 223 人浏览