BolunWu

BolunWu 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

BolunWu 发布了文章 · 2020-12-15

表单工作流创建

1.表单创建

1.1登录表单:http://10.254.56.58:7001/Liems/ToolsLogin.jsp
1.2选择菜单创建表单(画一步保存一步)

image.png

1.3 发布表单登录系统查看样式.

2.系统操作

2.1在生产运行菜单中找到发布的表单的位置

image.png

2.2到系统管理菜单中添加工作流

image.png

2.3选择应用类型维护添加行,将创建的表单程序号和表单名称添加进去保存

image.png

2.4进入工作流管理,选择表单所属的公司接着选择所属公司里的分组.

image.png

2.5右键新建流程

image.png

代码会自动生成,名称填写表单名称,描述与名称一致.
2.6应用类型选择,选择新添加的应用类型(表单程序号搜索)

image.png
image.png

2.7程序添加

image.png

点击添加按钮输入程序号,选择找到的业务程序.

image.png

2.8数据字典添加

image.png

点击添加,在数据字典ID搜索框中添加,程序号+MST后缀,找到对应的数据字典添加.

image.png

2.9启动添加

image.png

点击添加 选择你想让谁看到,都能看到选择下面所有职员.

image.png

启动成功状态设置:业务表名称+成功状态(一般情况下为03审核中)

image.png

3.流程设计

3.1节点配置

image.png

处理者添加可根据表添加,也可以勾选下面允许动态添加管理人.
查看原文

赞 0 收藏 0 评论 0

BolunWu 发布了文章 · 2020-11-13

Redis分片

1.Redis分片机制

1.1为什么需要分片机制

如果需要存储海量的内存数据,如果只使用一台redis,无法保证redis工作效率.大量的时间都浪费在了寻址当中.所以需要一种机制,来满足这种需求.

采用分片机制实现:

image

1.2Redis分片搭建

1.2.1搭建注意事项

Redis服务启动需要依赖redis.conf的配置文件.如果需要准备3台redis则需要准备3个redis.conf的配置.
准备端口号
6379
6380
6381

1.2.2分片实现

image

修改端口号:间各自的端口号进行修改

image

启动3台redis服务器

image

校验服务器是否正常运行

1.2.3关于分片的注意事项

1.问题描述
当启动多台redis服务器之后,多台redis暂时没有必然的联系,各自都是独立的实体,可以数据的存储.
2.如果将分片通过程序的方式进行操作,要把3台redis当做一个整体,所以与上述的操作完全不同,不会出现一个key同时保存多个redis的现象.

image

1.3分片入门案例

 /**
     * 测试Redis分片机制
     * 思考: shards 如何确定应该存储到哪台redis中呢???
     */
    @Test
    public void testShards(){
        List<JedisShardInfo> shards = new ArrayList<>();
        shards.add(new JedisShardInfo("192.168.126.129",6379));
        shards.add(new JedisShardInfo("192.168.126.129",6380));
        shards.add(new JedisShardInfo("192.168.126.129",6381));
        //准备分片对象
        ShardedJedis shardedJedis = new ShardedJedis(shards);
        shardedJedis.set("shards","redis分片测试");
        System.out.println(shardedJedis.get("shards"));
    }

1.4一致性hash算法

1.4.1一致性hash算法介绍

一致性hash算法在1997年由麻省理工学院提出,是一种特殊的算法,目的是解决分布式缓存问题.在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 。

image

1.4.2特性1-平衡性

概念:平衡性是指hash的结果应该平均分配各个节点,这样从算法上解决了负载均衡问题.(大致平分)
问题描述:由于节点都是通过hash方式进行算计,所以可能出现如图中的现象,导致负载严重不平衡.

image

解决方法:引入虚拟节点

image

1.4.3特性2-单调性

特点:单调性是指在新增或者删除节点时,不影响系统正常运行.

image

1.4.4特性3-分散性

谚语:鸡蛋不要放到一个篮里.
分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储的数据.

1.5SpringBoot整合Redis分片

1.5.1编辑配置文件

# 配置redis单台服务器
redis.host=192.168.126.129
redis.port=6379

# 配置redis分片机制
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

1.5.2编辑配置类

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.nodes}")
    private String nodes;  //node,node,node.....

    //配置redis分片机制
    @Bean
    public ShardedJedis shardedJedis(){
        nodes = nodes.trim();   //去除两边多余的空格
        List<JedisShardInfo> shards = new ArrayList<>();
        String[] nodeArray = nodes.split(",");
        for (String strNode : nodeArray){   //strNode = host:port
            String host = strNode.split(":")[0];
            int port = Integer.parseInt(strNode.split(":")[1]);
            JedisShardInfo info = new JedisShardInfo(host, port);
            shards.add(info);
        }
        return new ShardedJedis(shards);
    }
   }

1.5.3修改AOP注入项

image~~~~

查看原文

赞 0 收藏 0 评论 0

BolunWu 发布了文章 · 2020-11-13

Redis总结

1.Redis介绍

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis常见用法

1.Redis可以当做缓存使用
2.Redis可以当做数据库验证码使用
3.Redis可以当做消息中间件

1.1Redis安装

1)解压安装包
[root@localhost src]# tar -zxvf redis-5.0.4.tar.gz

image

2)安装Redis
说明:在Redis根目录执行命令
1.make
2.make install

image

1.2修改Redis配置文件

命令:展现行号:set nu

image

修改位置1:注释IP绑定

image

修改位置2:关闭保护模式

image

修改位置3:开启后台启动

image

1.3redis服务命令

1.启动命令:redis-server redis.conf
2.检索命令:ps-ef | grep redis
3.进入客户端:redis-cli -p 6379

4.关闭redis:kill-9 PID号 | redis-cli -p 6379 shutdown

image

2.Redis入门案例

2.1引入jar包文件

        <!--spring整合redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>

2.2编辑测试API

package com.jt.test;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

public class TestRedis {

    /**
     * 1.测试redis程序链接是否正常
     * 步骤:
     *      1.实例化jedis工具API对象(host:port)
     *      2.根据实例 操作redis  方法就是命令
     *
     * 关于链接不通的说明:
     *      1.检查Linux防火墙
     *      2.检查Redis配置文件修改项
     *          2.1 IP绑定
     *          2.2 保护模式
     *          2.3 后台启动
     *      3.检查redis启动方式  redis-server redis.conf
     *      4.检查IP 端口 及redis是否启动...
     *
     *      */
    @Test
    public void test01(){
        String host = "192.168.126.129";
        int port = 6379;
        Jedis jedis = new Jedis(host,port);
        jedis.set("cgb2006","好好学习 天天向上");
        System.out.println(jedis.get("cgb2006"));

        //2.练习是否存在key
        if(jedis.exists("cgb2006")){
            jedis.del("cgb2006");
        }else{
            jedis.set("cgb2006", "xxxx");
            jedis.expire("cgb2006", 100);
        }


    }
}

2.3Redis常见用法

2.3.1setex学习


/**
     * 2.需求:
     *      1.向redis中插入数据  k-v
     *      2.为key设定超时时间  60秒后失效.
     *      3.线程sleep 3秒
     *      4.获取key的剩余的存活时间.
     *
     *   问题描述: 数据一定会被删除吗??????
     *   问题说明: 如果使用redis 并且需要添加超时时间时 一般需要满足原子性要求.
     *   原子性:   操作时要么成功 要么失败.但是必须同时完成.
     */
    @Test
    public void test02() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.setex("宝可梦", 60, "小火龙 妙蛙种子");
        System.out.println(jedis.get("宝可梦"));

       /* Jedis jedis = new Jedis("192.168.126.129",6379);
        jedis.set("宝可梦", "小火龙 妙蛙种子");
        int a = 1/0;    //可能会出异常
        jedis.expire("宝可梦", 60);
        Thread.sleep(3000);
        System.out.println(jedis.ttl("宝可梦"));*/
    }

