配置或常用代码

java后端

jdbc

jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/x_admin?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=1507298022
获取连接/关闭资源
// 需要fastjson包
public class JDBCUtil {
    private static final ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
    private static final String driver = bundle.getString("driver");
    private static final String url = bundle.getString("url");
    private static final String user = bundle.getString("username");
    private static final String password = bundle.getString("password");

    private static final ThreadLocal<Connection> conns = new ThreadLocal<>();

    static {
        // 注册驱动
        try {
            Class.forName(driver);
//            System.out.println("注册驱动成功");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 创建连接
    public static Connection getConnection() throws SQLException {
        Connection connection = conns.get();
        if (connection == null){
            connection = DriverManager.getConnection(url, user, password);
            // 将事务改为手动提交
            connection.setAutoCommit(false);
            conns.set(connection);
        }
        return connection;
    }

    // 关闭资源,事务提交
    public static void close(){
        // 获取操作数据的当前连接
        Connection connection = conns.get();
        if (connection != null){
            try {
                // 提交事务
                connection.commit();
            } catch (Exception e) {
                try {
                    // 回滚数据
                    connection.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        // 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)
        conns.remove();
    }

}
执行sql

// 需要 commons-dbutils依赖

<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.7</version>
</dependency>
/**
     * 查询所有
     * @param sql sql语句
     * @param condition 条件
     * @param returnType 用于接收结果的实体类的class
     * @return 影响条数
     */
protected List<T> query(String sql, List<Object> condition, Class<T> returnType){
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    ResultSet resultSet = null;
    try {
        // 创建连接
        connection = JDBCUtil.getConnection();
        // 加载sql
        preparedStatement = connection.prepareStatement(sql);
        // 传值
        for (int i = 0; i < condition.size(); i++) {
            preparedStatement.setObject(i + 1, condition.get(i));
        }
        // 执行sql
        resultSet = preparedStatement.executeQuery();

        // 处理结果集
        BeanListHandler<T> bh = new BeanListHandler<T>(returnType);
        return bh.handle(resultSet);

    } catch (SQLException e) {
        e.printStackTrace();
    }finally {
        // 关闭资源
        JDBCUtil.close();
    }
    return null;
}

protected Integer update(String sql, List<Object> condition){
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = JDBCUtil.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < condition.size(); i++) {
            preparedStatement.setObject(i + 1, condition.get(i));
        }
        // 执行sql,可以是增删改操作
        return preparedStatement.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtil.close();
    }
    return 0;
}

mybatis

mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--mybatis的核心配置文件-->
<configuration>
    <!--引入外部文件-->
    <properties resource="jdbc.properties"/>

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/> <!--日志-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>  <!--驼峰命名-->
    </settings>

    <typeAliases>
        <!--方式二,通过引用包,直接使用类名调用,若实体类有注解@Alias("xxx"),则使用xxx调用-->
        <package name="com.feng.entity"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个xxxMapper.xml文件都必须要配置到核心配置文件中-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>
获取连接/关闭资源
public class MybatisUtil {
    private static final ThreadLocal<SqlSession> conns = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory;

    // 只需要一个工厂对象
    static {
        try {
            // 拿到SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession() {
        SqlSession sqlSession = conns.get();
        if (sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            conns.set(sqlSession);
            return sqlSession;
        }
        return sqlSession;
    }

    public static void closeSqlSession(){
        SqlSession sqlSession = conns.get();
        if (sqlSession != null){
            try{
                sqlSession.commit();    // 提交事务
            }catch (Exception e){
                sqlSession.rollback();    // 回滚
            }finally {
                sqlSession.close();    // 关闭连接,可以不用关闭 或 者在数据库设置关闭时间
            }
            conns.remove();
        }
    }
}
资源导出失败问题
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

Tool

统一返回结果封装
public class ResultVo<T> {

    private String msg;
    private Integer code;
    private Integer count;
    private T data;

    public static <T> ResultVo<T> success(T data){
        return new ResultVo<T>(StatusCodeEnum.SUCCESS.getMsg(), StatusCodeEnum.SUCCESS.getCode(), 0, data);
    }
    public static <T> ResultVo<T> failed() {
        return new ResultVo<T>(StatusCodeEnum.FAILED.getMsg(), StatusCodeEnum.FAILED.getCode(), 0, null);
    }
    public static <T> ResultVo<T> failed(T data) {
        return new ResultVo<T>(StatusCodeEnum.FAILED.getMsg(), StatusCodeEnum.FAILED.getCode(), 0, data);
    }
    public ResultVo<T> message(String msg){
        this.setMsg(msg);
        return this;
    }
    public ResultVo<T> code(Integer code){
        this.setCode(code);
        return this;
    }
    public ResultVo<T> count(Integer count){
        this.count = count;
        return this;
    }
}
md5加密
public static String md5(String str){
    MessageDigest md5 = null;
    try {
        md5 = MessageDigest.getInstance("MD5");
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(md5.digest(str.getBytes(StandardCharsets.UTF_8)));
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    // 123456
    return "4QrcOUm6Wau+VuBX8g+IPg==";
}
复制集合
public static <S, T> List<T> copyListProperties(List<S> sources, Class<T> targetClass) {
    String old = JSON.toJSONString(sources);
    return JSON.parseArray(old, targetClass);
}
获取图片的md5值
public static String getFileMD5(File file) {
        if (!file.isFile()) {
            return null;
        }
        // 创建MessageDigest对象,添加MD5处理
        MessageDigest digest = null;
        FileInputStream in = null;
        byte[] buffer = new byte[1024];
        int len;
        try {
            digest = MessageDigest.getInstance("MD5");
            // 读取图片
            in = new FileInputStream(file);
            while ((len = in.read(buffer, 0, 1024)) != -1) {
                digest.update(buffer, 0, len);
            }
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        BigInteger bigInt = new BigInteger(1, digest.digest());
        System.out.println(bigInt);
        // 返回16进制表示形式
        return bigInt.toString(16);
    }

逆向工程代码生成器

数据库信息 database.properties

db.url=jdbc:mysql://localhost:3306/nursing_homes
db.username=root
db.password=1507298022
g.output.dir=.\\src\\main\\java
pkg.name=com
pkg.xml.dir=mapper

代码生成工具类 AutoCodeUtil

public class AutoCodeUtil {

    private static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }

    public static void main(String[] args) {
        ResourceBundle res = ResourceBundle.getBundle("database");
        FastAutoGenerator.create(res.getString("db.url"), res.getString("db.username"), res.getString("db.password"))
                // 全局配置
                .globalConfig((scanner, builder) -> builder
                        .author(scanner.apply("请输入作者名称:"))
                        .fileOverride()     //允许重新文件
                        .disableOpenDir()   //禁止生成成功打开文件夹
                        .outputDir(res.getString("g.output.dir")) //设置输出路径
                )
                // 包配置
                .packageConfig((scanner, builder) -> builder
                        .parent(res.getString("pkg.name"))
                        .moduleName(scanner.apply("输入模块名称:"))
                        .pathInfo(Collections.singletonMap(OutputFile.mapperXml, res.getString("g.output.dir") + "\\..\\resources\\" + res.getString("pkg.xml.dir")))
                )
                // 策略配置
                .strategyConfig((scanner, builder) -> builder
                        .addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔,所有输入all:")))
                        .controllerBuilder().enableRestStyle().enableHyphenStyle()
                        .mapperBuilder().enableMapperAnnotation()
                        .entityBuilder().enableLombok().addTableFills(new Column("create_time", FieldFill.INSERT))
                        .build()
                )
                .execute();
    }
}

spring

mybatis配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置整合mybatis-->
    <!--关联数据库文件: 读取配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties, classpath:redis.properties"/>

    <!--数据库连接池: 这里使用c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--配置属性-->
        <property name="driverClass" value="${db.driver}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="user" value="${db.username}"/>
        <property name="password" value="${db.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!--配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据库连接池-->
        <property name="dataSource" ref="dataSource"/>
        <!--整合mybatis-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--配置扫描dao包, 动态实现dao接口注入spring容器  就可以不用去实现dao的接口了-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--注入sqlSessionFactory-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--给出需要扫描Dao接口包-->
        <property name="basePackage" value="com.feng.dao"/>
    </bean>
</beans>

springmvc

基本配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启mvc注解驱动-->
    <mvc:annotation-driven/>
    <!--静态资源默认配置-->
    <mvc:default-servlet-handler/>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--后缀-->
        <property name="suffix" value=".html"/>
        <!--前缀-->
        <!--        <property name="prefix" value="/WEB-INF/jsp/"/>-->
    </bean>

    <!--扫描controller包下的bean-->
    <context:component-scan base-package="com.feng.controller"/>
    <!--让全局异常处理注解生效-->
    <context:component-scan base-package="com.feng.exception"/>
    <context:component-scan base-package="com.feng.util"/>

    <!--关于拦截器的配置-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--对哪些资源执行拦截操作
            <mvc:mapping path="/**"/>,
            mapping表示要拦截,/**代表所有的请求路径,故拦截所有的请求路径。
            -->
            <mvc:mapping path="/**"/>
            <!--不拦截哪些资源-->
            <mvc:exclude-mapping path="/css/**"/>
            <mvc:exclude-mapping path="/front/**"/>
            <mvc:exclude-mapping path="/html/**"/>
            <mvc:exclude-mapping path="/js/**"/>
            <mvc:exclude-mapping path="/upload/**"/>
            <bean class="com.feng.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

    <!--    文件上传配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--上传文件总大小, 单位为字节.-->
        <property name="maxUploadSize" value="512000000"/>
        <!--上传单个文件的大小, 单位为 字节-->
<!--        <property name="maxUploadSizePerFile" value="5242800"/>-->
        <!-- 设置请求的编码格式, 默认为iso-8859-1 -->
        <property name="defaultEncoding" value="utf-8"/>
        <!-- 设置上传文件的临时路径 -->
<!--        <property name="uploadTempDir" value="upload"/>-->
    </bean>

    <!--解决json响应乱码-->
    <!--解决相应乱码-->
    <mvc:annotation-driven>
        <!--//响应类型转换器,可配置多个-->
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!--//相当于在servlet中写的response.setContentType("text/html;charset=utf-8")-->
                        <value>text/plain;charset=UTF-8</value>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
</beans>
处理乱码

post请求乱码:web.xml文件里配置

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

相应乱码:在核心配置文件中配置

<mvc:annotation-driven>
    <mvc:message-converters>  <!--//响应类型转换器,可配置多个-->
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <!--//相当于在servlet中写的response.setContentType("text/html;charset=utf-8")-->
                    <value>text/plain;charset=UTF-8</value>
                    <value>text/html;charset=UTF-8</value>
                    <value>application/json;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

springboot

Logback

logback.xml:日志的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <!-- 日志文件存放路径(日志目录) -->
    <property name="PATH" value="log"/>
    <!-- 日志文件的相关配置   -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${PATH}/spring.log</file>
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${PATH}/spring.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <maxFileSize>100MB</maxFileSize><!-- 单个文件最大100MB -->
            <maxHistory>30</maxHistory><!-- 最多保存30天的纪录-->
            <totalSizeCap>3GB</totalSizeCap><!-- 日志最大总量3GB-->
        </rollingPolicy>
        <encoder>
            <!--格式化输出:%d表示日期,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%logger:%line] --%mdc{client} %msg%n</pattern>
        </encoder>
    </appender>
    <!--    控制台输出的样式-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%date{yyyy-MM-dd HH:mm:ss}] [%-5level] [%logger:%line] --%mdc{client} %msg%n</pattern>
        </encoder>
    </appender>
    <!--这个表示指定某个包下的日志级别,需要改成自己的包 -->
    <logger name="com.feng" level="DEBUG"/>

    <!-- 默认 -->
    <root>
        <level value="INFO" />
        <appender-ref ref="FILE"/>
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>
swagger
3.x的版本
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

config

// 还需要在主启动类上加上 @EnableWebMvc
@Configuration
@EnableSwagger2    // 开启Swagger
public class Knife4jConfig extends WebMvcConfigurationSupport {
    @Bean(value = "dockerBean")
    public Docket dockerBean() {
        //指定使用Swagger2规范
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                //分组名称
                .groupName("用户服务")
                .select()
                //这里指定Controller扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.feng.controller"))
                // 过滤哪些
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("Knife4j 接口文档")
                // 支持md语法
                .description("# Knife4j RESTful APIs")
                .termsOfServiceUrl("https://doc.xiaominfo.com/")
                .contact(new Contact("丰","xiaoymin@foxmail.com", "xiaoymin@foxmail.com"))
                .version("1.0")
                .build();
    }

    // 过滤资源,防止资源找不到问题
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
}

knife4j的增强配置

knife4j:
  # 开启增强配置
  enable: true
  # 开启生产环境屏蔽
  production: false
  # 开启登录认证
  basic:
    enable: false
    username: admin
    password: 123456
4.x的版本

不需要再写配置类了,全部的配置写到yaml文件中

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.0.0</version>
</dependency>

增强配置

knife4j:
  enable: true
  openapi:
    title: Knife4j官方文档
    description: "`Logback,Swagger,Redis`,**学习**
    # aaa"
    email: 1507298022@qq.com
    concat:url: https://gitee.com/BiBifeng
    version: v4.0
    license: Apache 2.0
    license-url: https://stackoverflow.com/
    terms-of-service-url: https://stackoverflow.com/
    group:
      test1:
        group-name: 分组名称
        api-rule: package
        api-rule-resources:
          - com.feng.controller
Redis

自定义RedisTemplate

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    // 设置常规key value 的序列化策略
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    // 这里使用一般的json处理,就不容易存在兼容性问题。否则可能需要对应的json才能解析序列化的数据
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    // 设置hash类型的序列化策略
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
    // 注入连接工厂
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    return redisTemplate;
}

yaml配置

# 数据库索引(默认为0)
spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    # password: 123456
    timeout: 30000
    # 建议使用lettuce 可以换成jedis,spring默认集成lettuce
    client-type: lettuce
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 8
        # 最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1
        # 最大空闲连接数
        max-idle: 8
        # 最小空闲连接
        min-idle: 0
rabbitMq

定义RabbitTemplate,ConfirmCallback,ReturnsCallback

// 定义一个交换机名称
private final String TOPIC_EXCHANGE = "feng.exchanges.topic";
@Bean
RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    // 确认模式,需要重写确认回调函数
    rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause) -> {
        if (!ack){
            System.out.println("消息发送到交换机失败,ack=false" + cause);
            throw new RuntimeException(cause);
        }else {
            System.out.println("消息投递到exchange成功");
        }
    });

    rabbitTemplate.setReturnsCallback((ReturnedMessage returned) -> {
        System.out.println("消息发到队列失败,请检查交换机或路由:" + returned);
        throw new RuntimeException("消息发到队列失败,请检查交换机或路由");
    });
    // 设置消息转换成json格式,这个配置不是必须
    rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
    // 设置应答时长
    rabbitTemplate.setReplyTimeout(6000);
    return rabbitTemplate;
}

// 创建交换机
@Bean
public TopicExchange topicExchange(){
    // 参数:交换机名称,是否持久化,是否自动删除
    return new TopicExchange(TOPIC_EXCHANGE, true, false);
}

// 创建队列
@Bean
public Queue studentQueue(){
    return new Queue("feng.students", true);
}

// 创建连接(Binding)
// 话题交换节与学生队列绑定
@Bean
public Binding topicExchangeToStudentQueueBinding(){
    return BindingBuilder
            .bind(studentQueue())
            .to(topicExchange())
            .with("feng.student.*");
}

yaml配置

spring:
  rabbitmq:
    port: 5672
    host: 127.0.0.1
    username: guest
    password: guest
    # 这个配置是保证提供者确保消息推送到交换机中,不管成不成功,都会回调
    publisher-confirm-type: correlated
    # 开启return模式
    publisher-returns: true
    template:
      mandatory: true
邮件发送
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
spring:
  application:
    name: java-xadmin-spring-boot

  mail:
    host: smtp.163.com
    port: 25
    username: qq1507298022@163.com
    password: 
    #其他参数
    properties:
      mail:
        #配置SSL 加密工厂
        smtp:
          ssl:
            #本地测试,先放开ssl
            enable: false
            required: false
          #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
        debug: true
@Autowired
private JavaMailSenderImpl javaMailSender;

@Value("${spring.mail.username}")
private String sendMailer;

public void sendTextMailMessage(String to,String subject,String text){
    try {
        //true 代表支持复杂的类型
        MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(javaMailSender.createMimeMessage(),true);
        //邮件发信人
        mimeMessageHelper.setFrom(sendMailer);
        //邮件收信人  1或多个
        mimeMessageHelper.setTo(to.split(","));
        //邮件主题
        mimeMessageHelper.setSubject(subject);
        //邮件内容
        mimeMessageHelper.setText(text);
        //邮件发送时间
        mimeMessageHelper.setSentDate(new Date());
        //发送邮件
        javaMailSender.send(mimeMessageHelper.getMimeMessage());
        System.out.println("发送邮件成功:"+sendMailer+"->"+to);
    } catch (MessagingException e) {
        e.printStackTrace();
        System.out.println("发送邮件失败:"+e.getMessage());
    }
}
elasticsearch

插入数据

List<ProductDto> productDtos = productService.selectAll();
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
for (int i = 0; i < productDtos.size(); i++) {
    bulkRequest.add(
            new IndexRequest("product_doc")
                    .id(productDtos.get(i).getId() + "")
                    .source(JSON.toJSONString(productDtos.get(i)), XContentType.JSON)
    );
}
BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);

查询

/**
     * @param condition 分页、查询条件
     * @param doc 指定文档
     * @param target 需要高亮的字段
     * @param clazz 分装的实体类
     */
private ResultVo<List<T>> getUserDoc(PageVo condition, String doc, String target, Class<T> clazz) throws IOException {
    SearchRequest searchRequest = new SearchRequest(doc);
    // 构建搜索条件
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
    // 高亮  ,以哪个字段里面构建高亮
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field(target);
    highlightBuilder.requireFieldMatch(false);
    highlightBuilder.preTags("<span  style='color:red'>");
    highlightBuilder.postTags("</span>");
    // 将高亮的条件放到   搜索条件对象里面
    sourceBuilder.highlighter(highlightBuilder);

    if (condition.getSearch() != null){
        MatchQueryBuilder termQueryBuilder = QueryBuilders.matchQuery(target, condition.getSearch());
        // 添加延迟时间
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        // 将精确查询的对象放到   搜索对象里面
        sourceBuilder.query(termQueryBuilder);
    }else {
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(matchAllQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    }
    //分页查询
    sourceBuilder.from((condition.getPage() - 1) * condition.getLimit());
    sourceBuilder.size(condition.getLimit());
    // 放到数据库对象里面
    searchRequest.source(sourceBuilder);
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    //        解析搜索出来的数据,变为前端的格式,
    List<T> list = new ArrayList<>();
    for(SearchHit documentFields :searchResponse.getHits().getHits()){
        //            解析高亮的字段
        Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
        Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();

        HighlightField title = highlightFields.get(target);
        //            得到原来的字段
        if(title != null){
            Text[] fragments = title.fragments();  // 得到高亮字段的值
            String n_title = "";
            for (Text text:fragments){
                n_title += text;
            }
            sourceAsMap.put(target, n_title);//替换原来的字段
        }

        T t = JSON.parseObject(JSON.toJSONString(sourceAsMap), clazz);
        list.add(t);
    }
    return ResultVo.success(list).code(0).count(list.size());
}
jwt+security

授权

// 自定义的登录处理
@Autowired
private MyUserDetailsService userDetailsService;
// Taken过滤器
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/index","/login", "/back/user/login").permitAll()// index 放行
            .antMatchers(HttpMethod.OPTIONS).permitAll()   //options 方法的请求放行
			// 配置权限
            .antMatchers("/back/product/**", "/back/category/**", "/back/file/**").hasAuthority("feng:product")
            .anyRequest().authenticated()

            .and()
            .formLogin()
            .defaultSuccessUrl("/html/index.html", true)
            .loginPage("/html/login.html")   // 登录访问的路径
            .loginProcessingUrl("/login")     // 登录时,发起请求的地址,使用系统默认的

            .and()
            .csrf().disable()

            .sessionManagement()//允许配置会话管理
            //Spring Security永远不会创建一个HttpSession,也永远不会使用它获取SecurityContext
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            .and()
            .userDetailsService(userDetailsService);   // 使用自己的UserDetailsService;
    http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}

jwtFilter

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    // 使用自己的 userDetailService
    @Autowired
    private MyUserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Value("${jwt.tokenHeader}")//Authorization
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;//bearer

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        //从 header  中获取 Authorization
        String authHeader = request.getHeader(tokenHeader);
        // 访问没有资源,就放掉
        if (authHeader == null) {
            chain.doFilter(request, response);
            return;
        }
        // 判断 authHeader  不为空  并且以 bearer 开头
        boolean b1 = StringUtils.startsWithIgnoreCase(authHeader,this.tokenHead);
        if (!b1) {
            chain.doFilter(request, response);
            return;
        }
        //截取 bearer 后面的字符串  并且 两端去空格(获取token)
        String authToken = authHeader.substring(this.tokenHead.length()).trim();// The part after "Bearer "
        // 解析taken获取到用户名
        String username = jwtTokenUtil.getUserNameFromToken(authToken);
        // 用户名不为空  并且SecurityContextHolder.getContext()  存储 权限的容器中没有相关权限则继续
        boolean isNotAuthentication = SecurityContextHolder.getContext().getAuthentication() == null;
        if (username != null && isNotAuthentication) {
            //从数据库读取用户信息
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            //校验token
            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                // 一个实现的带用户名和密码以及权限的Authentication(spring 自带的类)
                UsernamePasswordAuthenticationToken authentication = null;
                authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                // 从HttpServletRequest 对象,创建一个WebAuthenticationDetails对象
                WebAuthenticationDetails details = new WebAuthenticationDetailsSource().buildDetails(request);
                //设置details
                authentication.setDetails(details);
                //存入本线程的安全容器   在访问接口拿到返回值后 要去主动清除 权限,避免干扰其他的线程
                //SecurityContextHolder会把authentication放入到session里,供后面使用
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

JwtTokenUtil

@Component
public class JwtTokenUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";
    @Value("${jwt.secret}")
    private String secret;// 盐
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     * 根据 负载(用户名 部门 权限 等) 生成JWT的token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    /**
     * 从token中获取JWT中的负载
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.info("JWT格式验证失败:{}",token);
        }
        return claims;
    }
    /**
     * 生成token的过期时间   30秒过期
     */
    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }
    /**
     * 从token中获取登录用户名
     */
    public String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username =  claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    /**
     * 验证token是否还有效
     * @param token       客户端传入的token
     * @param userDetails 从数据库中查询出来的用户信息
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }
    /**
     * 判断token是否已经失效
     */
    private boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }
    /**
     * 从token中获取过期时间
     */
    private Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }
    /**
     * 根据用户信息生成token
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, username);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }
    /**
     * 判断token是否可以被刷新
     */
    public boolean canRefresh(String token) {
        return !isTokenExpired(token);
    }
    /**
     * 刷新token
     */
    public String refreshToken(String token) {
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }
跨域配置
@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域
        config.addAllowedOrigin("*");
        //是否发送 Cookie
        config.setAllowCredentials(true);
        //放行哪些请求方式
        config.addAllowedMethod("*");
        //放行哪些原始请求头部信息
        config.addAllowedHeader("*");
        //暴露哪些头部信息
        config.addExposedHeader("*");
        //2. 添加映射路径
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);
        //3. 返回新的CorsFilter
        return new CorsFilter(corsConfigurationSource);
    }
}

