zoukankan      html  css  js  c++  java
  • Halo(六)

    Spring publish-event 机制

    监听者模式包含了一个监听者 Listener 与之对应的事件 Event,还有一个事件发布者 EventPublish。
    过程就是 EventPublish 发布一个事件,被监听者捕获到,然后执行事件相应的方法。
    
    1. 发布事件
    
    	private final ApplicationEventPublisher eventPublisher;
    
    	eventPublisher.publishEvent(new LogEvent(this, name, 0, ld));
    
    
    2. 事件
    
    public class LogEvent extends ApplicationEvent {
    
        private final LogParam logParam;
    
        public LogEvent(Object source, LogParam logParam) {
        	//事件源(发布事件的对象)
            super(source);
    
            this.logParam = logParam;
        }
    
        public LogEvent(Object source, String logKey, LogType logType, String content) {
            this(source, new LogParam(logKey, logType, content));
        }
    
        public LogParam getLogParam() {
            return logParam;
        }
    }
    
    
    3. 监听器
    
    @Component
    public class LogEventListener {
    
        private final LogService logService;
    
        public LogEventListener(LogService logService) {
            this.logService = logService;
        }
    
        //将方法标记为应用程序事件侦听器
        @EventListener
        //异步
        @Async
        public void onApplicationEvent(LogEvent event) {
            //转换成 Entity
            Log logToCreate = event.getLogParam().convertTo();
            //保存日志
            logService.create(logToCreate);
        }
    }
    

    缓存模块

    自定义注解并使用

    /**
     * 缓存锁注解(在具有该注解的方法上:加锁,执行目标方法,释放锁)
     */
    @Target(ElementType.METHOD)	//用于描述注解的使用范围
    @Retention(RetentionPolicy.RUNTIME) //用于描述注解的生命周期
    @Documented //表示该注解是否可以生成到 API 文档中
    @Inherited  //具备继承性(A被注解了,那么继承了A的B够继承到A中的注解)
    public @interface CacheLock {
    
        /** 缓存锁key前缀(默认"")*/
        @AliasFor("value")
        String prefix() default "";
    
        /** 缓存锁key前缀(默认"")*/
        @AliasFor("prefix")
        String value() default "";
    
        /** 过期时间 */
        long expired() default 5;
    
        /** 时间单位(默认 s)*/
        TimeUnit timeUnit() default TimeUnit.SECONDS;
    
        /** 分隔符(默认 :)*/
        String delimiter() default ":";
    
        /** 方法调用后是否删除缓存 */
        boolean autoDelete() default true;
    
        /** 是否跟踪请求信息(将请求IP添加到缓存key上)*/
        boolean traceRequest() default false;
    }
    
    
    /**
     * 缓存参数注释
     */
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface CacheParam {
    }
    
    
    Controller:
    
        @PostMapping("/login")
        @CacheLock(autoDelete = false)    //登陆操作加锁,防止重复登陆
        public AuthToken auth(@RequestBody @Valid LoginParam loginParam) {
            return adminService.authenticate(loginParam);
        }
    

    用于缓存锁注解的拦截器(AOP代理模式)

    @Aspect
    @Configuration
    public class CacheLockInterceptor {
    
        //缓存锁key前缀:cache_lock_
        private final static String CACHE_LOCK_PREFOX = "cache_lock_";
    
        //缓存锁value:locked(被锁定状态)
        private final static String CACHE_LOCK_VALUE = "locked";
    
    	//缓存池
        private final StringCacheStore cacheStore;
    
        public CacheLockInterceptor(StringCacheStore cacheStore) {
            this.cacheStore = cacheStore;
        }
    
        //具有 @annotation(run.halo.app.cache.lock.CacheLock) 注解的方法都会触发拦截器
        @Around("@annotation(run.halo.app.cache.lock.CacheLock)")
        public Object interceptCacheLock(ProceedingJoinPoint joinPoint) throws Throwable {
            //获取方法签名
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    
            //获取注解类
            CacheLock cacheLock = methodSignature.getMethod().getAnnotation(CacheLock.class);
    
            //建立缓存锁key
            String cacheLockKey = buildCacheLockKey(cacheLock, joinPoint);
    
            try {
                //放入缓存(key是cacheLockKey,value是locked)
                //如果缓存中没有此缓存锁key才会放入(返回true),否则会放入失败(返回false)
                Boolean cacheResult = cacheStore.putIfAbsent(cacheLockKey, CACHE_LOCK_VALUE, cacheLock.expired(), cacheLock.timeUnit());
    
                if (!cacheResult) {
                    throw new FrequentAccessException("访问过于频繁,请稍后再试!").setErrorData(cacheLockKey);
                }
    
                //处理被注解的方法,获取返回值
                return joinPoint.proceed();
            } finally {
                if (cacheLock.autoDelete()) {
                    //注解标注调用方法后删除缓存
                    cacheStore.delete(cacheLockKey);
                }
            }
        }
    
        /**
         * 建立缓存锁key(包括:cache_lock_cacheLock.prefix/Method:arg:IP)
         */
        private String buildCacheLockKey(CacheLock cacheLock,ProceedingJoinPoint joinPoint) {
    
            //获取方法
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    
            //建立缓存锁key
            StringBuilder cacheKeyBuilder = new StringBuilder(CACHE_LOCK_PREFOX);
    
            //分隔符(:)
            String delimiter = cacheLock.delimiter();
    
            if (StringUtils.isNotBlank(cacheLock.prefix())) {
                cacheKeyBuilder.append(cacheLock.prefix());
            } else {
                cacheKeyBuilder.append(methodSignature.getMethod().toString());
            }
    
            //获取方法上的参数注解数组(一个方法可以有多个参数,一个参数可以有多个注解)
            Annotation[][] parameterAnnotations = methodSignature.getMethod().getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                for (int j = 0; j < parameterAnnotations[i].length; j++) {
                    //获取注解
                    Annotation annotation = parameterAnnotations[i][j];
                    //如果被 @CacheParam 注解,则获取参数,添加到缓存锁key上
                    if (annotation instanceof CacheParam) {
                        //获取参数
                        Object arg = joinPoint.getArgs()[i];
                        //将参数添加到缓存key上
                        cacheKeyBuilder.append(delimiter).append(arg.toString());
                    }
                }
            }
    
            if (cacheLock.traceRequest()) {
                //添加 Request 客户端 IP
                cacheKeyBuilder.append(delimiter).append(ServletUtils.getRequestIp());
            }
            return cacheKeyBuilder.toString();
        }
    }
    

    缓存包装器(将缓存数据封装成对象)

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class CacheWrapper<V> implements Serializable {
    
        /** 缓存数据 */
        private V data;
    
        /** 过期时间 */
        private Date expireAt;
    
        /** 创造时间 */
        private Date createAt;
    }
    

    缓存池接口

    public interface CacheStore<K, V> {
    
        /** 通过key得到缓存 */
        Optional<V> get(@NonNull K key);
    
        /** 放入一个会过期的缓存 */
        void put(@NonNull K key, @NonNull V value, long timeout, @NonNull TimeUnit timeUnit);
    
        /** 缓存池中不存在key才放入 */
        Boolean putIfAbsent(@NonNull K key, @NonNull V value, long timeout, @NonNull TimeUnit timeUnit);
    
        /** 放入一个不会过期的缓存 */
        void put(@NonNull K key, @NonNull V value);
    
        /** 删除缓存 */
        void delete(@NonNull K key);
    }
    

    缓存池抽象类

    public abstract class AbstractCacheStore<K, V> implements CacheStore<K, V> {
    
        /** 通过key获取缓存包装器 */
        abstract Optional<CacheWrapper<V>> getInternal(@NonNull K key);
    
        /** 放入缓存包装器 */
        abstract void putInternal(@NonNull K key, @NonNull CacheWrapper<V> cacheWrapper);
    
        /** 如果key不存在才放入缓存包装器 */
        abstract Boolean putInternalIfAbsent(@NonNull K key, @NonNull CacheWrapper<V> cacheWrapper);
    
        /** 通过key获取value */
        @Override
        public Optional<V> get(K key) {
            return getInternal(key).map(cacheWrapper -> {
                //判断是否过期(过期时间不是null,并且在当前时间之前,表示已经过期)
                if (cacheWrapper.getExpireAt() != null && cacheWrapper.getExpireAt().before(run.halo.app.utils.DateUtils.now())) {
                    //删除缓存
                    delete(key);
                    return null;
                }
                return cacheWrapper.getData();
            });
        }
    
        /** 放入带过期时间的缓存 */
        @Override
        public void put(K key, V value, long timeout, TimeUnit timeUnit) {
            putInternal(key, buildCacheWrapper(value, timeout, timeUnit));
        }
    
        /** key不存在才放入缓存 */
        @Override
        public Boolean putIfAbsent(K key, V value, long timeout, TimeUnit timeUnit) {
            return putInternalIfAbsent(key, buildCacheWrapper(value, timeout, timeUnit));
        }
    
        /** 放入不过期的缓存 */
        @Override
        public void put(K key, V value) {
            putInternal(key, buildCacheWrapper(value, 0, null));
        }
    
        /** 构建缓存包装器 */
        @NonNull
        private CacheWrapper<V> buildCacheWrapper(@NonNull V value, long timeout, @Nullable TimeUnit timeUnit) {
            //过期时间必须>=0
            Assert.isTrue(timeout >= 0, "Cache expiration timeout must not be less than 0");
    
            Date now = run.halo.app.utils.DateUtils.now();
    
            Date expireAt = null;
    
            //如果设置了过期时间,则构造过期时间
            if (timeout > 0 && timeUnit != null) {
                expireAt = DateUtils.add(now, timeout, timeUnit);
            }
    
            //构建缓存包装器
            CacheWrapper<V> cacheWrapper = new CacheWrapper<>();
            cacheWrapper.setCreateAt(now);
            cacheWrapper.setExpireAt(expireAt);
            cacheWrapper.setData(value);
    
            return cacheWrapper;
        }
    }
    

    字符串缓存池抽象类(将缓存 Data 数据转换成 Json 字符串)

    public abstract class StringCacheStore extends AbstractCacheStore<String, String> {
    
        /** 放入值 */
        public <T> void putAny(String key, T value) {
            try {
            	//JsonUtils.objectToJson(value):对象转成Json
                put(key, JsonUtils.objectToJson(value));
            } catch (JsonProcessingException e) {
                throw new ServiceException("Failed to convert " + value + " to json", e);
            }
        }
    
        public <T> void putAny(@NonNull String key, @NonNull T value, long timeout, @NonNull TimeUnit timeUnit) {
            try {
                put(key, JsonUtils.objectToJson(value), timeout, timeUnit);
            } catch (JsonProcessingException e) {
                throw new ServiceException("Failed to convert " + value + " to json", e);
            }
        }
    
        /** 获取值 */
        public <T> Optional<T> getAny(String key, Class<T> type) {
            return get(key).map(value -> {
                try {
                	//JsonUtils.jsonToObject(value, type):Json转换成对象
                    return JsonUtils.jsonToObject(value, type);
                } catch (IOException e) {
                    return null;
                }
            });
        }
    }
    

    内存缓存池(字符串缓存池)

    public class InMemoryCacheStore extends StringCacheStore {
    
        /** 清理计划周期 */
        private final static long PERIOD = 60 * 1000;	//一分钟
    
        /** 缓存容器 */
        private final static ConcurrentHashMap<String, CacheWrapper<String>> CACHE_CONTAINER = new ConcurrentHashMap<>();
    
        //定时任务
        private final Timer timer;
    
        public InMemoryCacheStore() {
            //定时清理缓存(1分钟)
            timer = new Timer();
            //开启定时任务,延迟0,周期一分钟
            timer.scheduleAtFixedRate(new CacheExpiryCleaner(), 0, PERIOD);
        }
    
        /** 获取值 */
        @Override
        Optional<CacheWrapper<String>> getInternal(String key) {
            //获取value,并放入允许null的Optional对象中
            return Optional.ofNullable(CACHE_CONTAINER.get(key));
        }
    
        /** 放入值 */
        @Override
        void putInternal(String key, CacheWrapper<String> cacheWrapper) {
            //返回原始值
            CacheWrapper<String> oldCacheWrapper = CACHE_CONTAINER.put(key, cacheWrapper);
        }
    
        /** 不存在才放入值 */
        @Override
        Boolean putInternalIfAbsent(String key, CacheWrapper<String> cacheWrapper) {
            CacheWrapper<String> stringCacheWrapper = CACHE_CONTAINER.putIfAbsent(key, cacheWrapper);
            if(stringCacheWrapper==null)return true;
            return false;
        }
    
        /** 删除缓存 */
        @Override
        public void delete(String key) {
            CACHE_CONTAINER.remove(key);
        }
    
        /** 清空缓存前取消所有定时任务 */
        @PreDestroy    //销毁Bean之前的操作
        public void preDestroy() {
            //取消所有定时任务
            timer.cancel();
        }
    
        /** 缓存超时清理任务 */
        private class CacheExpiryCleaner extends TimerTask {
            //get()中会判断缓存是否过期,过期会删除缓存,并返回null
            @Override
            public void run() {
                CACHE_CONTAINER.keySet().forEach(key -> {
                	//类名.this表示类名所代表类的对象
                    if (!InMemoryCacheStore.this.get(key).isPresent()) {
                        log.info("删除的过期缓存key:[{}]", key);
                    }
                });
            }
        }
    }
    
  • 相关阅读:
    oracle的row_number()和rownum
    linux视频学习6(mysql的安装/)
    linux视频学习5(top, 网络监控
    linux视频学习4(crontab和进程)
    linux的视频学习4(网络配置和rpm)
    linux视频学习3(shell和网络)
    linux视频学习3(linux安装,shell,tcp/ip协议,网络配置)
    linux(视频学习)2
    linux视频学习(简单介绍)20160405
    java代码用dom4j解析xml文件的简单操作
  • 原文地址:https://www.cnblogs.com/loveer/p/11922142.html
Copyright © 2011-2022 走看看