2.3.2setnx学习

/**
     * 3.需求如果发现key已经存在时 不修改数据.如果key不存在时才会修改数据.
     *
     */
    @Test
    public void test03() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.setnx("aaaa", "测试nx的方法");
        /*if(jedis.exists("aaa")){
            System.out.println("key已经存在 不做修改");
        }else {
            jedis.set("aaa", "测试数据");
        }*/
        System.out.println(jedis.get("aaaa"));
    }

2.3.3set超时时间原子性操作

 /**
     * 需求:
     *  1.要求用户赋值时,如果数据存在则不赋值.  setnx
     *  2.要求在赋值操作时,必须设定超时的时间 并且要求满足原子性 setex
     */
    @Test
    public void test04() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        SetParams setParams = new SetParams();
        setParams.nx().ex(20);
        jedis.set("bbbb", "实现业务操作AAAA", setParams);
        System.out.println(jedis.get("bbbb"));


    }

2.3.4list集合练习

  @Test
    public void testList() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3");
        System.out.println(jedis.rpop("list"));
    }

2.3.5redis事务控制

 @Test
    public void testTx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //1.开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("aa", "aa");
            //提交事务
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            transaction.discard();
        }
    }

3.1常规锁操作

3.1.1超卖的原因

image

3.1.2同步锁的问题

说明同步锁只能解决tomcat内部问题,不能解决多个tomcat并发问题

image

3.1.3分布锁机制

思想
1.锁应该使用第三方操作,锁应该共用
2.原则:如果锁正在被人使用时,其他用户不能操作
3.策略:用户向redis中保存一个key,如果redis中有key表示正在有人使用这把锁,其他用户不允许操作,redis中没有key,则表示可以使用这把锁.
4.风险:如何解决死锁问题,设置超时时间.

image

3.SpringBoot整合Redis

3.1编辑配置文件redis.pro

说明:由于该配置被其他项目共同使用,则应该写入jt-common中.

image

3.2编辑配置类

说明:编辑redis配置类将Jedis对象交给Spring容器管理
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){

        return new Jedis(host,port);
    }
}

3.3对象与JSON转化 ObjectMapper介绍

3.3.1简单对象转化

/**
     * 测试简单对象的转化
     */
    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息")
                .setCreated(new Date()).setUpdated(new Date());
        //对象转化为json
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //json转化为对象
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

3.3.2集合对象转化

 /**
     * 测试集合对象的转化
     */
    @Test
    public void test02() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息1")
                .setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2 = new ItemDesc();
        itemDesc2.setItemId(100L).setItemDesc("商品详情信息2")
                .setCreated(new Date()).setUpdated(new Date());
        List<ItemDesc> lists = new ArrayList<>();
        lists.add(itemDesc);
        lists.add(itemDesc2);
        //[{key:value},{}]
        String json = objectMapper.writeValueAsString(lists);
        System.out.println(json);

        //将json串转化为对象
        List<ItemDesc> list2 = objectMapper.readValue(json, lists.getClass());
        System.out.println(list2);
    }

3.4编辑工具API

package com.jt.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.springframework.util.StringUtils;

public class ObjectMapperUtil {