前端

js

ajax请求
function myAjax(url, data, type){
    $.ajax({
    url: url,
    data: data,
    type: type,
    dataType: "json",
    contentType: "application/json;charset=UTF-8",
    headers: {'Accept': 'application/json', 'Authorization': sessionStorage.getItem("token")},
    success: function (res) {
        if(res.code === 200){
            // 将返回的数据封装到session中
            sessionStorage.setItem("username", res.data.username)
            location.href = "html/index.html"
        }else {
            layer.msg('用户名或者密码错误');
        }
    },
    error: function (data) {}   // 错误回调
})
}

显示动态时间
$(function (){
    setInterval(function (){
        let date = new Date().Format("yyyy-MM-dd HH:mm:ss");
        $("#loginTime").html(date)
    }, 1000)

})

Date.prototype.Format = function (fmt) {
    var o = {
        "M+": this.getMonth() + 1, //月份
        "d+": this.getDate(), //日
        "H+": this.getHours(), //小时
        "m+": this.getMinutes(), //分
        "s+": this.getSeconds(), //秒
        "q+": Math.floor((this.getMonth() + 3) / 3), //季度
        "S": this.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}

layui

监听按键
layui.use('form', function () {
    let form = layui.form;
    // login对应按键的 属性  lay-filter="login"
    form.on('submit(login)', function (data) {
        layer.msg('有表情地提示', {icon: 6});    // 提示框
    });
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值