源代码:
链接:https://pan.baidu.com/s/1Lxb-riH–YQNIy3c0i8pFA 提取码:y3aq 文档地址:https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HjcwL14w-1602078341146)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-akIdZ3Mf-1602078341149)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/3.jpg)]
这里我们使用@ComponentScan将组件扫描进 SpringMVC 容器,因此我们需要在WebAppConfig配置类添加该注解
@ComponentScan(basePackages = "com.shph.controller")这里我们将 UserServiceImpl 扫描到 Spring 容器中,因此我们需要在 RootConfig配置组件扫描注解
@ComponentScan(basePackages = "com.shph", excludeFilters = @ComponentScan.Filter({Controller.class}))在编写 UserService 时,我们指定的组件扫描注解已经可以将该实例扫描到Spring容器了,因此不需要再次配置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rHldE0gQ-1602078341152)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/4.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7uxqvci-1602078341155)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/5.jpg)]
这里重点时搭建SpringMVC全注解项目,有关Tomcat的配置就不再细说。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJifLjPT-1602078341156)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/6.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4nKh0sh-1602078341158)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/7.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ONZCbQu7-1602078341159)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/8.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCyWh49p-1602078341161)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/9.jpg)]
我们可以看到,查询id=3的用户会出现中文乱码问题
// 实际上我们的数据是这样的 map.put(3, new User(3, "张三", 22, "男"));我们再看看浏览器的【响应信息】,我们可以看到响应的数据编码格式并不是UTF-8
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WB9D2Syu-1602078341162)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/10.jpg)]
以前我们在web.xml会配置 CharacterEncodingFilter 过滤器,然而现在我们没有web.xml配置文件,我们又该如何配置?
我们来到我们的MyWebAppInitializer,我们知道MyWebAppInitializer继承了抽象类AbstractAnnotationConfigDispatcherServletInitializer,我们需要重写里面的方法void onStartup(...)
@Override public void onStartup(ServletContext sc) throws ServletException { super.onStartup(sc); // 调用父类方法 // 注册 CharacterEncodingFilter 过滤器 CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setForceEncoding(true); filter.setEncoding("UTF-8"); FilterRegistration.Dynamic encodingFilter = sc.addFilter("filter", filter); // 配置过滤规则 '/*' 拦截所有请求 encodingFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); }在ServletContext类里面提供了对servlet,filter,listener的组成方法
sc.addServlet(); sc.addFilter(); sc.addListener();好了,我们配置好 CharacterEncodingFilter 过滤器后,是不是觉得可以解决中文乱码了?我们在访问一下看看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKVhFFEK-1602078341163)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/11.jpg)]
怎么回事???
这是因为在返回消息的是否,我们返回的是字符串,在 SpringMVC 内部会调用StringHttpMessageConverter的方法转换,而这里StringHttpMessageConverter设置的编码格式为Charset.forName("ISO-8859-1")[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSXTeUMB-1602078341165)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/12.jpg)]
这里我们打开WebAppConfig配置类,它继承的WebMvcConfigurerAdapter有一个空方法
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { }我们需要注册一个StringHttpMessageConverter转换策略
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 配置 String 转换编码格式为 UTF-8 converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); }这里我们在测试一次:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGJzzbce-1602078341166)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/13.jpg)]
看到这个结果,是不是很开心,让我再来测试一下上传过程,作为是否会乱码???
这里再写一个方法,处理接收的请求体
// 测试链接:http://localhost:8080/user-info?name="王五"&age=30&gender="男" @RequestMapping("/user-info") public String getUser(User user, Model model) { System.out.println(user); // 将数据放到 request 域 model.addAttribute("user",user); return "/user.jsp"; }我们来看控制台的打印,可以看到中文没有出现乱码【在Tomcat 8以后,服务器默认的编码格式就是 UTF-8,因此request请求不会出现乱码的现象了】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmd0ORvC-1602078341168)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/14.jpg)]
一般我们返回视图的是否,不建议使用xxx.jsp的路径作为返回参数,这是我们通常会在web.xml配置视图解析器。
<!--视图解析器--> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> </bean>然而,在全注解的SpringMVC中,我们只需要来到我们的WebAppconfig配置类,重写父类WebMvcConfigurerAdapter的方法void configureViewResolvers(...),来配置视图解析
@Override public void configureViewResolvers(ViewResolverRegistry registry) { // 向容器注册了一个 InternalResourceViewResolver 视图解析器 registry.jsp("/WEB-INF/view/",".jsp"); } /* // 这是上面注册视图解析的底层代码实现,可以看到底层会给我们创建一个InternalResourceViewResolver, //并指定我们的视图前缀和后缀 public UrlBasedViewResolverRegistration jsp() { return this.jsp("/WEB-INF/", ".jsp"); } public UrlBasedViewResolverRegistration jsp(String prefix, String suffix) { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix(prefix); resolver.setSuffix(suffix); this.viewResolvers.add(resolver); return new UrlBasedViewResolverRegistration(resolver); } */启动Tomcat测试视图解析器是否生效[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VoWeGiDU-1602078341169)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/15.jpg)]
访问的地址:/WEB-INF/view/user.jsp.jsp
返回的地址:return "/user.jsp";
可以看出视图解析器已经生效了,我们需要修改返回的地址,并将视图放到/WEB-INF/view/目录即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGEFCwq4-1602078341170)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/16.jpg)]
对于一个Web应用,返回 JSON 数据应该是必要的功能,我们测试一下 SpringMVC 是否可以返回 JSON 数据
// 返回任意对象并转换成JSON对象 @RequestMapping("/all-users") @ResponseBody public Object getAllUsers() { List<User> userList = service.getAllUsers(); return userList; }当我们访问该地址时我们可以看到如下结果:
java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.ArrayList这是因为我们没有jackson-databind的依赖,在转换成 json 数据时,会执行MappingJackson2HttpMessageConverter,而该类型需要jackson-databind的支持,我们需要导入它的依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency>并且将MappingJackson2HttpMessageConverter重新注册到容器中;我们还是需要来到WebAppConfig配置类,在void configureMessageConverters(...)完成消息转换的注册
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 向容器注册一个 MappingJackson2HttpMessageConverter 实例 converters.add(new MappingJackson2HttpMessageConverter(new ObjectMapper())); }这样我们就可以解析对象为 json数据了[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6E7mJqxA-1602078341171)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/18.jpg)]
在spring-mvc.xml配置中,我们需要使用以下配置解决静态资源访问问题
<mvc:default-servlet-handler/>而在全注解配置里面,我们需要在WebAppconfig配置文件完成上面配置。
同样我们需要重写WebMvcConfigurerAdapter的void configureDefaultServletHandling(...)方法,开启即可。
@Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }接口:
@Repository public interface IUserDao { User selectUserById(int id); List<User> selectAllUsers(); int insertUser(User user); }Mybatis的 IUserDao.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.shph.dao.IUserDao"> <sql id="user_column"> id, name, age,gender, birthday </sql> <!--根据 id 查询--> <select id="selectUserById" parameterType="int" resultType="User"> select <include refid="user_column"></include> from tbl_user where id=#{arg0}; </select> <!--查询所有用户--> <select id="selectAllUsers" resultType="User"> select <include refid="user_column"></include> from tbl_user </select> <!--插入数据,并返回自增主键回填到 User对象--> <insert id="insertUser" parameterType="User"> <selectKey keyProperty="id" resultType="int" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> insert into tbl_user(name,age,gender,birthday) values (#{name},#{age},#{gender},#{birthday}) </insert> </mapper>在IDEA工具中,位于 java 文件夹下的必须是 java 文件,否则不能编译,我们需要到 pom.xml 里面配置如下内容来解决。
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>com/shph/dao/**/*.xml</include> </includes> </resource> </resources> </build>这里我们需要注册的组件主要有:
DataSource【数据源】
SqlSessionFactoryBean【这是Mybatis整合Spring提供的一个Bean用于mybatis的环境配置,这样就不需要添加mybatis-config.xml配置文件了】
MapperScannerConfigurer【这个也是Mybatis整合Spring封装的一个配置类,用于Mapper相关的配置,如指定Mapper所在的位置,指定SqlSession】
DataSourceTransactionManager【关于事务相关的配置】
有关整合Mybatis的组件组成配置我们应该在Spring的配置类完成,因此我们需要在RootConfig配置类完成相关配置:
// 开启事务控制 @EnableTransactionManagement // 将 db.properties 的配置导入到 Spring 的环境里面 @PropertySource(value = "classpath:db.properties") @Configuration @ComponentScan(basePackages = "com.shph", excludeFilters = @ComponentScan.Filter({Controller.class})) public class RootConfig { // 配置数据库连接池 @Bean(value = "dataSource", initMethod = "init", destroyMethod = "close") public DruidDataSource initDruidDataSource( @Value("${jdbc.driverClassName}") String driverClassName, @Value("${jdbc.url}") String url, @Value("${jdbc.username}") String username, @Value("${jdbc.password}") String password ) { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } // 初始化 SqlSessionFactoryBean @Bean("sqlSessionFactory") public SqlSessionFactoryBean initSqlSessionFactoryBean(DataSource dataSource) throws IOException { SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactory.setMapperLocations(resolver.getResources("classpath:com/shph/dao/*.xml")); sqlSessionFactory.setTypeAliasesPackage("com.shph.entity"); return sqlSessionFactory; } @Bean public MapperScannerConfigurer initMapperScannerConfigurer() { MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.shph.dao"); // 这里的 sqlSessionFactory 字符串必须与注册 SqlSessionFactoryBean 时指定的 id 值一样 configurer.setSqlSessionFactoryBeanName("sqlSessionFactory"); return configurer; } // 配置事务管理 @Bean public DataSourceTransactionManager transactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); return transactionManager; } }【注意:这里有一个小问题,在DataSource数据源配置时,使用 @Value注解从Spring的环境去配置信息可能会失效,关于失效原因这里可以参考该文档:@Value不能注入解决方案】
关于日志配置在Mybatis官网有介绍环境配置https://mybatis.org/mybatis-3/zh/logging.html,这里就简单概述一些。
在Mybatis官方推荐我们使用 log4j 日志框架,因此我们这里就介绍一些 log4j 的配置。
这里需要关注一些 log4j 的日志打印级别OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果我们想要看到 mysql 的sql语句执行输出日志,日志级别需要 DEBUG及以下
# 全局日志配置 log4j.rootLogger=DEBUG, stdout # MyBatis 日志配置 log4j.logger.org.mybatis.example.BlogMapper=TRACE # 控制台输出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%nURL地址:http://localhost:8080/all-users
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VcsFApx1-1602078341173)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/19.jpg)]
URL地址:http://localhost:8080/user?id=1
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBmIPcjA-1602078341174)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/20.jpg)]
这里编写了一个简单的 jsp页面,用于提交用户信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PtcSlY7N-1602078341175)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/21.jpg)]
URL地址:http://localhost:8080/add-user?name=翠芬&age=19&gender=女&birthday=2000-02-09
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g3eihR0Y-1602078341177)(https://shphuang_aliyun.gitee.io/pictures-of-the-warehouse/spring-mvc-annotation-all-demo-img/22.jpg)]
这是由于在提交的数据里面有一个日期birthday=2000-02-09数据,在后端接收数据时需要转换成Date对象类型,但是2000-02-09是字符串,无法转换成日期类型。
解决方案一:
在Spring的org.springframework.format.annotation.DateTimeFormat提供了一个注解,用于格式化日期,在属性或者set方法上添加该注解即可解决。
@DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday;解决方案二:参考文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web.html#mvc-config-conversion
我们需要编写一个实现类实现接口Converter,该接口有一个方法,用于数据转换
public class StringToDateConvert implements Converter<String, Date> { @Override public Date convert(String source) { if (!Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)) { return null; } SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = dateFormat.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; } }同时,我们还需要去注册到 Formatter 转换器才能使用:
来到配置类WebAppConfig重写其父类方法void addFormatters(...)
// 注册 Formatter 转换器 @Override public void addFormatters(FormatterRegistry registry) { // 将自定义字符串日期转换器添加到 FormatterRegistry registry.addConverter(new StringToDateConvert()); }nverter<String, Date> { @Override public Date convert(String source) { if (!Pattern.matches("^\d{4}-\d{2}-\d{2}$",source)) { return null; }
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = dateFormat.parse(source); } catch (ParseException e) { e.printStackTrace(); } return date; }}
同时,我们还需要去注册到 Formatter 转换器才能使用: 来到配置类`WebAppConfig`重写其父类方法`void addFormatters(...) ` ```java // 注册 Formatter 转换器 @Override public void addFormatters(FormatterRegistry registry) { // 将自定义字符串日期转换器添加到 FormatterRegistry registry.addConverter(new StringToDateConvert()); }