    /**
     * 1.将用户传递的数据转化为json串
     * 2.将用户传递的json串转化为对象
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();

     //1.将用户传递的数据转化为json串
    public static String toJSON(Object object){

        if(object == null) {
            throw new RuntimeException("传递的数据为null.请检查");
        }

        try {
            String json = MAPPER.writeValueAsString(object);
            return json;
        } catch (JsonProcessingException e) {
            //将检查异常,转化为运行时异常
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    //需求: 要求用户传递什么样的类型,我返回什么样的对象  泛型的知识
    public static <T> T toObj(String json,Class<T> target){
        if(StringUtils.isEmpty(json) || target ==null){
            throw new RuntimeException("参数不能为null");
        }
        try {
           return  MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

3.5商品分类的缓存实现

3.5.1实现步骤

1.定义Redis中的key key必须是唯一不能重复的,存取key="ITEM_CAT_PARENTID::70"
2.根据key去redis中进行查询 有数据 没有数据
3.没有数据 则查询数据库获取记录,之后将数据保存到redis中方便后续使用.
4.有数据 表示用户不是第一次查询 可以将缓存的数据直接返回即可.

3.5.2编辑ItemController

image

3.5.3编辑ItemCatService

@Override
    public List<EasyUITree> findItemCatListCache(Long parentId) {
        //0.定义公共的返回值对象
        List<EasyUITree> treeList = new ArrayList<>();
        //1.定义key
        String key = "ITEM_CAT_PARENTID::"+parentId;
        //2.检索redis服务器,是否含有该key

        //记录时间
        Long startTime = System.currentTimeMillis();
        if(jedis.exists(key)){
            //数据存在
            String json = jedis.get(key);
            Long endTime = System.currentTimeMillis();
            //需要将json串转化为对象
            treeList = ObjectMapperUtil.toObj(json,treeList.getClass());
            System.out.println("从redis中获取数据 耗时:"+(endTime-startTime)+"毫秒");
        }else{
            //3.数据不存在  查询数据库
            treeList = findItemCatList(parentId);
            Long endTime = System.currentTimeMillis();
            //3.将数据保存到缓存中
            String json = ObjectMapperUtil.toJSON(treeList);
            jedis.set(key, json);
            System.out.println("查询数据库 耗时:"+(endTime-startTime)+"毫秒");
        }
        return treeList;
    }

3.5.4使用Redis的速度差

image

4.AOP实现Redis缓存

4.1自定义缓存注解

1.注解名称:cacheFind
2.属性参数
2.1key:应该由用户自己手动添加一般添加业务名称之后动态拼接形成唯一的key
2.2seconds:用户可以指定数据的超时时间
@Target(ElementType.METHOD) //注解对方法有效
@Retention(RetentionPolicy.RUNTIME)  //运行期有效
public @interface CacheFind {

    public String preKey();          //用户标识key的前缀.
    public int seconds() default 0;  //如果用户不写表示不需要超时. 如果写了以用户为准.
}

image

4.2编辑CacheAOP

package com.jt.aop;

import com.jt.anno.CacheFind;
import com.jt.config.JedisConfig;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect     //我是一个AOP切面类
@Component  //将类交给spring容器管理
public class CacheAOP {

    @Autowired
    private Jedis jedis;

    /**
     * 切面 = 切入点 + 通知方法
     *        注解相关 + 环绕通知  控制目标方法是否执行
     *
     *  难点:
     *      1.如何获取注解对象
     *      2.动态生成key  prekey + 用户参数数组
     *      3.如何获取方法的返回值类型
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
        Object result = null;
        try {
            //1.拼接redis存储数据的key
            Object[] args = joinPoint.getArgs();
            String key = cacheFind.preKey() +"::" + Arrays.toString(args);

            //2. 查询redis 之后判断是否有数据
            if(jedis.exists(key)){
                //redis中有记录,无需执行目标方法
                String json = jedis.get(key);
                //动态获取方法的返回值类型   向上造型  向下造型
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
                Class returnType = methodSignature.getReturnType();
                result = ObjectMapperUtil.toObj(json,returnType);
                System.out.println("AOP查询redis缓存");
            }else{
                //表示数据不存在,需要查询数据库
                result = joinPoint.proceed();  //执行目标方法及通知
                //将查询的结果保存到redis中去
                String json = ObjectMapperUtil.toJSON(result);
                //判断数据是否需要超时时间
                if(cacheFind.seconds()>0){
                    jedis.setex(key,cacheFind.seconds(),json);
                }else {
                    jedis.set(key, json);
                }
                System.out.println("aop执行目标方法查询数据库");
            }

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

}

5.关于Redis配置说明

5.1关于Redis持久化说明

redis默认条件下支持数据的持久化操作,当redis中有数据时会定期将数据保存到磁盘中,当redis服务器重启时,会根据配置文件读取指定的持久化文件.实现内存数据的恢复.

5.2持久化方式介绍

5.2.1RDB模式

特点

1.RDB模式是redis的默认的持久化策略.
2.RDB模式记录的是Redis内存数据的快照,最新的快照会覆盖之前的内容,所有RDB持久化文件占用空间更小,持久化效率高.
3.RDB模式由于是定期持久化,所以可能导致数据丢失.

命令:

1. save 要求立即马上持久化 同步的操作 其他的redis操作会陷入阻塞的状态.
2. bgsave 开启后台运行 异步的操作 由于是异步操作,所以无法保证rdb文件一定是最新的需要等待.

配置

1.持久化文件名称:

image

2.持久化文件位置:

image

3.RDB模式持久化策略

image

5.2.2AOF模式

特点:

1.AOF模式默认条件下是关闭的,需要用户手动开启

image

2.AOF模式是异步操作 记录的是用户的操作过程可以防止用户数据的丢失.
3.由于AOF模式记录的是程序的运行状态,所以持久化文件较大,恢复数据的时间长,需要人为的优化持久化文件.

配置:

image

5.2.3关于持久化操作的总结

1.如不不允许数据丢失采用AOF模式
2.如果追求追求效率,运行少量数据丢失 采用RDB模式
3.如果既要保证效率 又要保证数据 则配置redis的集群 主机使用RDB 从机使用AOF

5.3关于Redis的内存策略

5.3.1关于内存策略的说明

说明:Redis数据的存储都在内存中,如果一直想内存中存储数据,必然会导致内存数据的溢出.

解决方式

1.尽可能为保存在redis中的数据添加超时时间.
2.利用算法优化旧数据

5.3.2LRU算法

特点:最好用的内存优化方法.
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰.该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间t,当必须淘汰一个页面时,选择现有页面中t值最大的,即最近最少使用被淘汰.

5.3.3LFU算法

LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页面置换引用计数最小的页,因为经常使用的页应该有一个较大的使用次数.但是有些页开始时使用次数最多,但是以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数.
维度:使用次数.

5.3.4 RANDOM算法

随机删除数据

5.3.5TTL算法

把设定了超时时间的数据将要移除的提前删除算法.

5.3.6Redis内存数据优化

1.volatile-lru 设定了超时时间的数据采用lru算法
2.allkeys-lru 所有的数据采用LRU算法
3.volatile-lfu 设定了超时时间的数据采用lfu算法删除
4.allkeys-lfu 所有数据采用lfu算法删除
5.volatile-random 设定超时时间的数据采用随机算法
6.allkeys-random 所有数据的随机算法
7.volatile-ttl 设定超时时间的数据的TTL算法
8.noeviction 如果内存溢出了 则报错返回. 不做任何操作. 默认值

image

查看原文

赞 0 收藏 0 评论 0

BolunWu 发布了文章 · 2020-11-13

Dubbo总结

1.Dubbo

1.1 Dubbo介绍

Apache Dubbo是一款高性能,轻量级的开源Java RPC框架,他提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现.

image

1.2 Dubbo特点

image

2.Dubbo入门案例

2.1定义公共接口项目

说明:接口项目一般定义公共的部分,并且被第三方依赖.

image

2.2服务提供者介绍

2.2.1提供者代码结构

image

2.2.2编辑实现类

package com.jt.dubbo.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.config.annotation.Service;
import com.jt.dubbo.mapper.UserMapper;
import com.jt.dubbo.pojo.User;
@Service(timeout=3000)    //3秒超时 内部实现了rpc
//@org.springframework.stereotype.Service//将对象交给spring容器管理
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public List<User> findAll() {
        
        System.out.println("我是第一个服务的提供者");
        return userMapper.selectList(null);
    }
    
    @Override
    public void saveUser(User user) {
        
        userMapper.insert(user);
    }
}

2.2.3编辑提供者配置文件

server:
  port: 9000   #定义端口

spring:
  datasource:
    #引入druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root

#关于Dubbo配置   
dubbo:
  scan:
    basePackages: com.jt    #指定dubbo的包路径 扫描dubbo注解
  application:              #应用名称
    name: provider-user     #一个接口对应一个服务名称   一个接口可以有多个实现
  registry:  #注册中心 用户获取数据从机中获取 主机只负责监控整个集群 实现数据同步
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20880  #每一个服务都有自己特定的端口 不能重复.

      
mybatis-plus:
  type-aliases-package: com.jt.dubbo.pojo       #配置别名包路径
  mapper-locations: classpath:/mybatis/mappers/*.xml  #添加mapper映射文件
  configuration:
    map-underscore-to-camel-case: true                #开启驼峰映射规则

2.3服务消费者介绍

2.3.1编辑Controller

package com.jt.dubbo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.dubbo.pojo.User;
import com.jt.dubbo.service.UserService;

@RestController
public class UserController {
    
    //利用dubbo的方式为接口创建代理对象 利用rpc调用
    @Reference
    private UserService userService; 
    
    /**
     * Dubbo框架调用特点:远程RPC调用就像调用自己本地服务一样简单
     * @return
     */
    @RequestMapping("/findAll")
    public List<User> findAll(){
        
        //远程调用时传递的对象数据必须序列化.
        return userService.findAll();
    }
    
    @RequestMapping("/saveUser/{name}/{age}/{sex}")
    public String saveUser(User user) {
        
        userService.saveUser(user);
        return "用户入库成功!!!";
    }
}

2.3.2 编辑配置YML文件

server:
  port: 9001
dubbo:
  scan:
    basePackages: com.jt
  application:
    name: consumer-user   #定义消费者名称
  registry:               #注册中心地址
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183

2.3.3Dubbo入门测试

image

2.4关于Dubbo框架知识点

2.4.1问题1:如果其中一个服务器宕机,用户访问受限?

由于zk的帮助,使得程序永远可以访问正确的服务器,并且当服务重启时,dubbo有服务自动发现功能,消费者不需要重启即可以访问新的服务.

2.4.2问题2:如果ZK集群短时间宕机,用户访问是否受限?

用户的访问不受影响,由于消费者在本地存储列表信息,当访问故障机时,自动的将标识信息改为down属性.

2.5Dubbo负载均衡策略

2.5.1负载均衡种类

1.客户端负载均衡
Dubbo/SpringCloud等微服务框架

image

2.服务端负载均衡
说明:客户端发起请求后,必须由统一的服务器进行负载均衡,所有压力都在服务器中.
NGINX

image

2.5.2Dubbo负载均衡的方式

@RestController
public class UserController {
    
    //利用dubbo的方式为接口创建代理对象 利用rpc调用
    //@Reference(loadbalance = "random")            //默认策略  负载均衡随机策略
    //@Reference(loadbalance = "roundrobin")        //轮询方式
    //@Reference(loadbalance = "consistenthash")    //一致性hash  消费者绑定服务器提供者
    @Reference(loadbalance = "leastactive")            //挑选当前负载小的服务器进行访问
    private UserService userService; 

}

3.京淘项目Dubbo改造

3.1改造JT-SSO

3.1.1 添加jar包文件

        <!--引入dubbo配置 -->
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>0.2.0</version>
        </dependency>

3.1.2创建DubboUserService接口

image

3.1.3创建提供者实现类

image

3.1.4编辑提供者YML配置文件

server:
  port: 8093
  servlet:
    context-path: /
spring:
  datasource:
    #引入druid数据源
    #type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root

  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true

logging:
  level: 
    com.jt.mapper: debug

