在学习了《Spring实战》的一些章节后,我打算写一个项目来锻炼一下。在创建项目前,我希望用全注解的方式来开发。后来就知道,debug掉的头发,都是前期拍脑袋进的水——网上关于注解的资料也太少了吧!!!
经过千辛万苦后,我终于顺利完成了项目的搭建:)
项目的源代码地址:https://github.com/FuGaZn/SpringBlog
欢迎给个star哦 ( ̄∇ ̄)
概述
首先介绍一下这个SpringBlog项目:
本项目基于Spring+SpringMVC+Mybatis的技术框架,实现个人博客的登入登出、管理文章、在线编写和发布文章、浏览博客等基本功能。
在一步步搭建项目的过程中,你将学习到:
- DispatcherServlet的配置
- Web界面访问的配置
- 用Interceptor实现拦截功能
- 自动装配(Auto wire)
- Controller和Service的编写
- 基于Mybatis实现的Mapper接口
以上所有功能将使用注解的形式配置!
(除了使用web.xml配置对静态资源的访问)
博客界面示例
登陆界面:
后台管理界面:
撰写文章界面:
博客主页:
查看博客界面:
搭建项目
新建一个Java web项目,引入Maven框架,将pom.xml的内容配置好就完成了基本的搭建。
项目结构:
本项目采用Maven来管理,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">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<groupId>com.fjx.blog.spring</groupId>
<artifactId>SpringBlog</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20170516</version>
</dependency>
<!--spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!--springMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--springMVC依赖结束-->
</dependencies>
</project>
编写Java web Config文件
java web cofig配置类是xml配置文件的替代,在Java类上添加@Configuration注解后,该类会被Spring识别为配置类。
配置DispatherServlet
DispatcherServlet用于将请求发送给SpringMVC控制器(controller)DispatcherServlet 会查询一个或多个处理器映射( handler mapping ) 来确定请求的下一站在哪里。处理器映射会根据请求所携带的 URL 信息来进行决策。
MyWebInitializer.java:
package com.fjx.blog.spring.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//根配置 和我们目前的项目无关,所有不用去关心
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
//指定配置类
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
//将DispatcherServlet映射到“/”,即该DispatcherServlet会处理所有的请求
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
《Spring实战》对AbstractAnnotationConfigDispatcherServletInitializer的讲解:
配置WebConfig
package com.fjx.blog.spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@ComponentScan("com.fjx.blog.spring")
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
@Bean(name = "multipartResolver") // bean必须写name属性且必须为multipartResolver
//用于传输MultipartFile
protected CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(5 * 1024 * 1024);
commonsMultipartResolver.setMaxInMemorySize(0);
commonsMultipartResolver.setDefaultEncoding("UTF-8");
return commonsMultipartResolver;
}
@Override
//用于处理静态资源
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resource/**").addResourceLocations("/resource/asset/");
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
InternalResourceViewResolver用于查找JSP文件,对于传入的视图名称(Controller里@RequestMapping注解的方法返回的String是视图名称),为其添加前缀和后缀,在web/webapp文件夹下查找对应的JSP文件。
例如,对以下方法:
@RequestMapping({"/index","/"})
public String index(Model model){
return "home/index";
}
InternalResourceViewResolver接收到“/home/index”字符串后,拼接前缀后缀得“/WEB-INF/views/home/index.jsp”。
注意:setPrefix方法里的/WEB-INF/views/不能写成WEB-INF/views/,前者通过相对路径访问资源,后者使用绝对路径,因此后者会出错。
addResourceHandlers方法用于处理对静态资源的访问。如果不设置,访问静态资源会被拦截。
如果addResourceHandlers不起作用的话,可以在web.xml中配置如下内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SpringBlog</display-name>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.woff</url-pattern>
<url-pattern>*.svg</url-pattern>
<url-pattern>*.ttf</url-pattern>
<url-pattern>*.woff2</url-pattern>
<url-pattern>*.eot</url-pattern>
<url-pattern>*.otf</url-pattern>
<url-pattern>*.ico</url-pattern>
<url-pattern>*.gif</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
</web-app>
RootConfig.java:
package com.fjx.blog.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = {"com.fjx.blog.spring"},
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class))
public class RootConfig {
}
配置SecurityConfig.java以实现对登陆状态的判断
package com.fjx.blog.spring.config;
import com.fjx.blog.spring.interceptor.SecurityInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SecurityConfig extends WebMvcConfigurationSupport {
@Bean
public SecurityInterceptor securityInterceptor() {
return new SecurityInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry.addInterceptor(securityInterceptor());
registration.excludePathPatterns("/home/*"); //排除对该路径的拦截
registration.excludePathPatterns("/");
registration.excludePathPatterns("/login");
registration.addPathPatterns("/admin/*"); //添加对该路径的拦截
registration.addPathPatterns("/admin");
}
}
SecurityInterceptor.java的内容:
package com.fjx.blog.spring.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class SecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws IOException {
//这里根据session的用户来判断角色的权限,根据权限来转发不同的页面
if(request.getSession().getAttribute("user") == null) {
response.sendRedirect("/login");
return false;
}
return true;
}
}
在第一次访问被SecurityConfig拦截的界面时,因为session里的user属性不存在,所以会重定向到login界面。登陆后,添加user属性,再次访问这些界面时,就不会被重定向了。
配置Mybatis:
在resources文件夹下建立properties文件,添加如下内容:
server.port=8080
# 数据库连接
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springblog
# 用户名
spring.datasource.username=root
# 密码
spring.datasource.password=123456
# 数据库驱动
spring.datasource.driver=com.mysql.jdbc.Driver
#mybatis设置相关
mybatis.type.alias.package=com.bdqn.lyrk.ssm.study.entity
logging.leve.com.fjx.blog.spring.mapper=debug
mybatis.configuration.mapUnderscoreToCamelCase=true
添加PropertiesConfig.java:
package com.fjx.blog.spring.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Configuration
@PropertySource("classpath:application.properties")
public class PropertiesConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.driver}")
private String driver;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${mybatis.type.alias.package}")
private String mybatisTypeAliasPackage;
public String getUrl() {
return url;
}
public String getDriver() {
return driver;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
添加MyBatisConfig.java:
package com.fjx.blog.spring.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
@MapperScan("com.fjx.blog.spring.mapper")
public class MyBatisConfig {
@Autowired
PropertiesConfig propertiesConfig;
@Bean
//如果项目运行后报“Error querying database. Cause: java.lang.NullPointerExceptio”这样的错误,你或许要检查一下这里的配置,看看是不是url或者driverClassName为空
public DataSource dataSource(PropertiesConfig propertiesConfig) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(propertiesConfig.getUsername());
dataSource.setPassword(propertiesConfig.getPassword());
dataSource.setUrl(propertiesConfig.getUrl());
dataSource.setDriverClassName(propertiesConfig.getDriver());
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
//不能用new PropertiesConfig()的方式引入参数,否则得不到变量
sessionFactory.setDataSource(dataSource(propertiesConfig));
return sessionFactory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
return dataSourceTransactionManager;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
return propertySourcesPlaceholderConfigurer;
}
}
在以上所有文件都配置完毕后,我们就可以开发具体页面啦!是不是很激动呢,如果你是第一次开发SpringMVC项目,可能会觉得上面的配置很繁琐,但是如果你接触过用XML文件进行配置的话,那肯定会觉得上面的配置太简单了。
而且接下来,当我们配置Controller、Service、Mapper时,只需要在类上添加注解就可以完成了,不用在辛辛苦苦写完java文件后,还要在xml文件里进行添加或修改。
接下来让我们以登陆操作为例,从数据库到jsp页面,自底向上一条龙讲述其中的全部操作。
登陆:Mapper->Service->Controller->JSP
Mapper
我们先在数据库中建立一张表,名字为users(在IDEA里可以图形化操作)
-- auto-generated definition
CREATE TABLE users
(
id INT AUTO_INCREMENT
PRIMARY KEY,
ukey VARCHAR(255) NULL,
name VARCHAR(255) NULL,
password VARCHAR(255) NULL,
CONSTRAINT user_uid_uindex
UNIQUE (id)
)
ENGINE = InnoDB;
然后创建对应的User.java:
package com.fjx.blog.spring.entity;
public class User {
private int id;
private String name; //暱称
private String ukey; //用户身份识别key,登陆使用
private String password;
//还有一系列getter和setter方法,别忘了哦。在IDEA里,同时摁住Alt和Insert键,可以一键添加全部getter和setter方法哦
}
配置UserMapper.java:
package com.fjx.blog.spring.mapper;
import com.fjx.blog.spring.entity.User;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Component;
import java.util.List;
@Mapper
@Component(value = "userMapper")
public interface UserMapper {
@Select("select * from users")
List<User> selectAll();
@Select("select * from users where ukey = #{ukey}")
List<User> selectUserByKey(String ukey);
@Insert("insert into users values(#{user.id},#{user.ukey},#{user.name},#{user.password},#{user.userLastLoginIp})")
@Options(useGeneratedKeys = true)
int save(@Param("user") User user);
}
@Mapper注解表明该类是一个Mybatis Mapper
@Select/@Insert注解等用来操作sql语句。在传入对象参数时,在参数前使用@Param注解,否则会报 xxx not found错误。
Mybatis的注解详细用法可以参照:MyBatis常用11种注解
创建UserService和UserServiceImpl文件:
package com.fjx.blog.spring.service;
import com.fjx.blog.spring.entity.User;
public interface UserService {
User getUserByKey(String key);
void save(User user);
}
package com.fjx.blog.spring.service.impl;
import com.fjx.blog.spring.entity.User;
import com.fjx.blog.spring.mapper.UserMapper;
import com.fjx.blog.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserByKey(String key) {
List<User> users = userMapper.selectUserByKey(key);
if (users == null || users.size() == 0){
return null;
}else
return users.get(0);
}
public void save(User user) {
int i = userMapper.save(user);
}
}
可以看到UserServiceImpl实现自UserServcie接口,@Service注解要放在UserServiceImpl上
使用@AutoWired注解来为UserMapper自动装配
创建LoginController.java:
package com.fjx.blog.spring.controller.admin;
import com.fjx.blog.spring.entity.User;
import com.fjx.blog.spring.service.UserService;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author fujiaxing
* 登陆登出功能
*/
@Controller
public class LoginController {
@Autowired
UserService userService;
@RequestMapping({"/login","/"})
public String login(){
return "/admin/login";
}
@RequestMapping(value = "/loginVerify",method = RequestMethod.POST)
@ResponseBody
public String loginVerify(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> map = new HashMap();
String userkey = request.getParameter("ukey");
String password = request.getParameter("password");
String rememberme = request.getParameter("rememberme");
User user = userService.getUserByKey(userkey);
if (user == null) {
map.put("code", 0);
map.put("msg", "用户名无效!");
} else if (!user.getPassword().equals(password)) {
map.put("code", 0);
map.put("msg", "密码错误!");
} else {
System.out.println("登陆成功");
//登录成功
map.put("code", 1);
map.put("msg", "");
//添加session
request.getSession().setAttribute("user", user);
if (rememberme != null) {
//创建两个Cookie对象
Cookie keyCookie = new Cookie("ukey", userkey);
//设置Cookie的有效期为3天
keyCookie.setMaxAge(60 * 60 * 24 * 3);
Cookie pwdCookie = new Cookie("password", password);
pwdCookie.setMaxAge(60 * 60 * 24 * 3);
response.addCookie(keyCookie);
response.addCookie(pwdCookie);
}
// user.setUserLastLoginTime(new Date());
user.setUserLastLoginIp(getIpAddr(request));
userService.save(user);
}
String result = new JSONObject(map).toString();
return result;
}
@RequestMapping(value = "/logOut",method = RequestMethod.GET)
@ResponseBody
public String loginOut(HttpServletRequest request, HttpServletResponse response){
request.getSession().removeAttribute("user");
Map<String, Object> map = new HashMap();
map.put("code", 1);
map.put("msg", "");
String result = new JSONObject(map).toString();
return result;
}
}
可以看到,在LoginController里,自动装配的对象是UserService, 而不是UserServiceImpl
login()方法上注解配置了映射“/login”,对应的jsp文件则是/WEB-INF/views/admin/login.jsp
上文提到,我们使用session里的user属性判断用户是否需要登陆,在执行登陆操作后,我们调用request.getSession().addAttribute()方法添加user属性。在登出系统后,我们调用request.getSession().removeAttribute()方法删除user属性
后端的所有类和方法都写好啦,接下来我们就可以编写前端界面和js代码了。
新建一个/admin/login.jsp文件,添加如下代码:
<body>
<%
String username = "", password = "";
Cookie[] cookies = request.getCookies();
if (cookies!=null){
for (int i = 0; i < cookies.length; i++) {//对cookies中的数据进行遍历,找到用户名、密码的数据
if ("ukey".equals(cookies[i].getName())) {
username = cookies[i].getValue();
} else if ("password".equals(cookies[i].getName())) {
password = cookies[i].getValue();
}
}
}
%>
<form name="loginForm" id="loginForm" method="post">
<input type="text" placeholder="用户名" name="username" id="user_login" class="input" value="<%=username%>" size="20" required/>
<input type="password" placeholder="密码" name="password" id="user_pass" class="input" value="<%=password%>" size="20" required/>
<p class="forgetmenot"><label for="rememberme"><input name="rememberme" type="checkbox" id="rememberme" value="1" checked /> 记住密码</label></p>
<a class="submit">登陆
<input type="button" name="wp-submit" id="submit-btn" value="登录" />
</a>
<p id="backtoblog" style="margin-left: 160px;color: black"><a href="/index">← 返回到博客</a></p>
</form>
</body>
<script src="/resource/assets/js/jquery.min.js"></script>
<script type="text/javascript">
$("#submit-btn").click(function () {
var user = $("#user_login").val();
var password = $("#user_pass").val();
console.log(user+' '+password)
if(user=="") {
alert("用户名不可为空!");
} else if(password==""){
alert("密码不可为空!");
} else {
$.ajax({
async: false,//同步,待请求完毕后再执行后面的代码
type: "POST",
url: '/loginVerify',
contentType: "application/x-www-form-urlencoded; charset=utf-8",
data: $("#loginForm").serialize(),
dataType: "json",
success: function (data) {
if(data.code==0) {
alert(data.msg);
} else {
window.location.href="/admin";
}
},
error: function () {
alert("数据获取失败")
}
})
}
})
只摘取了关键代码,要看全部代码可以点击Github: SpringBlog/Login.sjp
全部完成后,运行tomcat,就可以在localhost:8080上看到我们写的界面啦,并且可以正常使用哦!
总结
好的,本篇文章到这里就结束了。似乎写了不少内容(好吧,大部分都是代码)
注解有着比xm更简单的配置和更好的可读性,使用得当的话会大大减轻springmvc开发的复杂度和代码量。
本文也未涉及深层次的spring知识,主要还是以一个简单的登陆流程为例,讲一下如何以全注解的方式来开发一个springmvc项目。
如果对这个简单的个人博客框架有兴趣的话,可以关注https://github.com/FuGaZn/SpringBlog
SpringMVC相对于SpringBoot来说,开发起来还是太复杂了。所以这个博客项目仅用于熟悉springmvc注解配置的用法,不会搭建为成熟的博客系统。
博客的在线编辑功能使用了Editormd,如果想了解Editormd的用法,可以看:Editormd的使用——在线编辑和查看文章