Chapter 1 Requirements Analysis
It is planned to add Redis to implement cache processing in Team's open source project. Because the business functions have been implemented partly, by writing Redis tool classes and then referring to them, the amount of changes is large, and decoupling cannot be achieved, so I thought of the AOP (sectional-oriented programming) of the Spring framework.
Open source project: https://github.com/u014427391/jeepatform
Chapter 2 Introduction to SpringBoot
As an important open source framework in the field of JavaEE framework, Spring framework plays an important role in enterprise application development. At the same time, Spring framework and its subframes are a lot, so the amount of knowledge is very wide.
SpringBoot: A subframe of Spring framework, also called microframework, is a framework launched in 2014 that makes Spring framework development easy. After learning all the knowledge of Spring frameworks, Spring frameworks inevitably require a lot of XML. If you use SpringBoot frameworks, you can use annotation development to greatly simplify the development based on Spring frameworks. SpringBoot makes full use of JavaConfig's configuration mode and the concept of "convention is better than configuration", which can greatly simplify the development of Web applications and REST services based on SpringMVC.
Chapter 3 Introduction to Redis
3.1 Redis installation and deployment (Linux)
For Redis installation and deployment, please refer to my blog (Redis is written based on C, so install the gcc compiler before installation): //www.VeVB.COM/article/79096.htm
3.2 Introduction to Redis
Redis has now become one of the most popular in-memory databases in the web development community. With the rapid development of Web2.0 and the increase in the proportion of semi-structured data, websites have more and more demands for efficient performance.
Moreover, large websites generally have hundreds or more Redis servers. As a powerful system, Redis has its own use, whether it is storage, queue or cache system.
For getting started with SpringBoot framework, please refer to the previous article: http://www.VeVB.COM/article/111197.htm
Chapter 4 Redis cache implementation
4.1 The following structure diagram
Project structure diagram:
4.2 SpringBoot's yml file configuration
Add the application.yml configuration below resource, where mysql, druid, and redis are mainly configured
spring: datasource: # main data source shop: url: jdbc:mysql://127.0.0.1:3306/jeepatform?autoReconnect=true&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # Connection pool settings druid: initial-size: 5 min-idle: 5 max-active: 20 # Configure the time to get the connection waiting timeout max-wait: 60000 # Configure how long it takes to perform a detection interval to detect idle connections that need to be closed, in millisecond time-between-eviction-runs-millis: 60000 # Configure the minimum time to survive in the pool, in milliseconds min-evictable-idle-time-millis: 300000 # Oracle Please use select 1 from dual validation-query: SELECT 'x' test-while-idle: true test-on-borrow: false test-on-return: false # Open PSCache and specify the size of PSCache on each connection pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 # Configure filters for monitoring statistics intercepting. After removing the monitoring interface SQL cannot be counted, 'wall' is used for firewall filters: stat,wall,slf4j # Open mergeSql function through the connectProperties property; slow SQL records connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # Merge monitoring data of multiple DruidDataSource use-global-data-source-stat: true jpa: database: mysql hibernate: show_sql: true format_sql: true ddl-auto: none naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl mvc: view: prefix: /WEB-INF/jsp/ suffix: .jsp #Jedis configuration jedis: pool: host: 127.0.0.1 port: 6379 password: password timeout: 0 config: maxTotal: 100 maxIdle: 10 maxWaitMillis: 100000
Write a configuration class to start configuration JedisConfig.java:
package org.muses.jeepatform.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;@Configuration//@ConfigurationProperties(prefix = JedisConfig.JEDIS_PREFIX )public class JedisConfig { //public static final String JEDIS_PREFIX = "jedis"; @Bean(name= "jedisPool") @Autowired public JedisPool jedisPool(@Qualifier("jedisPoolConfig") JedisPoolConfig config, @Value("${spring.jedis.pool.host}")String host, @Value("${spring.jedis.pool.port}")int port, @Value("${spring.jedis.pool.timeout}")int timeout, @Value("${spring.jedis.pool.password}")String password) { return new JedisPool(config, host, port, timeout,password); } @Bean(name= "jedisPoolConfig") public JedisPoolConfig jedisPoolConfig (@Value("${spring.jedis.pool.config.maxTotal}")int maxTotal, @Value("${spring.jedis.pool.config.maxIdle}")int maxIdle, @Value("${spring.jedis.pool.config.maxWaitMillis}")int maxWaitMillis) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(maxTotal); config.setMaxIdle(maxIdle); config.setMaxWaitMillis(maxWaitMillis); return config; }}4.3 Meta-annotation class writing
Write a meta annotation class RedisCache.java. All classes defined by the modified annotation are automatically implemented in AOP cache processing.
package org.muses.jeepatform.annotation;import org.muses.jeepatform.common.RedisCacheNamespace;import java.lang.annotation.*;/** * Meta annotation is used to identify the method used to query the database*/@Documented@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface RedisCache {// RedisCacheNamespace nameSpace();} In addition to Retention, there are three other annotations provided by JDK 5, namely Target, Inherited and Documented. Based on this, we can implement custom meta annotations
We set RedisCache to reference based on Method method level.
1.RetentionPolicy.SOURCE This type of Annotations is only reserved at the source code level and will be ignored during compilation.
2. RetentionPolicy.CLASS This type of Annotations is retained during compilation and exists in the class file, but the JVM will ignore it.
3. RetentionPolicy.RUNTIME This type of Annotations will be reserved by the JVM, so they can be read and used by the JVM or other code that uses reflection mechanisms at runtime.
4.4 Calling JedisPool to implement Redis cache processing
package org.muses.jeepatform.cache;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import javax.annotation.Resource;@Component("redisCache")public class RedisCache { @Autowired private JedisPool jedisPool; private JedisPool getJedisPool(){ return jedisPool; } public void setJedisPool(JedisPool jedisPool){ this.jedisPool = jedisPool; } /** * Get data from Redis cache* @param redisKey * @return */ public Object getDataFromRedis(String redisKey){ Jedis jedis = jedisPool.getResource(); byte[] byteArray = jedis.get(redisKey.getBytes()); if(byteArray != null){ return SerializeUtil.unSerialize(byteArray); } return null; } /** * Save data to Redis * @param redisKey */ public String saveDataToRedis(String redisKey,Object obj){ byte[] bytes = SerializeUtil.serialize(obj); Jedis jedis = jedisPool.getResource(); String code = jedis.set(redisKey.getBytes(), bytes); return code; }}Object serialization tool class:
package org.muses.jeepatform.cache;import java.io.*;public class SerializeUtil { /** * Serialized object* @param obj * @return */ public static byte[] serialize(Object obj){ ObjectOutputStream oos = null; ByteArrayOutputStream baos = null; try{ baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(obj); byte[] byteArray = baos.toByteArray(); return byteArray; } catch(IOException e){ e.printStackTrace(); } return null; } /** * Deserialize object* @param byteArray * @return */ public static Object unSerialize(byte[] byteArray){ ByteArrayInputStream bais = null; try { //Deserialize to object bais = new ByteArrayInputStream(byteArray); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; } } Here I remember that Vo classes must implement Serializable
For example, menu information VO class, this is a JPA-mapping entity class
package org.muses.jeepatform.core.entity.admin;import javax.persistence.*;import java.io.Serializable;import java.util.List;/** * @description Menu information entity* @author Nicky * @date March 17, 2017*/@Table(name="sys_menu")@Entitypublic class Menu implements Serializable { /** Menu Id**/ private int menuId; /** Superior Id**/ private int parentId; /** Menu name**/ private String menuName; /** Menu icon**/ private String menuIcon; /** Menu URL**/ private String menuUrl; /** Menu type**/ private String menuType; /** Menu sort**/ private String menuOrder; /** Menu status**/ private String menuStatus; private List<Menu> subMenu; private String target; private boolean hasSubMenu = false; public Menu() { super(); } @Id @GeneratedValue(strategy=GenerationType.IDENTITY) public int getMenuId() { return this.menuId; } public void setMenuId(int menuId) { this.menuId = menuId; } @Column(length=100) public int getParentId() { return parentId; } public void setParentId(int parentId) { this.parentId = parentId; } @Column(length=100) public String getMenuName() { return this.menuName; } public void setMenuName(String menuName) { this.menuName = menuName; } @Column(length=30) public String getMenuIcon() { return this.menuIcon; } public void setMenuIcon(String menuIcon) { this.menuIcon = menuIcon; } @Column(length=100) public String getMenuUrl() { return this.menuUrl; } public void setMenuUrl(String menuUrl) { this.menuUrl = menuUrl; } @Column(length=100) public String getMenuType() { return this.menuType; } public void setMenuType(String menuType) { this.menuType = menuType; } @Column(length=10) public String getMenuOrder() { return menuOrder; } public void setMenuOrder(String menuOrder) { this.menuOrder = menuOrder; } @Column(length=10) public String getMenuStatus(){ return menuStatus; } public void setMenuStatus(String menuStatus){ this.menuStatus = menuStatus; } @Transient public List<Menu> getSubMenu() { return subMenu; } public void setSubMenu(List<Menu> subMenu) { this.subMenu = subMenu; } public void setTarget(String target){ this.target = target; } @Transient public String getTarget(){ return target; } public void setHasSubMenu(boolean hasSubMenu){ this.hasSubMenu = hasSubMenu; } @Transient public boolean getHasSubMenu(){ return hasSubMenu; }}4.5 Spring AOP implements the method cache that monitors all annotated by @RedisCache
First get the cache from Redis. If you cannot query it, query the MySQL database, and then save it to the Redis cache. Next time you query, call the Redis cache directly.
package org.muses.jeepatform.cache;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;/** * AOP implements Redis cache processing*/@Component@Aspectpublic class RedisAspect { private static final Logger LOGGER = LoggerFactory.getLogger(RedisAspect.class); @Autowired @Qualifier("redisCache") private RedisCache redisCache; /** * Methods to intercept all meta annotations RedisCache annotations*/ @Pointcut("@annotation(org.muses.jeepatform.annotation.RedisCache)") public void pointcutMethod(){ } /** * For surround processing, first get the cache from Redis. If you cannot query, you query the MySQL database, * Then save it to the Redis cache* @param joinPoint * @return */ @Around("pointcutMethod()") public Object around(ProceedingJoinPoint joinPoint){ //Previous: Get the cache from Redis//First get the target method parameter long startTime = System.currentTimeMillis(); String applId = null; Object[] args = joinPoint.getArgs(); if (args != null && args.length > 0) { applId = String.valueOf(args[0]); } //Get the class where the target method is located String target target = joinPoint.getTarget().toString className = target.split("@")[0]; //Get the method name of the target method String methodName = joinPoint.getSignature().getName(); //Redis key format: applId: method name String redisKey = applId + ":" + className + "." + methodName; Object obj = redisCache.getDataFromRedis(redisKey); if(obj!=null){ LOGGER.info("************Data found from Redis****************"); LOGGER.info("Redis KEY value:"+redisKey); LOGGER.info("REDIS VALUE value:"+obj.toString()); return obj; } long endTime = System.currentTimeMillis(); LOGGER.info("Redis cache AOP processing time:"+(endTime-startTime)); LOGGER.info("***************No data found from Redis****************"); try{ obj = joinPoint.proceed(); }catch(Throwable e){ e.printStackTrace(); } LOGGER.info("*************** Start querying data from MySQL*************"); //Post-set: Save the data found in the database to Redis String code = redisCache.saveDataToRedis(redisKey,obj); if(code.equals("OK")){ LOGGER.info("************Data was successfully saved to Redis cache!!!************"); LOGGER.info("Redis KEY value:"+redisKey); LOGGER.info("REDIS VALUE value:"+obj.toString()); } return obj; }}Then call @RedisCache to implement cache
/** * Get menu information through menu Id* @param id * @return */ @Transactional @RedisCache public Menu findMenuById(@RedisCacheKey int id){ return menuRepository.findMenuByMenuId(id); }The methods that log in to the system and then add the @RedisCache annotation will implement Redis cache processing
You can see that Redis is saved to cache
Project code: https://github.com/u014427391/jeepatform
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.