#关于Dubbo配置
dubbo:
  scan:
    basePackages: com.jt    #指定dubbo的包路径 扫描dubbo注解
  application:              #应用名称
    name: provider-user     #一个接口对应一个服务名称   一个接口可以有多个实现
  registry:  #注册中心 用户获取数据从机中获取 主机只负责监控整个集群 实现数据同步
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20880  #每一个服务都有自己特定的端口 不能重复.

3.1.5启动服务提供者

测试Dubbo服务器启动是否正常

image

3.2改造服务消费者JT-WEB

3.2.1注入Service接口

image

3.2.2编辑消费者配置文件

server:
  port: 8092    
spring:     #定义springmvc视图解析器
  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp

dubbo:
  scan:
    basePackages: com.jt
  application:
    name: consumer-web   #定义消费者名称
  registry:               #注册中心地址
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183

  

3.1.3启动效果测试

image~~~~

查看原文

赞 0 收藏 0 评论 0

BolunWu 发布了文章 · 2020-11-11

Spring AOP简介

1.1 AOP概述

1.1.1 AOP是什么?

AOP是一种设计思想,是软件设计领域的面向切面编程,他是面向对象编程OOP的一种补充和完善.它以通过预编译的方式和运行期动态代理方式,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术.

1.1.2 AOP应用场景分析?

实际项目中通常会将系统分为两部分,一部分是核心业务,一部分是非核心业务.在编程实现时我们首先要完成的是核心业务的实现,非核心业务我们一般通过特定的方式切入到系统中,这种方式一般借助AOP实现.
AOP就是要基于OCP(开闭原则),在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以控制对象执行.例如AOP应用于项目中的日志处理,事务处理,权限处理,缓存处理等等.

1.1.3 AOP应用原理分析

1)假如目标对象(被代理对象)实现接口,则底层可以采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口).
2)假如目标对象(被代理对象)没有实现接口,则底层可以采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型).
AOP在默认的情况下使用CGLIB代理,加入使用JDK动态代理可以在配置文件(application.properties)中配置如下:
spring.aop.proxy-target-class=false

1.2AOP相关术语分析

切面(asprct):横切面对象,一般为一个具体类对象(可以借助@Acpect声明).
通知(Advice):在横切面的某个特定连接点上执行的动作(扩展功能),例如around,before,after等.
连接点(joinpoint):程序执行中的某个特定点一般指被拦截到的方法.
切入点(pointcut):对多个连接点(Joinpoint)一种定义,一般可以理解为多个连接点的集合.

2.Spring AOP快速实践

2.1业务描述

基于项目中的核心业务,简单添加日志操作,借助SLF4J日志API输出目标方法的执行时长.(前提,不能修改目标方法的代码-遵循OCP原则)

2.2项目创建及配置

创建maven项目或在已有的项目基础上添加AOP启动依赖:
<dependency>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-aop</artifactId>

</dependency>

说明:基于此依赖spring可以整合AspectJ框架快锁完成AOP的基本操作,AspectJ是一个面向切面的框架,它定义了AOP的一些语法,有一个专门的自己吗生成器来生成遵守java规范的class文件.

2.3 扩展业务分析及实现

2.3.1创建日志切面类对象

将此日志切面类作为核心业务增强(一个横切面对象)类,用于输出业务执行时长.
package com.cy.pj.common.aspect;

@Aspect

@Slf4j

@Component

public class SysLogAspect {

         @Pointcut("bean(sysUserServiceImpl)")

         public void logPointCut() {}


         @Around("logPointCut()")

         public Object around(ProceedingJoinPoint jp)

         throws Throwable{

                 try {

                   log.info("start:{}"+System.currentTimeMillis());

                   Object result=jp.proceed();//最终会调用目标方法

                   log.info("after:{}"+System.currentTimeMillis());

                   return result;

                 }catch(Throwable e) {

                   log.error("after:{}",e.getMessage());

                   throw e;

                 }

         }
说明:
@Aspect注解用于标识或者描述AOP中切面类型,基于切面类型构建的对象用于为目标对象进行功能扩展或控制目标对象的执行.
@Pointcut注解用于描述切面中的方法,并定义切面中的切入点,(基于特定表达式的方式进行描述).
@Around注解用以描述切面中的方法,这样的方法会被认为是一个环绕通知(核心业务方法执行之前和执行之后要执行的一个动作),@Around注解内部value属性的值为一个切入点表达式或者是切入点表达式的一个引用(这个引用为@Pointcut注解描述的方法的方法名).
ProceedingJoinPoint类作为一个连接点类型,此类型的对象用于封装要执行的目标方法相关的一些信息.只能用于@Around注解描述的方法参数.

2.3.1业务切面测试实现

启动项目测试或者进行单元测试:
@SpringBootTest

public class AopTests {

         @Autowired

         private SysUserService userService;

         @Test

         public void testSysUserService() {

                 PageObject<SysUserDeptVo> po=

                 userService.findPageObjects("admin",1);

                 System.out.println("rowCount:"+po.getRowCount());

         }

}

2.4 扩展业务织入增强分析

2.4.1基于JDK代理方式实现

假如目标对象有实现接口,则可以基于JDK为目标对象创建代理对象,然后对目标对象进行功能扩展.

2.4.2基于CGLIB代理方式实现

加入目标对象没有实现接口(当然实现了接口也是可以的),可以基于CGLIB代理方式为目标对象织入功能扩展.
说明:目标对象实现了接口也可以基于CGLIB为目标对象创建对象.

3.Spring AOP增强

3.1切面通知应用增强

3.1.1通知类型

在基于Spring AOP编程的过程中,基于AspectJ框架标准,spring中定义了五种类型的通知(通知描述是一种扩展业务),它们分别是:
@Before
@AfterReturning
@AfterThrowing
@After
@Around重点掌握(优先级最高)
说明:在切面类中使用什么通知,由业务决定,并不是说,在切面中要把所有的通知都写上.

3.1.2 通知执行顺序

image

说明:实际项目中可能不会在切面中定义所有通知,具体定义那些通知要结合业务进行实现.

3.1.3通知实践过程分析

@Component

@Aspect

public class SysTimeAspect {

        @Pointcut("bean(sysUserServiceImpl)")

        public void doTime(){}


        @Before("doTime()")

        public void doBefore(JoinPoint jp){

                System.out.println("time doBefore()");

        }

        @After("doTime()")

        public void doAfter(){

                System.out.println("time doAfter()");

        }

        /**核心业务正常结束时执行* 说明:假如有after,先执行after,再执行returning*/

        @AfterReturning("doTime()")

        public void doAfterReturning(){

                System.out.println("time doAfterReturning");

        }

        /**核心业务出现异常时执行说明:假如有after,先执行after,再执行Throwing*/

        @AfterThrowing("doTime()")

        public void doAfterThrowing(){

                System.out.println("time doAfterThrowing");

        }

        @Around("doTime()")

        public Object doAround(ProceedingJoinPoint jp)

