<!-- 属性配置 --> <properties> <!-- 设置项目的编码 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 设置java的版本为1.8--> <java.version>1.8</java.version> <!-- 源码编译的版本为1.8--> <maven.compiler.source>1.8</maven.compiler.source> <!-- 目标字节码的版本为1.8--> <maven.compiler.target>1.8</maven.compiler.target> <!-- 指定编译器版本为1.8--> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> <!-- 依赖的版本号 --> <spring.version>5.1.5.RELEASE</spring.version> <junit.version>4.12</junit.version> <mysql.version>5.1.47</mysql.version> <mybatis.version>3.5.2</mybatis.version> <mybatis.spring.version>2.0.3</mybatis.spring.version> <druid.version>1.1.20</druid.version> <project.version>1.0-SNAPSHOT</project.version> <servlet.version>4.0.1</servlet.version> <jackson.version>2.10.0</jackson.version> <pagehelper.version>5.1.10</pagehelper.version> <logback.version>1.2.3</logback.version> </properties> <!-- 添加依赖 --> <dependencyManagement> <dependencies> <!-- 测试 --> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- mysql驱动 --> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- spring --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- spring mvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- spring jdbc --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- druid连接池 --> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- 事务管理 --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!-- mybatis整合插件 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis.spring.version}</version> </dependency> <!-- Jackson --> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- servlet --> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <!-- 分页插件 --> <!-- pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>${pagehelper.version}</version> </dependency> <!-- 日志框架 --> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <!-- 日志 --> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies> </dependencyManagement>
在dao层中,主要就是配置连接池,mybatis和分页插件
1、创建包:
创建如下:
City.java:
public class City { private String cityId; private String cityName; private String cityCode; private String province; }
CityDao.java:
public interface CityDao { /** * 分页,获取城市信息 * @param pageNum 第几页(从0开始) * @param pageSize 每页几条 * @return */ List<City> cityInfo(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize); /** * 获取指定城市信息 * @param cityId 城市id * @return */ City getCity(String cityId); }
3、创建xml配置文件和mapper映射文件
applicationContext.xml:
<?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:mybatis="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> <!-- 启用注解相关的后置处理器,这样才可以在代码中使用Spring提供的相关注解 --> <!-- <context:annotation-config /> --> <!-- 配置包扫描,也就是扫描哪些包下带有注解的类,将其纳入ioc容器管理, base-package属性将会扫描当前包以及子包下所有的类 注意:当配置了component-can,那么就不需要再配置annotation-config, 因为component-scan已经包含了--> <context:component-scan base-package="edu.nf.xml" > <context:exclude-filter type="aspectj" expression="edu.nf.xml.controller"/> </context:component-scan> <!-- 配置数据源,这里使用druid连接池,(alibaba)开源,所有的连接池都会实现了DataSource接口的规范, 下面就是将druid与spring整合,将连接池纳入spring容器中管理。 注意:druid是可以单独使用,并不一定要整合到spring中,只是通常用到Spring框架都会与数据源进行整合 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 设置数据库的连接属性 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&encoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="root"/> <!-- 连接池属性配置 --> <!-- 连接池最大连接池数量 --> <property name="maxActive" value="200"/> <!-- 初始化连接池时的连接数量 --> <property name="initialSize" value="2"/> <!-- 最小连接数量 --> <property name="minIdle" value="5"/> <!-- 获取连接的等待时间,单位:毫秒 --> <property name="maxWait" value="60000"/> <!-- 连接保持空闲而不被驱逐的最小时间(在规定时间内如果连接未被使用,则被驱逐连接池,直到最小连接数量) --> <property name="minEvictableIdleTimeMillis" value="300000"/> <!-- 检测连接,如果检测到连接空闲时间大于timeBetweenEvictionRunsMillis的值, 则关闭物理连接 --> <property name="timeBetweenEvictionRunsMillis" value="6000"/> <!-- 申请连接时检测,如果空余时间大于timeBetweenEvictionRunsMillis指定的时间, 就执行validationQuery检测连接是否有效 --> <property name="testWhileIdle" value="true"/> <!-- 伪sql语句,用于检查连接是否有用 --> <property name="validationQuery" value="select 1"/> <!-- 申请连接时执行validationQuery检测连接是否有用,默认true,建议设置false,提高性能 --> <property name="testOnBorrow" value="false"/> <!-- 归还连接时是否检测连接是否可用 --> <property name="testOnReturn" value="false"/> <!-- 是否缓存PreparedStatement,MySQL或Oracle建议关闭 --> <property name="poolPreparedStatements" value="false"/> </bean> <!-- 整合mybatis,配置SqlSessionFactoryBean,它将负责创建SqlSessionFactory交由容器管理 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 给实体包下的类定义默认的别名 --> <property name="typeAliasesPackage" value="edu.nf.ch04.entity"/> <!-- 指定mapper映射目录 --> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <!-- 配置分页插件--> <property name="plugins"> <!-- 配置分页插件的拦截器 --> <bean class="com.github.pagehelper.PageInterceptor"> <!-- 配置拦截器属性 --> <property name="properties"> <props> <!-- 指定数据库方言 --> <prop key="helperDialect">mysql</prop> <!-- 分页参数注解支持--> <prop key="supportMethodsArguments">true</prop> <!-- 分页合理化--> <prop key="rowBoundsWithCount">true</prop> </props> </property> </bean> </property> </bean> <!-- 指定扫描的dao接口包, mybatis插件会根据这些接口自动创建相应的实现类, 底层基于动态代理来创建dao接口的实现类 --> <mybatis:scan base-package="edu.nf.xml.dao"/> </beans>
CityServiceMapper.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="edu.nf.xml.dao.CityDao"> <resultMap id="cityMap" type="edu.nf.xml.entity.City"> <id column="city_id" property="cityId"/> <result column="city_name" property="cityName"/> <result column="city_code" property="cityCode"/> <result column="province" property="province"/> </resultMap> <select id="cityInfo" resultMap="cityMap"> select city_id, city_name, city_code, province from city_info </select> <select id="getCity" resultMap="cityMap" parameterType="String"> select city_id, city_name, city_code, province from city_info where city_id = #{cityId} </select> </mapper>
到这里,dao层已经基本配置完毕,现在开始配置service层和controller层
第三步:service层配置
service层主要做事务管理和aop切面
1、创建包:
2、实现业务类:
public interface CityService { /** * 分页,获取城市信息 * @param pageNum 第几页(从0开始) * @param pageSize 每页几条 * @return */ PageInfo<City> cityInfo(@Param("pageNum") Integer pageNum, @Param("pageSize") Integer pageSize); /** * 获取指定城市信息 * @param cityId 城市id * @return */ City getCity(String cityId); }
CityServiceImpl.java:实现CityService接口,dao会在运行时动态生成生成代理实现类而注入
@Service @Transactional(rollbackFor = DataAccessException.class) public class CityServiceImpl implements CityService { @Autowired private CityDao dao; /** * 分页:获取城市信息 * @param pageNum 第几页(从0开始) * @param pageSize 每页几条 * @return 返回分页信息 */ @Override public PageInfo<City> cityInfo(Integer pageNum, Integer pageSize) { try { List<City> list = dao.cityInfo(pageNum, pageSize); PageInfo pageInfo = new PageInfo(list); return pageInfo; } catch (Exception e) { e.printStackTrace(); throw new DataAccessException("获取城市列表失败"); } } /** * 获取指定城市信息 * @param cityId 城市id * @return */ @Override public City getCity(String cityId) { try { return dao.getCity(cityId); } catch (Exception e) { e.printStackTrace(); throw new DataAccessException("获取城市信息失败"); } } }
DataAccessException.java:自定义异常类,继承自RunTimeException
public class DataAccessException extends RuntimeException { public DataAccessException() { super(); } public DataAccessException(String message) { super(message); } public DataAccessException(String message, Throwable cause) { super(message, cause); } }
CityAspect.java:切面类(spring-study ch11-ch14)
public class UserServiceAspect { /** * 前置通知 * @param jp 连接点,通过这个连接点可以获取目标方法 */ public void before(JoinPoint jp){ System.out.println("前置通知,目标方法参数:" + jp.getArgs()[0]); } /** * 环绕通知 * @param pjp 连接点,可以获取目标方法参数以及方法信息以及调用目标方法等等 */ public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知前。。。"); //获取目标方法的的Method对象 MethodSignature ms = (MethodSignature)pjp.getSignature(); Method method = ms.getMethod(); System.out.println("当前调用的目标方法:" + method.getName()); //调用目标方法 Object returnVal = pjp.proceed(); System.out.println("环绕通知后。。。"); return returnVal; } /** * 后置通知 * @param returnVal 目标方法的返回值 */ public void afterReturning(String returnVal){ System.out.println("后置通知,返回参数:" + returnVal); } /** * 异常通知 * @param e 目标方法产生的异常对象 */ public void afterThrowing(Throwable e){ System.out.println("异常通知,异常信息:" + e.getMessage()); } /** * 最终通知 */ public void after(){ System.out.println("最终通知"); } }
要在applicationContext.xml配置文件中添加以下配置:
<!-- 定义切面 --> <bean id="cityAspect" class="edu.nf.xml.service.aspect.CityAspect"/> <!-- 配置aop,基于AspectJ --> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* edu.nf.xml.service.impl.CityServiceImpl.*(..))"/> <aop:aspect ref="cityAspect"> <!-- 装配通知,method对应通知的方法名,pointcut-ref引用上面定义的切入点的id 如果不同的通知想使用不同的切入点,那么使用pointcut属性进行的自定义 --> <!-- 前置通知 --> <aop:before method="before" pointcut-ref="myPointcut"/> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="myPointcut"/> <!-- 后置通知,returnVal属性指定后置通知方法的参数名(参数名称要一致) --> <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/> <!-- 异常通知,throwing属性指定异常通知方法的参数名(名称要一致) --> <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/> <!-- 最终通知 --> <aop:after method="after" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config>
3、添加配置:
在applicationContext.xml配置文件中添加以下配置:
<!-- 装配事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- 启用事务注解处理器 --> <tx:annotation-driven transaction-manager="txManager"/>
4、测试:
CityTest.java,在test模块中创建测试类:
public class CityTest { @Test public void testListCity(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); CityService service = context.getBean("cityService", CityService.class); PageInfo<City> pageInfo = service.cityInfo(0, 5); List<City> list = pageInfo.getList(); list.forEach(city -> System.out.print(city.getCityName() + "++")); } }
运行结果:
第四步:controller层配置
controller主要有处理请求的方法,全局异常处理,拦截器等与springmvc相关的配置
1、创建包和类,及web构面
2、实现业务类:
ControllerAspect.java:全局异常处理,在service层中可能回抛出DataAccessException异常,在这个类中可以处理:
@ControllerAdvice("edu.nf.xml.controller") public class ControllerAspect { @ExceptionHandler(DataAccessException.class) @ResponseBody public ResponseVO handleDataAccessException(DataAccessException e){ ResponseVO vo = new ResponseVO(); vo.setCode(500); vo.setMessage(e.getMessage()); return vo; } }
LoginInterceptor.java:拦截器(springmvc-study ch10*),这里是用作登录拦截,在写拦截器类前需要添加配置信息:
<!-- 配置拦截器 --> <mvc:interceptors> <!-- 可以配置多个拦截器 --> <mvc:interceptor> <!-- 指定哪些url是需要被拦截的 --> <mvc:mapping path="/***"/> <!-- 配出哪些请求不需要被拦截 --> <mvc:exclude-mapping path="/login.html"/> <mvc:exclude-mapping path="/user_login"/> <mvc:exclude-mapping path="/js/**"/> <!-- 配置自定义的拦截器 --> <bean class="edu.nf.xml.controller.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
public class LoginInterceptor implements HandlerInterceptor { /** * 调用controller方法之前调用 * @param request * @param response * @param handler 目标控制器实例 * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("调用controller方法前。。。"); HttpSession session = request.getSession(); if(session.getAttribute("user") == null){ //如果没登录,则重定向回登录页面 response.sendRedirect("login.html"); //返回false,阻止后面的方法执行 return false; } return true; } /** * 再调用controller方法之后执行,但未返回ModelAndView时执行 * 注意:只有再preHandle方法返回true的情况下才会执行 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("调用controller方法之后。。。"); } /** * 方法返回后,视图响应之前调用此方法,在返回ModelAndView后执行 * 注意:这个方法只有在preHandle方法返回true的时候才能调用 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("响应视图。。。"); } }
CityController.java:请求处理器
@RestController @RequestMapping("/city") public class CityController extends BaseController { @Autowired private CityService service; /** * 查询城市列表 * @param pageNum * @param pageSize * @return */ @GetMapping("/city") public ResponseVO listCity(Integer pageNum, Integer pageSize){ PageInfo<City> pageInfo = service.cityInfo(pageNum, pageSize); System.out.println(pageInfo.getList()); return success(pageInfo); } /** * 获取城市信息 * @param cityId * @return */ @GetMapping("/${cityId}") public ResponseVO cityInfo(@PathVariable("cityId") String cityId){ City city = service.getCity(cityId); System.out.println(city); return success(city); } }
3、添加配置
dispatcher-serverlt.xml:在controller层中,主要做servlet的配置
<?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:mvc="http://www.springframework.org/schema/mvc" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描,只负责扫描controller中的类 --> <context:component-scan base-package="edu.nf.xml.controller"/> <!-- mvc注解 --> <mvc:annotation-driven/> <!-- 静态资源 --> <mvc:default-servlet-handler/> <!-- 内部视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 方式一:导入applicationContext.xml配置文件 --> <!-- <import resource="applicationContext.xml"/>--> </beans>
添加了web构面后,在web/WEB-INF目录下就会有一个web.xml配置文件,在这里配置DispatcherServlet和applicationContext.xml
<!-- 配置一个上下文监听器,这个是由spring提供的 注意:监听器会优先于Servlet初始化,因此当监听器初始化一个配置文件时, 会创建一个ioc容器,这个容器就是主容器,而下面通过DispatcherServlet创建的容器 就会自动设置为当前容器的子容器,这就是父子容器的概念,当我们要从容器中获取一个bean对象时, 先从子容器中查找,如果没有则到父容器中查找,反过来,父容器是不能访问子容器的内容。--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- spring提供的字符编码过滤器 --> <filter> <filter-name>encodingFilter</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> <!-- 将request和response的编码保持一致 --> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置核心控制器 servlet--> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
4、测试:
使用ajax异步请求,测试获取城市列表请求:
发送请求:
<script> $(function(){ $.ajax({ url:'city/list_city', data: {'pageNum': 0, 'pageSize': 5}, type:'post', success: function(result){ console.log(result); } }) }) </script>
结果: