写在前面
招聘信息专题热力图展示系统 项目最开始是使用原生javaWeb+jsp实现的,后来改用SSM框架,最近学习了springboot后将其改为springboot实现。从开发到部署可以说是遇到了无数的问题,特此整理一下。
问题汇总
1. 使用log4j打印日志与springboot自带的日志冲突
springboot项目启动后,控制台会有如下输出:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/E:/develop/maven_repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/E:/develop/maven_repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
这种情况就是我们在pom.xml中引入的log4j与springboot中使用的logback产生了依赖冲突,当然直接使用springboot自带的日志框架完全可以,不过现在用log4j的人很多,所是流行的日志框架,所以如果需要使用log4j应该怎么办呢?
解决办法:
只需要在springboot的起步依赖中排除掉日志打印就可以了。即在pom.xml添加如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
把log4j.properties配置文件放置在resources目录下就可以了,关于配置文件怎么配置,这里不再赘述。启动之后,日志输出如下:
2. springboot无法解析jsp文件
因为项目使用了jsp,但是项目使用了springboot后,访问index.jsp 无法显示页面,而是将index.jsp当做一个文件下载了。
一开始怀疑是不能直接把index.jsp放在webapps目录下,然后直接访问,需要使用Controller添加了一个RequestMapping为 / 的控制器进行跳转。把index.jsp移动到WEB-INF/jsp/目录下之后,也添加了控制器,如下
@Controller
public class WebController {
@RequestMapping("/")
public String index(){
return "index";
}
}
然后发现启动后,访问仍然无法显示,直接404了。
解决办法:
springboot其实是不推荐使用jsp的,现在也没有多少程序员使用jsp了,需要给项目添加一个依赖,使之支持jsp解析,所以只需要在pom.xml添加下面一段就可以了。
<!--用于编译jsp-->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
有了这个依赖,再运行,发现论直接访问index.jsp(放在webapps目录下),还是通过Controller跳转都可以显示jsp页面了,当然了,更推荐使用Controller控制器统一管理项目接口。
3. 项目错误页面处理
当项目中没有做统一的异常处理时,看到的页面就是这样的…
这个异常其实已经说明了问题,就是我们没有为项目提供/error 路径的跳转。当然,springboot中完全没有必要自己去定义 /error 路径的跳转,事实上,springboot已经为我们准备好了。
静态异常页面
自定义的静态异常页面,实际上就是一些显示错误信息的HTML等资源。可以使用http状态码命名,例如404.html、500.html,也可以使用4xx.html这样命名,表示400-499都会显示这个异常页面。默认在classpath:/static/error/
目录下定义(也就是项目的resources目录下)。
动态异常页面
动态异常页面其实与静态类似,可以把页面放在 classpath:/templates/error/
目录下,可以使用jsp等显示错误信息,打印异常信息等。
当静态与动态都定义了的时候,优先使用动态异常页面。完整的查找错误的方法为: 发生了404错误-->查找动态 404.html 页面-->查找静态 404.html --> 查找动态 4xx.html-->查找静态 4xx.html 。
4.Consider defining a bean of type 'cn.xgblack.heatmap.mapper.JobMapper' in your configuration.
这是对象注入异常,可以理解为spring没有找到这个JobMapper对象。
***************************
APPLICATION FAILED TO START
***************************
Description:
Field jobMapper in cn.xgblack.heatmap.service.impl.JobServiceImpl required a bean of type 'cn.xgblack.heatmap.mapper.JobMapper' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'cn.xgblack.heatmap.mapper.JobMapper' in your configuration.
解决办法
方案一:在 启动类 中添加 @MapperScan("xxx.xxx.xxx.mapper")注解
方案二:将XxxMapper类中的@Repository注解换成@Mapper注解(使用@Mapper不行的话可以试试这两个注解同时使用)
5. Error querying database. Cause: java.lang.IndexOutOfBoundsException: Index: 10, Size: 10
mybatis查询数组越界异常,Size为多少,Index就为多少。异常如下:
### Error querying database. Cause: java.lang.IndexOutOfBoundsException: Index: 10, Size: 10
### The error may exist in file [D:\Code\IdeaJava\CareerInfo_HeatMap\target\classes\mapper\JobMapper.xml]
### The error may involve cn.xgblack.heatmap.mapper.JobMapper.findByPage
### The error occurred while handling results
### SQL: SELECT jid,cname,jname,lon,lat,minwage,maxwage,province,highlights,erequir FROM job LIMIT ?,?
### Cause: java.lang.IndexOutOfBoundsException: Index: 10, Size: 10] with root cause
java.lang.IndexOutOfBoundsException: Index: 10, Size: 10
解决办法
其实这种情况只需要给项目的查询的 entity 类加上默认的无参构造器就可以了。
不过一般如果没有自己写带参的构造器的话,无参构造器是默认的,不许有手动添加,在写自定义的构造参数的时候需要把无参构造器也手动添加。那么我是怎么没有无参构造器的呢?
因为我是用了Lombok框架,在类上加上 @Data注解,会自动为类生成 setter/getter、equals、canEqual、hashCode、toString方法 ,我之前看百度说无参构造器也会生成的,所以我给类只加了@Data和@AllArgsConstructor注解。只需要再加上@NoArgsConstructor这个注解就可以了。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Job {
/**
* 主键id
*/
private Integer jid;
/**
* 公司名称
*/
//...省略
}
6. 注册登录后页面跳转与重定向问题
由于项目最开始使用原生的JavaWeb开发的,所以,页面转发和重定向都是使用的request和response,换成SSM框架后,没有更改依旧可以用,但是再使用springboot后,竟然跳转不了了…
其实重定向和转发的方法有很多,我只讲一下我用的方法,因为我是做登录注册验证,根据很多的if判断,进行转发或者重定向,所以有一些方法虽然也可以用,但不是很方便。我使用的是返回String,如下:
/**
* 注册验证
* @param request 请求
* @param session 会话
* @param verifycode 验证码
* @param registerUser 注册用户信息
* @throws ServletException 异常//TODO
* @throws IOException 异常//TODO
*/
@RequestMapping("/registerCheck")
public String register(HttpServletRequest request, HttpSession session , String verifycode , User registerUser) throws ServletException, IOException {
//获取生成的验证码
String checkcode_session = (String)session.getAttribute("checkcode_session");
//去除生成的验证码,以防止验证码被重复使用的bug
session.removeAttribute("checkcode_session");
//先判断验证码是否正确
if (verifycode != null && !"".equals(verifycode) && !verifycode.equalsIgnoreCase(checkcode_session)) {
//验证码不对,存储错误信息,并跳转
request.setAttribute("regist_msg","验证码输入错误!");
return "/user/register";
}
// 调用方法查看用户名是否已经被注册
if (service.usernameIsExist(registerUser.getUsername())) {
//用户名已存在
request.setAttribute("regist_msg", "用户名已存在");
return "/user/register";
}
if (service.regist(registerUser)) {
//注册成功
request.setAttribute("login_msg", "注册成功,自动登录");
//注册成功。尝试自动登录
session.setAttribute("user",registerUser);
//跳转页面(直接重定向)
return "redirect:/";
} else {
//注册失败
request.setAttribute("regist_msg", "注册失败,请重试");
return "/user/register";
}
}
return "redirect:/";重定向到首页,return "/user/register";跳转到WEB-INF/jsp/下的register.jsp文件,即注册页面,转发可以存入一些提示信息。
7. springboot中使用Servlet和Filter
首先要在项目启动类中添加@ServletComponentScan注解,自动扫描。然后再Filter类上添加@WebFilter("/")注解,value填写拦截的路径。
8.springboot 打war包,部署404
如果把springboot项目也像SSM项目那样直接打包,部署到服务器之后,会直接404,项目无法启动。
解决办法
首先,在pom.xml中, spring-boot-starter-tomcat 依赖下添加上<scope>provided</scope>
,在编译打包的时候去掉springboot的Tomcat,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
之后,添加一个 ServletInitializer 类,(也可以直接使用启动类)继承自 SpringBootServletInitializer ,并重写其 configure 方法,如下:
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(CareerInfoHeatMapApplication.class);
}
}
如果还不可以,可以试试把spring-boot-maven-plugin插件的版本改低一些。
9. springboot 打war包,部署502
502是因为项目出现了异常,每个人可能遇到的都不一样。经过查看日志,虽然没分析出来具体是什么情况,但是可以肯定是mybatis的问题,经过一番排查,发现我自己指定了mybatis-spring-boot-starter这个依赖的版本,且与springboot起步依赖的版本不一样,尝试更改版本号为springboot起步依赖的版本后,果然解决了这个问题。
10. springboot项目打war包,静态资源访问不到,显示404
这个问题是因为使用了Nginx进行反向代理Tomcat8080端口,静态资源算是交给Nginx来管理了。在项目网址上指定8080端口访问,其实是没有问题的。网上说这是项目默认动静分离造成的。
解决办法很简单,只需要把static目录下的所有静态资源放到对应的静态网站该放的位置就好了,即如果在springboot项目中,访问123.png的网址是test.xgblack.cn:8080/img/123.png,只需要把img/这个目录复制到test.xgblack.cn网站根目录就可以了。
11. idea 打包的jar运行报 “XXX中没有主清单属性”
这可能是使用IDEA打包造成的,我之后在cmd中用mvn命令就没有出现这种问题了。也不知道是我没用IDEA打包,还是因为我更改了spring-boot-maven-plugin的版本。
可以参考idea 打包的jar运行报 “XXX中没有主清单属性”
12. springboot项目打jar包,启动后访问404
首先要在pom.xml文件中制定打包方式为jar
之后需要修改 maven打包插件,我用2.x打包出来的jar都不行,网上说用1.4.2 ,版本高了就不兼容 ,我试了一下果然可以。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.2.RELEASE</version>
<configuration>
<!-- 设置启动类 -->
<mainClass>cn.xgblack.heatmap.CareerInfoHeatMapApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
我到这一步,打包出来就已经可以用了。如果不可以的话,网上还有下面的一步。参考springboot+jsp打成jar包部署404问题”
<resource>
<directory>src/main/webapp</directory>
<!--这里必须是META-INF/resources-->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
13. 修改Tomcat的server.xml部署Javaweb项目
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log" suffix=".txt" />
</Host>
<Host appBase="/www/wwwroot/study.xgblack.cn" autoDeploy="true" name="study.xgblack.cn" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
<Context crossContext="true" docBase="heatmap" path="/heatmap" reloadable="true" />
<Context crossContext="true" docBase="heatmap_ssm" path="/heatmap_ssm" reloadable="true" />
</Host>
一个Host表示一个虚拟主机,可以包含一个或多个web应用。
- appBase : 指定虚拟主机的目录,可以指定绝对目录,也可以指定相对于的相对目录.如果没有此项,默认 为/webapps. 它将匹配请求和自己的Context的路径,并把请求转交给对应的Context来处理。 (这个目录下的war包会被自动解包)
- autoDeploy : 如果此项设为true,表示Tomcat服务处于运行状态时,能够监测appBase下的文件,如果有新有web应用加入进来,会自运发布这个WEB应用
- unpackWARs : 如果此项设置为true,表示把WEB应用的WAR文件先展开为开放目录结构后再运行.如果设为false将直接运行为WAR文件
Context元素, 每个web应用有唯一的一个相对应的Context代表web应用自身.servlet容器为第一个web应用创建一个
- path : 该Context的路径名是"",故该Context是该Host的默认Context
- docBase : 该Context的根目录
- reloadable : 如果这个属性设为true, Tomcat服务器在运行状态下会监视在WEB-INF/classes和Web-INF/lib目录CLASS文件的改运.如果监视到有class文件 被更新,服务器自重新加载Web应用
14. 宝塔面板SSL证书错误
这个问题遇到很多次了,应该是使用宝塔面板的bug,虽然不明白原因,但应该可以断定是某一个网站SSL证书导致的。
之前没有截图,图是宝塔的论坛上的。这段错误提示仔细分析一下,不需要看明白,只需要从中找到他报错的那个站点就可以了。然后删除那个站点(注意,只删除站点,不删除数据库,根目录等等),然后照原样新建一个站点就可以了。
小结
虽然是一个小项目,但是从开发到部署真的是遇到了各种各样的问题。每个问题都是毫无头绪,需要在百度上看好几篇相关的文章才能找到解决的办法,甚至尝试半天也不能解决。不过当最终项目在服务器上正常运行的那一刻,感觉还是很不错的,哈哈哈。
这篇只是记录一下,防止以后再遇到忘记怎么解决还需要搜半天…
退出登录?