                        throws Throwable{

                System.out.println("doAround.before");

         try{

                 Object obj=jp.proceed();

           System.out.println("doAround.after");

          return obj;

                 }catch(Throwable e){

          System.out.println(e.getMessage());

          throw e;

         }

                

        }

}
说明:对于@AfterThrowing通知只有在出现有异常时才会执行,所以当做一些异常监控时可在此方法中进行代码实现.

3.2切入点表达式增强

3.2.1bean表达式(重点)

bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:
bean("userServiceImpl")指定一个userServiceImpl中的所有方法.
bean("*ServiceImpl")指定所有后缀为ServiceImpl的类中所有方法.
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的名字应该是spring容器中的某个bean的name.

3.2.2within表达式(了解)

within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:
within("aop.service.UserServiceImpl")指定当前包中这个类内部的所有方法.
within("aop.service.*") 指定当前目录下的所有类的所有方法.
within("aop.service..*") 指定当前目录以及子目录中类的所有方法.

3.2.3execution表达式(了解)

execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析:
语法:execution(返回值类型 包名.类名.方法名(参数列表)).
execution(void aop.service.UserServiceImpl.addUser())匹配addUser方法。
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为String的addUser方法
execution( aop.service...*(..)) 万能配置.

3.2.4@annotation表达式(重点)

@annontation(anno.RequiredLog)匹配有此注解描述的方法.
@annontation(anno.RequiredCache)匹配有此注解描述的方法.
其中:RequiredLog为我们自定义注解,当我们使用@RequiredLog注解修饰业务层方法时,系统底层会在执行此方法时进行日扩展操作.
:定义一Cache相关切面,使用注解表达式定义切入点,并使用此注解对需要使用cache的业务方法进行描述,代码分析如下:
第一步:定义注解RequiredCache
package com.cy.pj.common.annotation;

/**

 * 自定义注解,一个特殊的类,所有注解都默认继承Annotation接口

 */

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface RequiredCache {

   //...

}
第二步:定义SysCacheAspect切面对象
package com.cy.pj.common.aspect;

@Aspect

@Component

public class SysCacheAspect {

            @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredCache)")

          public void doCache() {}


          @Around("doCache()")

          public Object around(ProceedingJoinPoint jp)

    throws Throwable{

                  System.out.println("Get data from cache");

                  Object obj=jp.proceed();

                  System.out.println("Put data to cache");

                  return obj;

          }

   

}
第三步: 使用@RequiredCache注解对特定业务目标对象中的查询方法进行描述.
 @RequiredCache

        @Override

        public List<Map<String, Object>> findObjects() {

                ….

                return list;

        }

3.3切面优先级设置实现

切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低。例如:
定义日志切面并指定优先级
@Order(1)

@Aspect

@Component

public class SysLogAspect {

 …

}
定义缓存切面并指定优先级
@Order(2)

@Aspect

@Component

public class SysCacheAspect {

        …

}
说明: 当多个切面作用于同一个目标对象方法时,这些切面会构建成一个切面链,类似过滤器链、拦截器链.

3.4关键对象与术语总结

Spring 基于AspectJ框架实现AOP设计的关键对象概览,如图-10所示:

image

4.Spring AOP中的事务处理

4.1Spring AOP中事务简介

4.1.1事务定义

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

4.1.2事物的特性

事务具备的ACID特性,分别是:
原子性:一个事务中的多个操作要么都成功要么都失败。
一致性:例如存钱操作,存之前和存之后的总钱数应该是一致的。
隔离性:事务与事务应该是相互隔离的。
持久性:事务一旦提交,数据要持久保存。

4.2Spring中的事务处理

4.2.1Spring中事务方式概述

Spring框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦.也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制.
在SpringBoot项目中,其内部提供了事务的自动配置,当我们在项目中添加了指定依赖spring-boot-starter-jdbc时,框架会自动为我们的项目注入事务管理器对象,最常用的为DataSourceTransactionMannager对象.

4.2.2Spring 中事务管理实现

1)启用声明式事务管理,在项目启动类上添加@EnableTransactionManagement,新版本可以不用添加(例如SpringBoot项目)
2)将@Transactional注解添加到合适的业务类或方法上,并设置合适的属性信息.
代码示例:
@Transactional(timeout = 30,

               readOnly = false,

               isolation = Isolation.READ_COMMITTED,

               rollbackFor = Throwable.class,

               propagation = Propagation.REQUIRED)

  @Service

  public class implements SysUserService {

        @Transactional(readOnly = true)

    @Override

         public PageObject<SysUserDeptVo> findPageObjects(

                        String username, Integer pageCurrent) {

      …

       }

}

当@Transactional注解应用在类上时表示类中所有方法启动事务管理,并且一般用于事务共性的定义
当@Transactional描述方法时表示此方法要进行事务管理,假如类和方法上都有@Transactional注解,则方法上的事务特性优先级比较高。
@Transactional 常用属性应用说明:
timeout:事务的超时时间,默认值为-1,表示没有超时显示。如果配置了具体时间,则超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务开启以后到sql语句执行之前.
read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true。对添加,修改,删除业务read-only的值应该为false。
rollback-for:用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for: 抛出no-rollback-for 指定的异常类型,不回滚事务
isolation事务的隔离级别,默认值采用 DEFAULT。当多个事务并发执行时,可能会出现脏读,不可重复读,幻读等现象时,但假如不希望出现这些现象可考虑修改事务的隔离级别(但隔离级别越高并发就会越小,性能就会越差).

5.Spring AOP 异步操作实现

5.1异步场景分析

在开发系统的过程中,通常会考虑到系统的性能问题,提升系统性能的一个重要思想就是“串行”改“并行”。说起“并行”自然离不开“异步”,今天我们就来聊聊如何使用Spring的@Async的异步注解。

5.2Sping 业务的异步实现

5.2.1启动异步配置

再基于注解方式的配置中,借助@EnableAsync注解进行异步启动声明,SpringBoot版本的项目,将@EnableAsync注解应用到启动类上,代码示例如下:
  @EnableAsync //spring容器启动时会创建线程池

   @SpringBootApplication

   public class Application {

        public static void main(String[] args) {

                SpringApplication.run(Application.class, args);

        }

}

5.2.2Spring中@Async注解应用

在需要异步执行的业务方法上,使用@Async方法进行异步声明.
@Async

        @Transactional(propagation = Propagation.REQUIRES_NEW)

        @Override

        public void saveObject(SysLog entity) {

      System.out.println("SysLogServiceImpl.save:"+

Thread.currentThread().getName());

          sysLogDao.insertObject(entity);

          //try{Thread.sleep(5000);}catch(Exception e) {}

        }
假如需要获取业务层异步方法的执行结果,可以参考如下代码设计进行实现:
 @Transactional(propagation = Propagation.REQUIRES_NEW)

   @Async

        @Override

        public Future<Integer> saveObject(SysLog entity) {

                System.out.println("SysLogServiceImpl.save:"+

Thread.currentThread().getName());

                int rows=sysLogDao.insertObject(entity);

                //try{Thread.sleep(5000);}catch(Exception e) {}

            return new AsyncResult<Integer>(rows);

        }
其中,AsyncResult对象可以对异步方法的执行结果进行封装,假如外界需要异步方法结果时,可以通过Future对象的get方法获取结果。
我们需要自己对spring框架提供的连接池进行一些简易配置,可以参考如下代码:
spring:

  task:

    execution:

      pool:

        queue-capacity: 128

        core-size: 5

        max-size: 128

        keep-alive: 60000

      thread-name-prefix: db-service-task-

查看原文

赞 1 收藏 1 评论 0

BolunWu 发布了文章 · 2020-11-11

Shiro安全框架

1.Shiro安全框架

1.1Shiro框架简介

Shiro是apache旗下一个开源安全框架(http://shiro.apache.org),它将...

1.2Shiro的概要架构

1)Subject:主体对象,负责提交用户认证和授权信息.
2)SecuritvManager:安全管理器,负责认证,授权等业务实现.
3)Realm:领域对象,负责从数据层获取业务数据.

1.3Shiro框架认证拦截实现

1.3.1Shiro基本环境配置

1)添加shiro依赖
<dependency>

   <groupId>org.apache.shiro</groupId>

   <artifactId>shiro-spring</artifactId>

   <version>1.5.3</version>

</dependency>
2)shiro核心对象配置
在SpringBoot项目中没有提供shiro的自动化配置,需要我们自己配置
第一步创建SpringShiroConfig类,关键代码如下:
package com.cy.pj.common.config;

/**@Configuration 注解描述的类为一个配置对象,

 * 此对象也会交给spring管理

 */

@Configuration

