前后端分离开发
SSM整合环境搭建(Axios异步请求——前后端分离)以员工管理为例
一、maven聚合工程的搭建
1、创建父工程
2、删除父工程中的src文件夹,右键工程名,新建子工程
重复上述步骤创建其他子工程,如若出现pom.xml 变灰 出现删除线,请参考BUG- IDEA Maven pom.xml 变灰 出现删除线
3、修改子工程employee_web,将其变成web工程
3.1、选择employee_web添加web
修改成功后如图
3.2、更改打包方式为war
二、maven的父工程做版本管理
给相应的子工程添加依赖,以employee_common的pom.xml为例
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>employee-parent</artifactId>
<groupId>com.shangma.cn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>employee_common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
添加依赖时,使用父工程管理依赖的版本
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.shangma.cn</groupId>
<artifactId>employee_root</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>employee_common</module>
<module>employee_entity</module>
<module>employee_mapper</module>
<module>employee_service</module>
<module>employee_web</module>
</modules>
<properties>
<lombok-version>1.18.12</lombok-version>
<mybatis-version>3.5.4</mybatis-version>
<mysql-version>5.1.47</mysql-version>
<druid-version>1.1.22</druid-version>
<spring-version>5.1.9.RELEASE</spring-version>
<mybatis-spring-version>2.0.3</mybatis-spring-version>
<log4j-version>1.2.17</log4j-version>
<aspectjweaver-version>1.9.5</aspectjweaver-version>
<jackson-databind-version>2.10.2</jackson-databind-version>
<dysmsapi20170525-version>2.0.4</dysmsapi20170525-version>
<spring-data-redis-version>2.1.3.RELEASE</spring-data-redis-version>
<jedis-version>2.9.1</jedis-version>
<slf4j-simple-version>1.7.30</slf4j-simple-version>
<pagehelper-version>5.1.10</pagehelper-version>
<aliyun-sdk-oss-version>3.10.2</aliyun-sdk-oss-version>
</properties>
<!-- 版本控制-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis-version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind-version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${dysmsapi20170525-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring-data-redis-version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j-simple-version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper-version}</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-sdk-oss-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
三、maven的配置类加注解的方式进行SSM整合
在Mapper层和Service层配置Spring,在web层配置Spring MVC和web.xml
1、MpperConfig类(spring)
1.1、加载外部配置文件(没写)
1.2、配置数据源
1.3、配置SQLSessionFactoryBean
1.4、配置MapperScanConfiguar
package com.shangma.cn.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MapperConfig {
/**
* 配置数据源
* @return
*/
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource =new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mavenssm");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
/**
* 配置SQLSessionFactory,加载mybatis配置文件
* @return
*/
@Bean
public SqlSessionFactoryBean sessionFactory(){
SqlSessionFactoryBean sessionFactory=new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
return sessionFactory;
}
/**
* 配置mapper扫描
* @return
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.shangma.cn.mapper");
return mapperScannerConfigurer;
}
}
2、ServiceConfig类(spring)
2.1、配置包扫描
2.2、配置事务管理器
2.3、配置事务驱动
package com.shangma.cn.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @author: JAVASM
* @className: ServiceConfig
* @description:
* @date: 2021/6/14 13:03
* @version: 0.1
* @since: jdk1.8
*/
@Configuration
@ComponentScan(basePackages = {"com.shangma.cn.service"})//包扫描
@EnableTransactionManagement//开启事务注解驱动
public class ServiceConfig {
@Autowired
private DruidDataSource dataSource;
/**
* 事务管理器
* @return
*/
@Bean
public DataSourceTransactionManager transactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
3、WebConfig类(spring mvc)
3.1、配置包扫描
3.2、配置mvc的注解驱动
package com.shangma.cn.config;
import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author: JAVASM
* @className: WebConfig
* @description:
* @date: 2021/6/14 13:04
* @version: 0.1
* @since: jdk1.8
*/
@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {
}
4、MyWebApplnitalizer类(web.xml)
4.1、加载spring的配置类
4.2、加载spring mvc的配置类
4.3、设置开启文件上传
4.4、设置Filter解决乱码问题(没写)
package com.shangma.cn;
import com.shangma.cn.config.MapperConfig;
import com.shangma.cn.config.ServiceConfig;
import com.shangma.cn.config.WebConfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import javax.servlet.Filter;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration;
/**
* @author: JAVASM
* @className: MyWebAppInitializer
* @description:
* @date: 2021/6/14 19:16
* @version: 0.1
* @since: jdk1.8
*/
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 加载父容器的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{MapperConfig.class, ServiceConfig.class};
}
/**
* 加载子容器的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 表示设置DispatcherServlet路径
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 文件上传
* @param registration
*/
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement(""));
}
}
四、逆向工程生成和mybatis的日志打印
使用逆向工程,能够一键生成entity层和mapper层的代码,CV大法到相应的位置
因为使用的是配置类的方式进行SSM的整合,所以没有了配置文件,因此在配置SQLSessionFactoryBean时并没有对mybatis的日志打印进行设置
配置文件在配置类中都有对应的写法,导入log4j的配置文件,修改后的MpperConfig类如下:
package com.shangma.cn.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.logging.log4j.Log4jImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: JAVASM
* @className: MapperConfig
* @description:
* @date: 2021/6/14 13:02
* @version: 0.1
* @since: jdk1.8
*/
@Configuration
public class MapperConfig {
/**
* 配置数据源
* @return
*/
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource =new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mavenssm");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
/**
* 配置SQLSessionFactory,加载mybatis配置文件
* @return
*/
@Bean
public SqlSessionFactoryBean sessionFactory(){
SqlSessionFactoryBean sessionFactory=new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
//设置mybatis的日志
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setLogImpl(Log4jImpl.class);
sessionFactory.setConfiguration(configuration);
return sessionFactory;
}
/**
* 配置mapper扫描
* @return
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.shangma.cn.mapper");
return mapperScannerConfigurer;
}
}
五、发送手机验证码
package com.shangma.cn.controller;
import com.shangma.cn.common.AxiosResult;
import com.shangma.cn.common.AxiosStatus;
import com.shangma.cn.entity.Employee;
import com.shangma.cn.exception.MyLoginException;
import com.shangma.cn.factory.AsyncFactory;
import com.shangma.cn.pool.AsyncManager;
import com.shangma.cn.service.EmployeeService;
import com.shangma.cn.utils.RandomCodeUtil;
import com.shangma.cn.utils.SmsUtil;
import com.shangma.cn.utils.UploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author: JAVASM
* @className: CommonController
* @description:
* @date: 2021/6/14 19:15
* @version: 0.1
* @since: jdk1.8
*/
@RestController
@RequestMapping("common")
public class CommonController {
@Autowired
private EmployeeService employeeService;
@RequestMapping("getCode/{phone}")
public String getCode(@PathVariable String phone){
System.out.println(phone);
Employee employee = employeeService.findByPhone(phone);
System.out.println(employee);
if (employee==null) {
return "NOT_Employee";
}
//生成验证码
String code = RandomCodeUtil.munCode();
//发送短信,单独的Java不能实现发送手机验证码
//所以需要依赖于第三方的SDK,本案例使用的是阿里云所提供的服务
SmsUtil.sendSms(phone,code);
return "success";
}
}
六、使用异步线程池发送手机验证码
发送短信是一个很耗时间的操作,非常容易造成线程的阻塞,所以我们可以把发送这个操作发到子线程中,并且发送验证码这个操作过程复杂
需要 一定的代码量,而且耦合度高、使用的次数多,所以我们可以使用工厂模式降低代码的重复和解耦。
还有就是返回给前端的值,一般都会返回json形式的字符串,并且在前端用于条件判断的多是数字(状态码),因此,我们先设置好返回值类,定义好返回值(枚举类)
定义状态码
定义返回值类
package com.shangma.cn.common;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
@Data
public class AxiosResult<T> {
private int status;
private String message;
private T data;
/**
* 私有化构造函数
*/
private AxiosResult() {
}
/**
* 返回状态码不带数据
* @param <T>
* @return
*/
public static <T> AxiosResult<T> success() {
return setData(AxiosStatus.OK, null);
}
/**
* 返回状态码带数据
* @param t
* @param <T>
* @return
*/
public static <T> AxiosResult<T> success(T t) {
return setData(AxiosStatus.OK, t);
}
/**
* 返回别的类型的状态码不带数据
* @param axiosStatus
* @param <T>
* @return
*/
public static <T> AxiosResult<T> success(AxiosStatus axiosStatus) {
return setData(axiosStatus, null);
}
/**
* 返回别的类型的状态码带数据
* @param axiosStatus
* @param t
* @param <T>
* @return
*/
public static <T> AxiosResult<T> success(AxiosStatus axiosStatus, T t) {
return setData(axiosStatus, t);
}
public static <T> AxiosResult<T> error() {
return setData(AxiosStatus.ERROR, null);
}
public static <T> AxiosResult<T> error(AxiosStatus axiosStatus) {
return setData(axiosStatus, null);
}
public static <T> AxiosResult<T> error(AxiosStatus axiosStatus, T t) {
return setData(axiosStatus, t);
}
public static <T> AxiosResult<T> setData(AxiosStatus axiosStatus, T t) {
AxiosResult<T> tAxiosResult = new AxiosResult<>();
tAxiosResult.setStatus(axiosStatus.getStatus());
tAxiosResult.setMessage(axiosStatus.getMessage());
tAxiosResult.setData(t);
return tAxiosResult;
}
}
线程池
package com.shangma.cn.pool;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 使用单例模式设计异步线程池
*/
public class AsyncManager {
private static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private static AsyncManager asyncManager;
private AsyncManager (){
scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(50);
}
/**
* 创建单例对象
* @return
*/
public static AsyncManager getInstance(){
if (asyncManager==null) {
return new AsyncManager();
}
return asyncManager;
}
/**
* 使用线程池执行内容
* 这种执行没有延迟时间
*
* @param runnable
*/
public void execute(Runnable runnable) {
scheduledThreadPoolExecutor.execute(runnable);
}
/**
* 使用线程执行代码
* schedule方法可以延迟一段时间后再去执行Runnable中的代码
* @param runnable 要执行的代码
* @param seconds 延迟的时间
*/
public void schedule(Runnable runnable,long seconds){
scheduledThreadPoolExecutor.schedule(runnable,seconds, TimeUnit.SECONDS);
}
/**
* 关闭线程池
*/
public void release(){
scheduledThreadPoolExecutor.shutdownNow();
}
}
工厂类
package com.shangma.cn.factory;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.teaopenapi.models.Config;
import com.shangma.cn.utils.SmsUtil;
import lombok.extern.log4j.Log4j;
import java.io.IOException;
import java.util.Properties;
/**
* 异步工厂(使用工厂模式完成功能)
* 1、发送手机验证码
*/
@Log4j
public class AsyncFactory {
private static Properties properties;
static {
//加载配置文件
try {
properties = new Properties();
properties.load(SmsUtil.class.getClassLoader().getResourceAsStream("aliyun.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param phone
* @param code
* @return
*/
public static Runnable sendPhoneCode(String phone, String code){
Config config = new Config()
// 您的AccessKey ID
.setAccessKeyId(properties.getProperty("aliyun.accessKeyId"))
// 您的AccessKey Secret
.setAccessKeySecret(properties.getProperty("aliyun.accessKeySecret"));
// 访问的域名
config.endpoint = properties.getProperty("aliyun.endpoint");
try {
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(phone)
.setSignName(properties.getProperty("aliyun.signName"))
.setTemplateCode(properties.getProperty("aliyun.templateCode"))
.setTemplateParam("{\"code\":" + code + "}");
// 复制代码运行请自行打印 API 的返回值
Runnable runnable = ()->{
//当前线程名
System.out.println("当前线程为:"+Thread.currentThread().getName());
try {
client.sendSms(sendSmsRequest);
} catch (Exception e) {
e.printStackTrace();
}
};
return runnable;
} catch (Exception e) {
log.error(phone + "+手机验证码发送失败:" + e.getMessage());
e.printStackTrace();
}
return ()->{};
}
}
修改后的发送手机验证码
@RequestMapping("getCode/{phone}")
public AxiosResult<Void> getCode(@PathVariable String phone){
System.out.println(phone);
Employee employee = employeeService.findByPhone(phone);
System.out.println(employee);
if (employee==null) {
return AxiosResult.error();
}
//生成验证码
String code = String.valueOf((int) (Math.random() * (999999 - 100000 + 1) + 100000));
//发送短信,放入子线程中
AsyncManager.getInstance().schedule(AsyncFactory.sendPhoneCode(phone,code),2L);
return AxiosResult.success();
}
七、SpringDataRedis的使用
在进行登录验证的时候,我们不仅需要从前端传递过来的验证码,还需要我们自己生成的验证码进行对照,对于验证码我们不可能永久保存,若是放到MySQL中,不仅是存取麻烦,还有对数据库的大量操作会造成数据库的崩溃,这时就需要用到对临时数据的存储Redis数据库。
在spring mvc中对Redis的操作有两种,使用RedisTemplate操作Redis和使用StringRedisTemplate操作Redis,这两种无论是哪一种都需要Redis工厂的支持。
在WebConfig类中配置
package com.shangma.cn.config;
import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;
@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {
/**
* Redis工厂
* 如果是本机,可以什么都不配置
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
return jedisConnectionFactory;
}
/**
* 使用RedisTemplate操作Redis
* @return
*/
// @Bean
// public RedisTemplate redisTemplate(){
// RedisTemplate redisTemplate = new RedisTemplate();
// redisTemplate.setConnectionFactory(redisConnectionFactory());
// return redisTemplate;
// }
/**
* 使用StringRedisTemplate操作Redis
* @return
*/
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}
}
登录验证
//在发送验证码方法中添加
//在生成验证码之后
//生成验证码,存入Redis,设置2分钟自动销毁
stringRedisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);
/**
* 登录验证
* @param map
* @param session
* @return
*/
@RequestMapping("doLogin")
public AxiosResult<Void> doLogin(@RequestBody Map<String,String>map, HttpSession session){
System.out.println(map);
String phone = map.get("phone");
String code = map.get("code");
String s = stringRedisTemplate.opsForValue().get(phone);
if (s.equals(code)) {
Employee employee = employeeService.findByPhone(phone);
//将登陆信息放入session
session.setAttribute("user",employee);
//清除
stringRedisTemplate.delete(phone);
System.out.println(AxiosResult.success().getData());
return AxiosResult.success();
}
return AxiosResult.error();
}
八、Redis的连接池
在WebConfig类中配制
package com.shangma.cn.config;
import com.shangma.cn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import redis.clients.jedis.JedisPoolConfig;
/**
* @author: JAVASM
* @className: WebConfig
* @description:
* @date: 2021/6/14 13:04
* @version: 0.1
* @since: jdk1.8
*/
@ComponentScan(basePackages = {"com.shangma.cn.controller", "com.shangma.cn.exception"})
@EnableWebMvc//开启MVC注解驱动
@Configuration//加入容器
public class WebConfig implements WebMvcConfigurer {
/**
* jedis线程池
* @return
*/
@Bean
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(50);
jedisPoolConfig.setMaxIdle(50);
return jedisPoolConfig;
}
/**
* Redis工厂
* 如果是本机,可以什么都不配置
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig());
return jedisConnectionFactory;
}
/**
* 使用RedisTemplate操作Redis
* @return
*/
// @Bean
// public RedisTemplate redisTemplate(){
// RedisTemplate redisTemplate = new RedisTemplate();
// redisTemplate.setConnectionFactory(redisConnectionFactory());
// return redisTemplate;
// }
/**
* 使用StringRedisTemplate操作Redis
* @return
*/
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}
}
九、使用异常处理的方式来解决不满足条件的问题
异常可以阻止程序的执行,我们通过自定异常,在捕捉处理这个异常,就能达到解决不满足条件时程序的执行问题
定义异常
package com.shangma.cn.exception;
import com.shangma.cn.common.AxiosStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class MyLoginException extends RuntimeException{
private AxiosStatus axiosStatus;
}
捕捉处理异常
package com.shangma.cn.exception;
import com.shangma.cn.common.AxiosResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class MyHandler {
@ExceptionHandler(MyLoginException.class)
public AxiosResult<Void> myHandler(MyLoginException e){
return AxiosResult.error(e.getAxiosStatus());
}
}
完整版的登录
package com.shangma.cn.controller;
import com.shangma.cn.common.AxiosResult;
import com.shangma.cn.common.AxiosStatus;
import com.shangma.cn.entity.Employee;
import com.shangma.cn.exception.MyLoginException;
import com.shangma.cn.factory.AsyncFactory;
import com.shangma.cn.pool.AsyncManager;
import com.shangma.cn.service.EmployeeService;
import com.shangma.cn.utils.RandomCodeUtil;
import com.shangma.cn.utils.SmsUtil;
import com.shangma.cn.utils.UploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author: JAVASM
* @className: CommonController
* @description:
* @date: 2021/6/14 19:15
* @version: 0.1
* @since: jdk1.8
*/
@RestController
@RequestMapping("common")
public class CommonController {
@Autowired
private EmployeeService employeeService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 发送验证码
* @param phone
* @return
*/
@RequestMapping("getCode/{phone}")
public AxiosResult<Void> getCode(@PathVariable String phone){
System.out.println(phone);
Employee employee = employeeService.findByPhone(phone);
System.out.println(employee);
if (employee==null) {
throw new MyLoginException(AxiosStatus.PHONE_NOT_FOUND);
}
//生成验证码,存入Redis,设置2分钟自动销毁
String code = RandomCodeUtil.munCode();
stringRedisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);
//发送短信,放入子线程中
AsyncManager.getInstance().schedule(AsyncFactory.sendPhoneCode(phone,code),2L);
return AxiosResult.success();
}
/**
* 登录验证
* @param map
* @param session
* @return
*/
@RequestMapping("doLogin")
public AxiosResult<Void> doLogin(@RequestBody Map<String,String>map, HttpSession session){
System.out.println(map);
String phone = map.get("phone");
String code = map.get("code");
String s = stringRedisTemplate.opsForValue().get(phone);
if (s.equals(code)) {
Employee employee = employeeService.findByPhone(phone);
//将登陆信息放入session
session.setAttribute("user",employee);
//清除
stringRedisTemplate.delete(phone);
System.out.println(AxiosResult.success().getData());
return AxiosResult.success();
}
throw new MyLoginException(AxiosStatus.CODE_CHECK_ERROR);
}
}
十、手动打印日志
在项目发布后没有控制台让我们查看BUG,此时我们就需要记录程序的报错信息
一般我们使用log4j来记录错误信息
先导入lombok的jar,在需要记录日志的类上加上@Log4j就可以直接使用了
例如
try {
client.sendSms(sendSmsRequest);
} catch (Exception e) {
e.printStackTrace();
log.error(phone + "+手机验证码发送失败:" + e.getMessage());
}
十一、跨域问题的解决以及登录拦截的实现
十二、分页查询以及日期时区问题的解决
前端部分页面
<div class="box-body">
<table id="example2" class="table table-bordered table-hover">
<thead>
<tr>
<th><input type="checkbox" @change="chooseAll"/>全选</th>
<th>id</th>
<th>头像</th>
<th>姓名</th>
<th>手机号</th>
<th>工资</th>
<th>地址</th>
<th>入职时间</th>
<th>操作</th>
</tr>
</thead>
<tbody ref="table_Body">
<tr v-for="(item,index) in tableData" :key="index">
<td><input type="checkbox" class="check_td"
@click="chooseDeleteItem(item.employeeId,$event)"></td>
<td class="id_td">{{item.employeeId}}</td>
<td><img :src="item.employeeAvatar" width="35px" height="35px"></td>
<td>{{item.employeeName}}</td>
<td>{{item.employeePhone}}</td>
<td>{{item.employeeSalary}}</td>
<td>{{item.employeeAddress}}</td>
<td>{{item.employeeTime}}</td>
<td>
<button class="btn btn-sm btn-info" @click="findById(item.employeeId)"
data-toggle="modal" data-target="#editDialog">
编辑
</button>
<button class="btn btn-sm btn-warning" @click="doDeleteBtn(item.employeeId)"
data-toggle="modal" data-target="#delModal">
删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
前端部分JS代码
let vue = new Vue({
el: "#app",
data: {
tableData: [],
currentPage: 1,
pageSize: 5,
total: 0,
imgUrl: '',
formData: {},
deleteIds: []
},
created() {
this.findPage();
},
methods: {
//查询全部
findAll() {
myaxios.get(`employee/findAll`).then(response => {
let {data} = response;
this.tableData = data;
})
},
//分页查询
findPage() {
myaxios.get(`employee/findPage?currentPage=${this.currentPage}&pageSize=${this.pageSize}`).then(response => {
let {total, list} = response;
this.total = total;
this.tableData = list;
})
},
//分页回掉函数
pageChange(page) {
this.currentPage = page;
var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
for (let i = 0; i < elementsByClassName.length; i++) {
elementsByClassName[i].checked=false;
}
this.findPage();
}
}
后台
Service层
@Override
public List<Employee> findAll() {
List<Employee> employees = employeeMapper.selectByExample(null);
return employees;
}
@Override
public PageBean<Employee> findPage() {
List<Employee> employees = employeeMapper.selectByExample(null);
PageInfo<Employee> pageInfo = new PageInfo<>(employees);
return PageBean.init(pageInfo.getTotal(),employees);
}
Controller层
/**
* 查询全部
*
* @return
*/
@GetMapping("findAll")
public AxiosResult<List<Employee>> findAll() {
List<Employee> all = employeeService.findAll();
return AxiosResult.success(all);
}
/**
* 分页查询
*
* @param currentPage 当前页
* @param pageSize 每一页大小
* @return
*/
@GetMapping("findPage")
public AxiosResult<PageBean<Employee>> findPage(int currentPage, int pageSize) {
//开启分页
PageHelper.startPage(currentPage, pageSize);
PageBean<Employee> all = employeeService.findPage();
return AxiosResult.success(all);
}
这里我们查询后,会发现时间会和数据库中的不同,这是因为数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息。如果你把数据库服务器的时区改了,或者把应用服务器的时区改了,读出来的日期和时间就是错误的。所以这里我们要上设置时区,
在实体类相应的时间属性上加上注解
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "CMT+8")
因为是maven工程,在依赖了相关Jackson的jar后,会自动帮我们依赖
十三、文件上传——使用阿里云对象存储
前端部分JS代码
//选择图片上传
chooseAvatar(e) {
let file = e.target.files[0];
let formData = new FormData();
formData.append("avatar", file);
myaxios.post("common/upload/", formData, {headers: {"Content-Type": "multipart/form-data"}})
.then(response => {
// console.log(response)
this.imgUrl = response;
this.formData.employeeAvatar = response;
})
}
工具类(需要依赖阿里云相关jar)
package com.shangma.cn.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author: JAVASM
* @className: UploadUtil
* @description:
* @date: 2021/6/15 21:34
* @version: 0.1
* @since: jdk1.8
*/
public class UploadUtil {
private static Properties properties;
static {
//加载配置文件
try {
properties = new Properties();
properties.load(SmsUtil.class.getClassLoader().getResourceAsStream("aliyun-oss.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件上传
* @param fileName 文件名
* @param in 流
* @return
*/
public static String upload(String fileName, InputStream in) {
String endpoint = properties.getProperty("aliyun-oss.endpoint");
String accessKeyId = properties.getProperty("aliyun-oss.accessKeyId");
String accessKeySecret = properties.getProperty("aliyun-oss.accessKeySecret");
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//第一参数: 表示bucket名称
//第二个参数: 文件名称 携带后缀
ossClient.putObject(properties.getProperty("aliyun-oss.bucketName"), fileName, in);
ossClient.shutdown();
String url = properties.getProperty("aliyun-oss.imgUrl") + fileName;
return url;
}
}
Controller层
/**
* 上传文件
* @param avatar
* @return
* @throws IOException
*/
@RequestMapping("upload")
public AxiosResult<String> upload(@RequestPart Part avatar) throws IOException {
//重命名
// TODO: 2021/6/15 不能上传文件
System.out.println(avatar);
String fileName = System.nanoTime() + "." + StringUtils.getFilenameExtension(avatar.getSubmittedFileName());
String url = UploadUtil.upload(fileName, avatar.getInputStream());
return AxiosResult.success(url);
}
十四、添加员工信息
前端页面
<div class="box-header">
<div class="form-group form-inline">
<div class="btn-group">
<button type="button" class="btn btn-danger" title="新建" @click="formData={},imgUrl=''"
data-toggle="modal"
data-target="#editDialog">新建
</button>
<button type="button" class="btn btn-success"
data-toggle="modal"
data-target="#delModal">批量删除
</button>
<button type="button" class="btn btn-primary">导入</button>
<button type="button" class="btn btn-info">导出</button>
</div>
</div>
</div>
模态框
<!--新建 编辑弹框-->
<div class="modal fade" id="editDialog">
<div class="modal-dialog modal-lg edit-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">员工操作</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-2 col-form-label">员工姓名:</label>
<div class="col-sm-10">
<input type="text" v-model="formData.employeeName" class="form-control"
placeholder="请输入员工姓名">
</div>
</div>
<div class=" form-group row">
<label class="col-sm-2 col-form-label">员工手机:</label>
<div class="col-sm-10">
<input type="text" v-model="formData.employeePhone" class="form-control"
placeholder="请输入员工手机"/>
</div>
</div>
<div class=" form-group row">
<label class="col-sm-2 col-form-label">员工工资:</label>
<div class="col-sm-10">
<input type="text" v-model="formData.employeeSalary" class="form-control"
placeholder="请输入员工工资"/>
</div>
</div>
<div class=" form-group row">
<label class="col-sm-2 col-form-label">员工地址:</label>
<div class="col-sm-10">
<input type="text" v-model="formData.employeeAddress" class="form-control"
placeholder="请输入员工地址"/>
</div>
</div>
<div class=" form-group row">
<label class="col-sm-2 col-form-label">入职时间:</label>
<div class="col-sm-10">
<input id="date" v-model="formData.employeeTime" type="date" class="form-control"/>
</div>
</div>
<div class=" form-group row">
<label class="col-sm-2 col-form-label">员工头像</label>
<div class="col-sm-10">
<label class="btn btn-primary">
<input type="file" @change="chooseAvatar" style="display:none;" id="avater">
上传图片
</label>
<img :src="imgUrl" alt="" width="100px" height="100px"
style="border: 1px solid #ccc; margin-left: 100px">
</div>
</div>
</div>
</div>
<div class="modal-footer ">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" @click="addOrEdit" data-dismiss="modal">确定</button>
</div>
</div>
</div>
</div>
前端JS
//添加或修改确认按钮
addOrEdit() {
if (this.formData.employeeId) {
//修改
} else {
//添加
//如果没有图片,添加默认图片
this.formData.employeePassword='123456';
if (this.formData.employeeAvatar) {
myaxios.post(`employee/addEmployee/`, this.formData)
.then(response => {
this.findPage();
});
} else {
this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
myaxios.post(`employee/addEmployee/`, this.formData)
.then(response => {
this.findPage();
});
}
}
}
Service
@Override
public void addEmployee(Employee employee) {
employeeMapper.insert(employee);
}
Controller
/**
* 添加
* @param employee
* @return
*/
@PostMapping("addEmployee")
public AxiosResult<Void> addEmployee(@RequestBody Employee employee) {
employeeService.addEmployee(employee);
return AxiosResult.success();
}
十五、改造前端Axios以及解决返回值的问题
自定义Axios,可以解决重复写基本路径、设置是否携带cookie,设置请求时间,设置拦截器(请求之前拦截,返回值响应拦截),
let myaxios = axios.create({
//设置基础路径
baseURL:'http://localhost:8080/',
//设置携带cookie
withCredentials:true,
//设置请求时间
timeout:5000
});
// 请求之前拦截
myaxios.interceptors.request.use(function (config) {
// config.baseURL='http://localhost:8080/';
return config;
}, function (error) {
return Promise.reject(error);
});
// 响应拦截器
myaxios.interceptors.response.use(function (response) {
console.log(response);
let{status,massage,data}=response.data;
if (status==2000){
//console.log(data)
return data;
}
if (status==4004){
alert("登录过期,请重新登录")
location.replace("./login.html");
}
else {
console.log(massage);
//阻止代码向下执行
return Promise.reject(false);
}
}, function (error) {
return Promise.reject(error);
});
当我们给前端返回数据的时候,data有可能是null,这就有可能在前端产生错误,因此,我们需要在返回数据时进行一次判断,返回不为空的值
我们在返回值进行json序列化的时候,只序列化有数据的,在返回值类AxiosResult上加@JsonInclude(JsonInclude.Include.NON_NULL)就可以进行过滤。
十六、修改员工信息
前端页面同查询和添加
前端JS
//添加或修改确认按钮
addOrEdit() {
if (this.formData.employeeId) {
//修改
myaxios.put(`employee/updateEmployee/`, this.formData).then(() => {
this.findPage();
})
} else {
//添加
//如果没有图片,添加默认图片
this.formData.employeePassword='123456';
if (this.formData.employeeAvatar) {
myaxios.post(`employee/addEmployee/`, this.formData)
.then(response => {
this.findPage();
});
} else {
this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
myaxios.post(`employee/addEmployee/`, this.formData)
.then(response => {
this.findPage();
});
}
}
},
//修改按钮点击事件
findById(id) {
myaxios.get(`employee/findById/${id}`).then(response => {
this.formData = response;
//没有图片添加默认图片,
if (response.employeeAvatar) {
this.imgUrl = response.employeeAvatar;
} else {
this.imgUrl = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
this.formData.employeeAvatar = 'https://shangmasanshiqi.oss-cn-beijing.aliyuncs.com/282285958269457.gif';
}
})
}
Service
@Override
public Employee findById(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
}
@Override
public void update(Employee employee) {
employeeMapper.updateByPrimaryKey(employee);
}
Controller
/**
* 根据id查询信息
* @param id
* @return
*/
@GetMapping("findById/{id}")
public AxiosResult<Employee> findById(@PathVariable Integer id) {
Employee employee = employeeService.findById(id);
return AxiosResult.success(employee);
}
/**
* 修改
* @param employee
* @return
*/
@RequestMapping("updateEmployee")
public AxiosResult<Void> updateEmployee(@RequestBody Employee employee){
employeeService.update(employee);
return AxiosResult.success();
}
十七、删除和批量删除员工信息
前端页面同修改,多了一个模态框
<!--删除弹框-->
<div class="modal fade" id="delModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">温馨提示</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
你确定要删除吗?
</div>
<div class="modal-footer ">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="button" @click="delBtn" class="btn btn-success delSure" data-dismiss="modal">确定
</button>
</div>
</div>
</div>
</div>
前端JS
//获取删除元素
chooseDeleteItem(id, e) {
if (e.target.checked) {
this.deleteIds.push(id);
} else {
this.deleteIds.splice(this.deleteIds.findIndex(item => item == id, 1))
}
},
//点击删除按钮
doDeleteBtn(id) {
//获取checkbox的值
var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
for (let i = 0; i < elementsByClassName.length; i++) {
elementsByClassName[i].checked=false;
}
this.deleteIds=[];
this.deleteIds.push(id);
},
//删除确认按钮
delBtn(){
myaxios.delete(`employee/deleteByIds/${this.deleteIds}`).then(()=>{
console.log(this.tableData.length)
if (this.tableData.length==1){
if (this.currentPage>1){
this.currentPage=this.currentPage-1
}
}
this.findPage()
})
},
//全选
chooseAll(e){
if (e.target.checked) {
var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
for (let i = 0; i < elementsByClassName.length; i++) {
elementsByClassName[i].checked=true;
}
//清空数组
this.deleteIds=[];
//添加到数组
var elementsByClassName1 = this.$refs.table_Body.getElementsByClassName("id_td");
for (let i = 0; i < elementsByClassName1.length; i++) {
this.deleteIds.push(elementsByClassName1[i].outerText);
}
console.log(this.deleteIds)
} else {
var elementsByClassName = this.$refs.table_Body.getElementsByClassName("check_td");
for (let i = 0; i < elementsByClassName.length; i++) {
elementsByClassName[i].checked=false;
}
//清空数组
this.deleteIds=[];
}
}
Service
@Override
public void deleteByIds(List<Integer> ids) {
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria();
criteria.andEmployeeIdIn(ids);
employeeMapper.deleteByExample(employeeExample);
}
Controller
@DeleteMapping("deleteByIds/{ids}")
public AxiosResult<Void> deleteByIds(@PathVariable List<Integer> ids){
employeeService.deleteByIds(ids);
return AxiosResult.success();
}
十八、Excel的导出和导入
引入依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
导出前端代码
<a type="button"href="http://localhost:8080/common/writeExcel" class="btn btn-info">导出</a>
导出后台代码
@GetMapping("writeExcel")
public ResponseEntity<byte []> writeExcel (HttpSession session) throws Exception {
//1、拿到数据
List<Employee> list = employeeService.findAll();
//2、创建workBook
Workbook workbook = new XSSFWorkbook();
//3、创建sheet(员工信息sheet)
Sheet sheet = workbook.createSheet("员工信息表");
//7、设置一些样式(可选)
//修改列宽
sheet.setColumnWidth(3,22*256);
sheet.setColumnWidth(4,22*256);
sheet.setColumnWidth(7,60*256);
//设置单元格样式
CellStyle cellStyle = workbook.createCellStyle();
//垂直居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//水平居中
cellStyle.setAlignment(HorizontalAlignment.CENTER);
//字体cellStyle.setFont();
//4、创建行(数据集合的长度)
//创建标题行
Row titleRow = sheet.createRow(0);
List<String> titles = Arrays.asList("员工ID", "员工名称", "员工地址", "员工手机", "入职时间", "员工密码", "员工工资", "员工头像地址");
for (int i = 0; i < titles.size(); i++) {
Cell cell =titleRow.createCell(i);
cell.setCellValue(titles.get(i));
cell.setCellStyle(cellStyle);
}
//创建内容行
for (int i = 0; i < list.size(); i++) {
Employee employee = list.get(i);
Row row = sheet.createRow(i+1);
//5、创建每一行中的单元格(一个属性一个单元格)
Cell idCell = row.createCell(0);
Cell nameCell = row.createCell(1);
Cell addressCell = row.createCell(2);
Cell phoneCell = row.createCell(3);
Cell timeCell = row.createCell(4);
Cell passwordCell = row.createCell(5);
Cell salaryCell = row.createCell(6);
Cell avatarCell = row.createCell(7);
//6、填充单元格中的文字
idCell.setCellValue(employee.getEmployeeId());
nameCell.setCellValue(employee.getEmployeeName());
addressCell.setCellValue(employee.getEmployeeAddress());
phoneCell.setCellValue(employee.getEmployeePhone());
timeCell.setCellValue(sdf.format(employee.getEmployeeTime()));
passwordCell.setCellValue(employee.getEmployeePassword());
salaryCell.setCellValue(employee.getEmployeeSalary());
avatarCell.setCellValue(employee.getEmployeeAvatar());
}
// //8、写到本地
// FileOutputStream out =new FileOutputStream("E:\\employee.xlsx");
// workbook.write(out);
//
// //就近原则关闭资源
// out.close();
// workbook.close();
//在浏览器下载
ByteArrayOutputStream stream = new ByteArrayOutputStream();
workbook.write(stream);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentDispositionFormData("attachment", URLEncoder.encode("员工信息表.xlsx","utf-8"));
byte [] bytes = stream.toByteArray();
ResponseEntity<byte []> responseEntity = new ResponseEntity<byte[]>(bytes,httpHeaders, HttpStatus.OK);
stream.close();
workbook.close();
return responseEntity;
}
导入前端页面
<label class="btn btn-primary">
<input type="file" @change="importExcel" style="display:none;">
导入
</label>
导入前端JS代码
//导入文件
importExcel(){
let file = e.target.files[0];
let formData = new FormData();
formData.append("excel", file);
myaxios.post("common/importExcel/", formData, {headers: {"Content-Type": "multipart/form-data"}})
.then(response => {
this.findPage();
})
}
导入后台代码
@PostMapping("importExcel")
public AxiosResult<Void> importExcel(@RequestPart Part part) throws IOException, ParseException {
//1、把上传的文件变成一个workBook
Workbook workbook = new XSSFWorkbook(part.getInputStream());
//2、获取第一页
Sheet sheetAt = workbook.getSheetAt(0);
//3、获取页中的每一行(每一行都是一个Employee对象)
//获取最后一行的索引
int lastRowNum = sheetAt.getLastRowNum();
for (int i = 1; i < lastRowNum; i++) {
Row row = sheetAt.getRow(i);
//获取单元格
Cell idCell = row.getCell(0);
Cell nameCell = row.getCell(1);
Cell addressCell = row.getCell(2);
Cell phoneCell = row.getCell(3);
Cell timeCell = row.getCell(4);
Cell passwordCell = row.getCell(5);
Cell salaryCell = row.getCell(6);
Cell avatarCell = row.getCell(7);
Integer id = (int) idCell.getNumericCellValue();
String name = nameCell.getStringCellValue();
String address = addressCell.getStringCellValue();
String phone = phoneCell.getStringCellValue();
Date time = sdf.parse(timeCell.getStringCellValue());
String password = passwordCell.getStringCellValue();
Double salary = salaryCell.getNumericCellValue();
String avatar = avatarCell.getStringCellValue();
//4、每一个单元格都是这个对象的属性值(给属性赋值)
Employee employee = new Employee(id,name,address,phone,time,password,salary,avatar);
//5、把对象插入到数据库
employeeService.addEmployee(employee);
}
//6、释放资源
return AxiosResult.success();
}