public class SpringShiroConfig {


}
第二步在Shiro配置类中添加SecurityManager配置(这里一定要使用org.apache.shiro.mgt.SecurityManager这个接口对象),关键代码如下:
@Bean

public SecurityManager securityManager() {

                 DefaultWebSecurityManager sManager=

                 new DefaultWebSecurityManager();

                 return sManager;

}

第三步:在Shiro配置类中添加ShiroFilterFactoryBean对象的配置。通过此对象设置资源匿名访问,认证访问.关键代码如下:
@Bean

public ShiroFilterFactoryBean shiroFilterFactory (

                         SecurityManager securityManager) {

                 ShiroFilterFactoryBean sfBean=

                 new ShiroFilterFactoryBean();

                 sfBean.setSecurityManager(securityManager);

                 //定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)

                 LinkedHashMap<String,String> map= new LinkedHashMap<>();

                 //静态资源允许匿名访问:"anon"

                 map.put("/bower_components/**","anon");

                 map.put("/build/**","anon");

                 map.put("/dist/**","anon");

                 map.put("/plugins/**","anon");

                 //除了匿名访问的资源,其它都要认证("authc")后访问

                 map.put("/**","authc");

                 sfBean.setFilterChainDefinitionMap(map);

                 return sfBean;

         }

1.4Shiro登录页面实现

业务描述当服务端拦截到用户请求以后,判定此请求是否已经被认证,假如没有认证应该先跳转到登录页面.
第一步在Controller层添加一个呈现登录页面的方法,关键代码如下:
@RequestMapping("doLoginUI")

public String doLoginUI(){

                return "login";

}
第二步:修改SpringShiroConfig类中shiroFilterFactorybean的配置,添加登录的url的设置,关键代码如下:
@Bean

public ShiroFilterFactoryBean shiroFilterFactory (

                         SecurityManager securityManager) {

                 ShiroFilterFactoryBean sfBean=

                 new ShiroFilterFactoryBean();

                 sfBean.setSecurityManager(securityManager);

 sfBean.setLoginUrl("/doLoginUI");

//定义map指定请求过滤规则(哪些资源允许匿名访问,

哪些必须认证访问)

                 LinkedHashMap<String,String> map=

                                 new LinkedHashMap<>();

                 //静态资源允许匿名访问:"anon"

                 map.put("/bower_components/**","anon");

                 map.put("/modules/**","anon");

                 map.put("/dist/**","anon");

                 map.put("/plugins/**","anon");

                 //除了匿名访问的资源,其它都要认证("authc")后访问

                 map.put("/**","authc");

                 sfBean.setFilterChainDefinitionMap(map);

                 return sfBean;

}

1.4Shiro框架认证业务实现

1.4.1认证流程分析

身份认证即判定用户是否是系统的合法用户,用户访问系统资源时的认证(对用户身份信息的认证)
1)系统调用subject的login方法将用户信息提交给SecurityManager
2)SecurityManager将认证操作委托给认证器对象Authenticator
3)Authenticator将用户输入的身份认证信息传递个Realm
4)Realm访问数据库获取用户信息然后对信息进行封装并返回
5)Authenticator对realm返回的信息进行身份认证.

1.4.2具体业务代码

Dao层
SysUser findUserByUserName(String username)。
SysUserMapper文件添加SQL
 <select id="findUserByUserName"

           resultType="com.cy.pj.sys.entity.SysUser">

      select *

      from sys_users  

      where username=#{username}

   </select>
Service接口及实现
第一步定义ShiroUserRealm类,关键代码如下:
package com.cy.pj.sys.service.realm;

@Service

public class ShiroUserRealm extends AuthorizingRealm {


        @Autowired

        private SysUserDao sysUserDao;

                

        /**

         * 设置凭证匹配器(与用户添加操作使用相同的加密算法)

         */

        @Override

        public void setCredentialsMatcher(

              CredentialsMatcher credentialsMatcher) {

                //构建凭证匹配对象

                HashedCredentialsMatcher cMatcher=

                new HashedCredentialsMatcher();

                //设置加密算法

                cMatcher.setHashAlgorithmName("MD5");

                //设置加密次数

                cMatcher.setHashIterations(1);

                super.setCredentialsMatcher(cMatcher);

        }

        /**

         * 通过此方法完成认证数据的获取及封装,系统

         * 底层会将认证数据传递认证管理器,由认证

         * 管理器完成认证操作。

         */

        @Override

        protected AuthenticationInfo doGetAuthenticationInfo(

                        AuthenticationToken token)

                        throws AuthenticationException {

                //1.获取用户名(用户页面输入)

                UsernamePasswordToken upToken=

                (UsernamePasswordToken)token;

                String username=upToken.getUsername();

                //2.基于用户名查询用户信息

                SysUser user=

                sysUserDao.findUserByUserName(username);

                //3.判定用户是否存在

                if(user==null)

                throw new UnknownAccountException();

                //4.判定用户是否已被禁用。

                if(user.getValid()==0)

                throw new LockedAccountException();

                

                //5.封装用户信息

                ByteSource credentialsSalt=

                ByteSource.Util.bytes(user.getSalt());

                //记住:构建什么对象要看方法的返回值

                SimpleAuthenticationInfo info=

                new SimpleAuthenticationInfo(

                                user,//principal (身份)

                                user.getPassword(),//hashedCredentials

                                credentialsSalt, //credentialsSalt

                                getName());//realName

                //6.返回封装结果

                return info;//返回值会传递给认证管理器(后续

                //认证管理器会通过此信息完成认证操作)

        }

    ....

}
第二步:对此realm,需要在SpringShiroConfig配置类中,注入SecurityManager对象,修改securityManager方法
@Bean

public SecurityManager securityManager(Realm realm) {

                 DefaultWebSecurityManager sManager=

                 new DefaultWebSecurityManager();

                 sManager.setRealm(realm);

                 return sManager;

}
Controller层实现
第一步: 在SysUserController中添加处理登陆的方法.关键代码如下:
   public JsonResult doLogin(String username,String password){

                   //1.获取Subject对象

                   Subject subject=SecurityUtils.getSubject();

                   //2.通过Subject提交用户信息,交给shiro框架进行认证操作

                   //2.1对用户进行封装

                   UsernamePasswordToken token=

                   new UsernamePasswordToken(

                                   username,//身份信息

                                   password);//凭证信息

                   //2.2对用户信息进行身份认证

                   subject.login(token);

                   //分析:

                   //1)token会传给shiro的SecurityManager

                   //2)SecurityManager将token传递给认证管理器

                   //3)认证管理器会将token传递给realm

                   return new JsonResult("login ok");

           }
第二步: 修改shiroFilerFactory的配置,对/user/doLogin这个路径进行匿名访问的配置:
@Bean

public ShiroFilterFactoryBean shiroFilterFactory (

                         SecurityManager securityManager) {

                 ShiroFilterFactoryBean sfBean=

                 new ShiroFilterFactoryBean();

                 sfBean.setSecurityManager(securityManager);

                 //假如没有认证请求先访问此认证的url

                 sfBean.setLoginUrl("/doLoginUI");

                 //定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)

                 LinkedHashMap<String,String> map=

                                 new LinkedHashMap<>();

                 //静态资源允许匿名访问:"anon"

                 map.put("/bower_components/**","anon");

                 map.put("/build/**","anon");

                 map.put("/dist/**","anon");

                 map.put("/plugins/**","anon");


  map.put("/user/doLogin","anon");                       //authc表示,除了匿名访问的资源,其它都要认证("authc")后才能访问访问

                 map.put("/**","authc");

                 sfBean.setFilterChainDefinitionMap(map);

                 return sfBean;

         }
第三步: 当我们在执行登录操作时,为了提高用户体验,可对系统中的异常信息进行处理,在统一的异常类中处理:
  @ExceptionHandler(ShiroException.class)

   @ResponseBody

        public JsonResult doHandleShiroException(

                        ShiroException e) {

                JsonResult r=new JsonResult();

                r.setState(0);

                if(e instanceof UnknownAccountException) {

                        r.setMessage("账户不存在");

                }else if(e instanceof LockedAccountException) {

                        r.setMessage("账户已被禁用");

                }else if(e instanceof IncorrectCredentialsException) {

                        r.setMessage("密码不正确");

                }else if(e instanceof AuthorizationException) {

                        r.setMessage("没有此操作权限");

                }else {

                        r.setMessage("系统维护中");

                }

                e.printStackTrace();

                return r;

        }

1.5Shiro框架授权过程实现

1.5.1 shiro框架授权过程分析

1)系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager.
2)SecurityManager将权限检测信息交给Authorize
3)Authorize将用户信息委托给realm
4)Realm访问数据库获取用户权限信息并封装
5)Autrorize对用户授权信息进行判定

1.5.2添加授权配置

在SpringShiroConfig类中添加授权时的相关配置:
第一步: 配置bean对象的生命周期管理(SpringBoot可以不用配置)
@Bean

public LifecycleBeanPostProcessor   lifecycleBeanPostProcessor() {

                 return new LifecycleBeanPostProcessor();

}
第二步: 通过如下配置为目标业务对象创建代理对象:(SpringBoot中可以省略)

@DependsOn("lifecycleBeanPostProcessor")

@Bean

public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

                 return new DefaultAdvisorAutoProxyCreator();

}
第三步: 配置advisor对象,shiro框架会通过此对象的matchs方法返回值(类似于切入点)决定是否创建代理对象,进行权限控制.
@Bean

public AuthorizationAttributeSourceAdvisor

authorizationAttributeSourceAdvisor (

                                SecurityManager securityManager) {

                        AuthorizationAttributeSourceAdvisor advisor=

                                new AuthorizationAttributeSourceAdvisor();

advisor.setSecurityManager(securityManager);

        return advisor;

}

//说明:使用框架最重要的尊重规则,框架规则指定了什么方式就使用什么方式。

1.5.3授权代码实现

Dao实现
第一步: 在SysUserRoleDao中定义基于用户id查找角色id的方法:
   List<Integer> findRoleIdsByUserId(Integer id);
第二步: 在RoleMenuDao中定义基于角色id查找菜单的id方法:
  List<Integer> findMenuIdsByRoleIds(

                        @Param("roleIds")List<Integer> roleIds);
第三步: 在SysMenuDao中基于菜单id查找权限标识的方法:
 List<String> findPermissions(

                        @Param("menuIds")

                        List<Integer> menuIds);
Mapper实现
第一步: 在SysUserRoleMapper中定义findRoleIdsByUserId元素:
<select id="findRoleIdsByUserId"

            resultType="int">

           select role_id

           from sys_user_roles

           where user_id=#{userId}        

</select>
第二步: 在SysRoleMenuMapper中定义findMenuIdsByRoleIds元素:
 <select id="findMenuIdsByRoleIds"

         resultType="int">

         select menu_id

         from sys_role_menus

         where role_id in

         <foreach collection="roleIds"

                  open="("

                  close=")"

                  separator=","

                  item="item">

               #{item}

         </foreach>

</select>
第三步: 在SysMenuMapper中定义findPermission元素:
  <select id="findPermissions"

           resultType="string">

       select permission <!-- sys:user:update -->

       from sys_menus

       where id in

       <foreach collection="menuIds"

                open="("

                close=")"

                separator=","

                item="item">

            #{item}

       </foreach>

   </select>
Service实现
@Service

public class ShiroUserRealm extends AuthorizingRealm {

        @Autowired

        private SysUserDao sysUserDao;

        @Autowired

        private SysUserRoleDao sysUserRoleDao;

        @Autowired

        private SysRoleMenuDao sysRoleMenuDao;

        @Autowired

        private SysMenuDao sysMenuDao;

        /**通过此方法完成授权信息的获取及封装*/

        @Override

        protected AuthorizationInfo doGetAuthorizationInfo(

                PrincipalCollection principals) {

                //1.获取登录用户信息,例如用户id

                SysUser user=(SysUser)principals.getPrimaryPrincipal();

                Integer userId=user.getId();

                //2.基于用户id获取用户拥有的角色(sys_user_roles)

                List<Integer> roleIds=

                sysUserRoleDao.findRoleIdsByUserId(userId);

                if(roleIds==null||roleIds.size()==0)

                throw new AuthorizationException();

                //3.基于角色id获取菜单id(sys_role_menus)

                List<Integer> menuIds=

                sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);

            if(menuIds==null||menuIds.size()==0)

            throw new AuthorizationException();

                //4.基于菜单id获取权限标识(sys_menus)

            List<String> permissions=

            sysMenuDao.findPermissions(menuIds);

                //5.对权限标识信息进行封装并返回

            Set<String> set=new HashSet<>();

            for(String per:permissions){

                    if(!StringUtils.isEmpty(per)){

                            set.add(per);

                    }

            }

            SimpleAuthorizationInfo info=

            new SimpleAuthorizationInfo();

            info.setStringPermissions(set);

                return info;//返回给授权管理器

        }




}

1.6 Shiro记住我实现

1.6.1 服务端业务实现的具体步骤如下:

第一步:在SysUserController中的doLogin方法中基于是否选中记住我,设置token的setRememberMe方法.
@RequestMapping("doLogin")

         @ResponseBody

         public JsonResult doLogin(

                         boolean isRememberMe,

                         String username,

                         String password) {

                 //1.封装用户信息

                 UsernamePasswordToken token=

                 new UsernamePasswordToken(username, password);

                 if(isRememberMe) {

                        token.setRememberMe(true);

                 }

                 //2.提交用户信息

                 Subject subject=SecurityUtils.getSubject();

                 subject.login(token);//token会提交给securityManager

                 return new JsonResult("login ok");

         }
第二步: 在SpringShiroConfig配置类中添加记住我配置,关键代码如下:
@Bean

         public RememberMeManager rememberMeManager() {

                 CookieRememberMeManager cManager=

                 new CookieRememberMeManager();

  SimpleCookie cookie=new SimpleCookie("rememberMe");

                 cookie.setMaxAge(7*24*60*60);

                 cManager.setCookie(cookie);

                 return cManager;

         }
第三步: 在SpringShiroConfig中修改securityManager的配置,为securityManager注入rememberManager对象:
 @Bean

         public SecurityManager securityManager(

                        Realm realm,CacheManager cacheManager

RememberMeManager rememberManager) {

                 DefaultWebSecurityManager sManager=

                 new DefaultWebSecurityManager();

                 sManager.setRealm(realm);

                 sManager.setCacheManager(cacheManager);

                 sManager.setRememberMeManager(rememberManager);

                 return sManager;

         }
第四步: 修改shiro的过滤认证级别,将/=author修改为/=user:
@Bean

         public ShiroFilterFactoryBean shiroFilterFactory(

                         SecurityManager securityManager) {

                 ShiroFilterFactoryBean sfBean=

                 new ShiroFilterFactoryBean();

                 sfBean.setSecurityManager(securityManager);

                 //假如没有认证请求先访问此认证的url

                 sfBean.setLoginUrl("/doLoginUI");

                 //定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)

                 LinkedHashMap<String,String> map=

                                 new LinkedHashMap<>();

                 //静态资源允许匿名访问:"anon"

                 map.put("/bower_components/**","anon");

                 map.put("/build/**","anon");

                 map.put("/dist/**","anon");

                 map.put("/plugins/**","anon");

                 map.put("/user/doLogin","anon");

                 map.put("/doLogout", "logout");//自动查LoginUrl

                 //除了匿名访问的资源,其它都要认证("authc")后访问

                 map.put("/**","user");//authc

                 sfBean.setFilterChainDefinitionMap(map);

                 return sfBean;

         }

1.7shiro会话时长配置

使用shiro框架实现认证操作,用户登陆成功会将用户信息写入到会话对象中,其默认时长为30分钟,假如需要对此进行配置,可以参考如下代码:
第一步: 在SpringShiroConfig类中,添加会话管理器配置:
@Bean  

public SessionManager sessionManager() {

                 DefaultWebSessionManager sManager=

                                 new DefaultWebSessionManager();

                 sManager.setGlobalSessionTimeout(60*60*1000);

                 return sManager;

}
第二步: 在SpringShiroConfig配置类中,对安全管理器 securityManager增加sessionManager值的注入:
@Bean

public SecurityManager securityManager(

                        Realm realm,CacheManager cacheManager,

RememberMeManager rememberManager,

SessionManager sessionManager) {

                 DefaultWebSecurityManager sManager=

                 new DefaultWebSecurityManager();

                 sManager.setRealm(realm);

                 sManager.setCacheManager(cacheManager);

                 sManager.setRememberMeManager(rememberMeManager);

                 sManager.setSessionManager(sessionManager);

                 return sManager;

}
查看原文

赞 0 收藏 0 评论 0

BolunWu 发布了文章 · 2020-11-10

动吧旅游资源整合

1.Spring Boot核心特性

1.1Springboot是一个脚手架,构建于Spring框架(framwork)基础之上,基于快速构建理念,提供了自动配置功能,可实现其开箱即用特性(创建完一个基本的项目以后,可零配置或者少量配置即可运行我们的项目),其核心主要有如下几个方面:
起步依赖--创建项目时底层帮你关联依赖.
自动配置
健康检查
其中,Spring Boot官方网址为:https://spring.io/projects/spring-boot

2.常见的连接池有哪些?

DBCP C3P0 DRUID HikariCP等.

3.整合HikariCP连接池

1.添加依赖
#1.mysql数据库驱动依赖
<dependency>

        <groupId>mysql</groupId>

        <artifactId>mysql-connector-java</artifactId>

        <scope>runtime</scope>

</dependency>
#2.spring对象jdbc支持(此时会默认帮我们下载HikariCP连接池)
<dependency>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>
2.配置连接池
打开application.properties配置文件,添加如下内容
spring.datasource.url=jdbc:mysql:///dbgoods?serverTimezone=GMT%2B8&characterEncoding=utf8

spring.datasource.username=root

spring.datasource.password=root

3.单元测试(测试包中编写)
package com.cy.pj.common.datasource;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest

public class DataSourceTests {

  @Autowired

        private DataSource dataSource;

        

  @Test

        public void testConnection() throws Exception{

                System.out.println(dataSource.getConnection());

        }

   }

4.整合MyBatis框架

MyBatis是一个优秀的持久层框架,底层基于JDBC实现数据交互.并在JDBC操作的基础上做了封装和优化,他借助灵活的SQL定制,参数及结果集的映射方式,更好的适应了当前互联网技术的发展.
1.添加mybatis启动依赖
   <dependency>

                        <groupId>org.mybatis.spring.boot</groupId>

                        <artifactId>mybatis-spring-boot-starter</artifactId>

                        <version>2.1.1</version>

        </dependency>
2.Mybatis简易配置(applicaion.properties)

mybatis.configuration.default-statement-timeout=30

mybatis.configuration.map-underscore-to-camel-case=true

5.整合SpringMVC应用

MVC是软件工程中的一种软件架构模式,基于此模式把软件系统分为三个基本部分,模型(Model) 视图(View) 控制器(Controller)目的是通过这样的设计使程序更加简洁,直观,降低问题的复杂度.其中各个组成部分的职责为:
模型(Model)-实现业务逻辑和数据逻辑
视图(View)-UI设计人员进行图形界面设计,负责实现与用户进行交互.
控制器(Controller)-负责获取请求,处理请求,响应结果.
核心组件
DispatcherServlet:前端控制器,处理请求的入口.
HandlerMapping:映射器对象,用于管理url与对应controller的映射关系.
Interceptors:拦截器,实现请求响应的共性处理.
Controller:后端控制器-handler,负责处理请求的控制逻辑.
ViewResolver:视图解析器,解析对应的视图关系(前缀+viewname+后缀).
1.添加springmvc依赖
编辑pom.xml文件,添加spring-web依赖,Thymeleaf依赖,代码如下:
Web依赖(提供了Spring MVC核心API,同时会嵌入一个Tomcat服务器)
<dependency>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-starter-web</artifactId>

</dependency>

Thymeleaf依赖(提供了一个视图解析器对象以及数据绑定机制)
<dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-thymeleaf</artifactId>~~~~

</dependency>

2.配置SpringMVC核心对象(application.properties)
spring.thymeleaf.prefix=classpath:/templates/pages/

spring.thymeleaf.suffix=.html

6.健康检查配置

1.添加依赖
项目中添加健康检查依赖:

<dependency>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-starter-actuator</artifactId>

</dependency>
2.修改application.properties文件
management.endpoints.web.exposure.include=*
3.在Google浏览器中下载json View插件

7.热部署的配置

基于SpringBoot的Web项目,在修改le某个类以后,默认不会再自动部署和加载,需要为我们手动重启服务器.假如我们希望自动部署,可以添加热部署依赖.
<dependency>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-devtools</artifactId>

                <scope>runtime</scope>

</dependency>

查看原文

赞 0 收藏 0 评论 0

BolunWu 关注了用户 · 2020-11-09

廾匸 @s8_5f4486e4c4351

关注 3

BolunWu 关注了用户 · 2020-11-09

吴欣赏 @wuxinshang

关注 1

BolunWu 发布了文章 · 2020-11-07

京淘项目知识点总结

1.restFul

业务需求:

实现用户的页面跳转
url:http://localhost:8091/page/item-add 页面:item-add
http://localhost:8091/page/item-list 页面:item-list
能否利用一个方法来实现通用页面的跳转功能
如果能动态获取url中的参数就可以实现页面的跳转.restFul风格...

1.1restFul语法:

1.参数必须使用"/"分隔;
2.参数必须使用{}形式包裹
3.参数接收时必须使用@PathVariable 获取

1.2restFul风格用法

利用不同的请求类型,定义不用的业务功能
type="GET" 查询业务
type="POST" 新增操作
type="PUT" 更新操作
type="DELETE"删除操作

2.反向代理

业务需求:当完成文件上传时,业务返回页面的是虚拟地址路径

url地址: http://image.jt.com/2020/09/30/a.jpg
真实图片地址: file:///D:/JT-SOFT/image/2020/09/30/d534bed912c748b0ac979ee40222490a.jpg
问题: 如何让用户通过url访问 找到真实的磁盘地址的图片.

2.1反向代理机制

2.1.1反向代理机制介绍

反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源.同时,用户不需要知道目标服务器的地址,也无需在用户端作任何设定.反向代理服务器通常可以用来做WEB加速,即用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。
概括
1.位于用户(客户端)-服务器之间.
2.用户访问反向代理服务器,以为是真是的服务器.
3.用户根本不清楚真实的服务器信息到底是谁.
4.一般反向代理机制保护了真实的服务器信息,所以称之为服务器端代理.

image

2.2正向代理机制

2.2.1正向代理介绍

正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。
总结
1.正向代理位于客户与服务器之间.
2.客户端在发起请求之前 确定了目标服务器的地址.
3.服务器不清楚到底是哪台客户端访问的我,以为只是路由器访问的.~~~~
4.正向代理保护了客户的信息,所以也称之为 客户端代理.
查看原文

赞 0 收藏 0 评论 0

认证与成就

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

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-08-25
个人主页被 914 人浏览