美文网首页
Java Maven+Idea 构建SpringMvc+Myba

Java Maven+Idea 构建SpringMvc+Myba

作者: MicoCube | 来源:发表于2019-02-04 17:03 被阅读0次
    • 本文将使用maven与Idea创建一个Springmvc+Mybatis+Ehcache的空项目
    • 本文全部代码
    • mvn命令行创建项目基本结构
    mvn archetype:generate -DgroupId=com.mico.emptyspring -DartifactId=emptyspring -DarchetypeArtifactId=maven-archetype-webapp -DinteractivMode=false
    
    • 使用idea打开项目
    • 修改pom.xml,定义源码版本和编译的class版本,否则maven有个默认的编译版本,可能较低
    <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>
      ......
    <build>
            <finalName>emptyspring</finalName>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <source>${maven.compiler.source}</source>
                            <target>${maven.compiler.target}</target>
                            <encoding>${project.build.sourceEncoding}</encoding>
                        </configuration>
                    </plugin>
                </plugins>
            </pluginManagement>
    </build>
    
    • src/main目录下创建java文件夹,标记为Source root

      Sources
    • 在java文件夹下创建包名


      大概像这样
    • 添加spring依赖到pom.xml

            <!-- 基本包 -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.0.1</version>
            </dependency>
    
            <!-- 文件上传解析 -->
            <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.3.3</version>
            </dependency>
    
            <!--JDBC-->
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.40</version>
            </dependency>
    
    
    
            <!-- jackson -->
            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.9.7</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.7</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>2.9.7</version>
            </dependency>
    
    
    
            <!-- 日志文件管理包 -->
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>1.7.22</version>
            </dependency>
            <!--slf4j-log4j12 代表绑定的是log4j 1.2版本-->
            <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-log4j12</artifactId>
              <version>1.7.22</version>
            </dependency>
            <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
            </dependency>
    
    
    
             <!--核心包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--webmvc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--context-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!-- context-support EhCacheManagerFactoryBean 在这里 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--aop-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--web-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--事物-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--jdbc-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
            <!--orm-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>5.1.3.RELEASE</version>
            </dependency>
    
    
            <!-- mybatis -->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.3.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.6</version>
            </dependency>
    
            <!-- 分页插件 -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>5.1.8</version>
            </dependency>
    
            <!-- mybatis ehcache -->
            <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
            <dependency>
                <groupId>org.mybatis.caches</groupId>
                <artifactId>mybatis-ehcache</artifactId>
                <version>1.1.0</version>
            </dependency>
    
    • 编辑web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath:spring-*.xml
            </param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <filter>
            <filter-name>characterEncodingFilter</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>
            <init-param>
                <param-name>forceEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>characterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
    
        </servlet>
        <servlet-mapping>
            <servlet-name>appServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    </web-app>
    
    • 在resource文件夹下新加spring-mvc.xml[扫描控制层,配置静态资源,消息转换器,视图解析器]
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/mvc
                               http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- 配置自动扫描的包 -->
        <!--<task:annotation-driven/>-->
        <mvc:annotation-driven/>
        <context:component-scan base-package="com.mico.emptyspring.controller"/>
    
    
        <!-- 加载properties配置文件 -->
        <context:property-placeholder location="classpath:*.properties"/>
    
    
        <!-- 处理静态资源 -->
        <!-- <mvc:default-servlet-handler/>  -->
        <!--<mvc:resources mapping="/*.jsp" location="/"/>-->
        <!--<mvc:resources mapping="/*.html" location="/"/>-->
        <!--<mvc:resources mapping="/*.ico" location="/"/>-->
        <mvc:resources mapping="/js/**" location="/js/"/>
        <mvc:resources mapping="/css/**" location="/css/"/>
    
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <mvc:exclude-mapping path="/static/**"/>
                <mvc:exclude-mapping path="/*.html"/>
                <mvc:exclude-mapping path="/favicon.ico"/>
                <bean class="com.mico.emptyspring.interceptor.CommonInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
        <mvc:annotation-driven>
            <mvc:message-converters>
                <bean class="com.mico.emptyspring.convert.MessageConverter"></bean>
            </mvc:message-converters>
        </mvc:annotation-driven>
    
        <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
            <!-- set the max upload size100MB -->
            <property name="maxUploadSize">
                <value>104857600</value>
            </property>
            <property name="maxInMemorySize">
                <value>4096</value>
            </property>
            <property name="defaultEncoding" value="UTF-8"></property>
        </bean>
    
         <!--配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图
    
            如果方法不标示为@ResponseBody,将会走视图解析器,也不会走消息转换器[将对象转为json字符串],
    
            @responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,
            写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,
            在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,
            他的效果等同于通过response对象输出指定格式的数据。@ResponseBody都会在异步获取数据时使用,
            被其标注的处理方法返回的数据将输出到相应流中,客户端获取并显示数据。
         -->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/view/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
    
    </beans>
    
    • 新增spring-mybatis.xml 整合spirng和mybatis
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
    
    
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
    
        <!-- Mybatis 和 Spring的整合 -->
    
        <!-- 自动扫描 ,忽略@Controller注解的类-->
        <context:component-scan base-package="com.mico.emptyspring">
            <context:exclude-filter type="annotation"
                                    expression="org.springframework.stereotype.Controller"></context:exclude-filter>
        </context:component-scan>
    
        <!-- 1.数据源:DriverManagerDataSource -->
        <bean id="dataSource"
              class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true"></property>
            <property name="username" value="root"></property>
            <property name="password" value="micocube"></property>
        </bean>
    
        <!-- 2.Mybatis 的 SqlSession的工厂:SqlSessionFactoryBean dataSource引用数据源 Mybatis
            定义数据源,同意加载配置 -->
        <!--<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
        <!--<property name="dataSource" ref="dataSource"></property>-->
        <!--<property name="configLocation" value="classpath:mybatis-config.xml"></property>-->
        <!--</bean>-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--<property name="mapperLocations" value="classpath:mappers/*.xml"/>-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <!-- <property name="typeAliasesPackage" value="com.tiantian.ckeditor.model" /> -->
        </bean>
    
    
        <!-- 3. Mybatis自动扫描加载Sql映射文件/接口:MapperScannerConfigurer sqlSessionFactory
            basePackage:指定sql映射文件/接口所在的包(自动扫描) -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.mico.emptyspring.dao"></property>
            <!-- 一直不明白为什么sqlSessionFactoryBeanName要用value而不用ref.
            在mybatis-spring1.1.0以前,是通过<property name="sqlSessionFactory" r
            ef="sqlSessionFactory"/>将SqlSessionFactory对象注入到sqlSessionFactory,
            这样做可能会有一个问题,就是在初始化MyBatis时,jdbc.properties文件还没被加载进来,
            dataSource的属性值没有被替换,就开始构造sqlSessionFactory类,属性值就会加载失败。
            在1.1.0以后,MapperScannerConfigure提供了String类型的sqlSessionFactoryBeanName,
            这样将bean name注入到sqlSessionFactoryBeanName,这样就会等到spring初始化完成后,
            再构建sqlSessionFactory。-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        </bean>
    
    
        <!-- 4.事务管理:DataSourceTransactionManager dataSource 引用上面定义好的数据源 -->
        <bean id="txManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
    
        <!-- 5.使用声明式事务: transaction-manager = "txManager" tx:advice 这种 是用 aop方式管理事物
            annotation-driven 这种是注解方式管理事物 第一种方式,需要在spring配置文件配置一些参数 第二种方式,需要在 类里 加一些注解进行事物管理用一种就行,没必须都用 -->
        <tx:annotation-driven transaction-manager="txManager"/>
    </beans>
    
    • 新增mybatis-config.xml,对mybatis进行配置
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <!-- mybatis 配置文件文档 http://www.mybatis.org/mybatis-3/zh/configuration.html -->
    
        <!-- 全局参数 Mappers文件里可以重写,查看UserMapper.xml-->
        <settings>
            <!-- 配置打印 SQL 到控制台 -->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
            <!-- 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
            <setting name="cacheEnabled" value="true"/>
            <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
            <setting name="aggressiveLazyLoading" value="true"/>
            <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
            <setting name="multipleResultSetsEnabled" value="true"/>
            <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
            <setting name="useColumnLabel" value="true"/>
            <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
            <setting name="useGeneratedKeys" value="true"/>
            <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
            <setting name="autoMappingBehavior" value="PARTIAL"/>
            <!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
            <setting name="defaultExecutorType" value="SIMPLE"/>
            <!-- 使用驼峰命名法转换字段。 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
            <setting name="localCacheScope" value="SESSION"/>
            <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->
            <setting name="jdbcTypeForNull" value="NULL"/>
        </settings>
    
    
        <!--设置一个别名 -->
        <typeAliases>
            <typeAlias type="com.mico.emptyspring.entity.User" alias="User"/>
        </typeAliases>
    
        <!-- 启用pageHelper插件 在代码中使用
        Page page = PageHelper.startPage(pageNum, pageSize, true);在查询前使用
        true表示需要统计总数,这样会多进行一次请求select count(0); 省略掉true参数只返回分页数据。
    
        1.统计总数,(将SQL语句变为 select count(0) from xxx,只对简单SQL语句其效果,复杂SQL语句需要自己写)
            Page<?> page = PageHelper.startPage(1,-1);
            long count = page.getTotal();
    
    
        2.分页,pageNum - 第N页, pageSize - 每页M条数
            2.1只分页不统计(每次只执行分页语句)
                PageHelper.startPage([pageNum],[pageSize]);
                List<?> pagelist = queryForList( xxx.class, "queryAll" , param);
                //pagelist就是分页之后的结果
            2.2 分页并统计(每次执行2条语句,一条select count语句,一条分页语句)
                适用于查询分页时数据发生变动,需要将实时的变动信息反映到分页结果上
                Page<?> page = PageHelper.startPage([pageNum],[pageSize],[iscount]);
                List<?> pagelist = queryForList( xxx.class , "queryAll" , param);
                long count = page.getTotal();
                也可以 List<?> pagelist = page.getList();  获取分页后的结果集
    
    
        3.使用PageHelper查全部(不分页)
            PageHelper.startPage(1,0);
            List<?> alllist = queryForList( xxx.class , "queryAll" , param);
    
        4.PageHelper的其他API
            //获取orderBy语句
            String orderBy = PageHelper.getOrderBy();
            Page<?> page = PageHelper.startPage(Object params);
            Page<?> page = PageHelper.startPage(int pageNum, int pageSize);
            Page<?> page = PageHelper.startPage(int pageNum, int pageSize, boolean isCount);
            Page<?> page = PageHelper.startPage(pageNum, pageSize, orderBy);
            //isReasonable分页合理化,null时用默认配置
            Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable);
            //isPageSizeZero是否支持PageSize为0,true且pageSize=0时返回全部结果,false时分页,null时用默认配置
            Page<?> page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable, isPageSizeZero);
    
        5.默认值
            //RowBounds参数offset作为PageNum使用 - 默认不使用
            private boolean offsetAsPageNum = false;
            //RowBounds是否进行count查询 - 默认不查询
            private boolean rowBoundsWithCount = false;
            //当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果
            private boolean pageSizeZero = false;
            //分页合理化
            private boolean reasonable = false;
            //是否支持接口参数来传递分页参数,默认false
            private boolean supportMethodsArguments = false;
    
        6.有一个安全性问题,需要注意一下,不然可能导致分页错乱
            什么时候会导致不安全的分页?
            PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
            只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。
            如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。
            但是如果你写出下面这样的代码,就是不安全的用法:
    
            PageHelper.startPage(1, 10);
            List<Country> list;
            if(param1 != null){
                list = countryMapper.selectIf(param1);
            } else {
                list = new ArrayList<Country>();
            }
    
            这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
            上面这个代码,应该写成下面这个样子:
    
            List<Country> list;
            if(param1 != null){
                PageHelper.startPage(1, 10);
                list = countryMapper.selectIf(param1);
            } else {
                list = new ArrayList<Country>();
            }
    
            这种写法就能保证安全。
            如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
    
            List<Country> list;
            if(param1 != null){
                PageHelper.startPage(1, 10);
                try{
                    list = countryMapper.selectAll();
                } finally {
                    PageHelper.clearPage();
                }
            } else {
                list = new ArrayList<Country>();
            }
    
            这么写很不好看,而且没有必要。
    
        PageHelper的优点是,分页和Mapper.xml完全解耦。实现方式是以插件的形式,
        对Mybatis执行的流程进行了强化,添加了总数count和limit查询。属于物理分页。
        5.0 是用这个类
        com.github.pagehelper.PageInterceptor
        因为PageHelper类,继承Interceptor
        public class PageHelper extends PageMethod implements Dialect
        -->
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <!--自4.0.0以后的版本已经可以自动识别数据库了,所以不需要我们再去指定数据库-->
                <!--<property name="dialect" value="mysql"/>-->
                <!--<property name="offsetAsPageNum" value="false"/>-->
                <!--<property name="rowBoundsWithCount" value="false"/>-->
                <!--<property name="pageSizeZero" value="true"/>-->
                <!--<property name="reasonable" value="false"/>-->
                <!--<property name="supportMethodsArguments" value="false"/>-->
                <!--<property name="returnPageInfo" value="none"/>-->
            </plugin>
        </plugins>
    
        <mappers>
            <package name="com.mico.emptyspring.dao"></package>
        </mappers>
    </configuration>
    
    • 新增spring-ehcache.xml,整合spring和ehcache
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cache="http://www.springframework.org/schema/cache"
           xmlns="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/cache
            http://www.springframework.org/schema/cache/spring-cache.xsd">
    
    
        <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
        <cache:annotation-driven cache-manager="ehcacheManager"/>
        <!-- cacheManager工厂类,指定ehcache.xml的位置 -->
        <bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
            <property name="configLocation" value="classpath:ehcache.xml"/>
        </bean>
        <!-- 声明cacheManager -->
        <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
            <property name="cacheManager" ref="ehcacheManagerFactory"/>
        </bean>
    </beans>
    
    • 编写ehcache.xml,配置ehcache
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
             updateCheck="false">
    
        <!--添加updateCheck="false",不添加会报错-->
        <!--[DEBUG-console] 2019/01/28,15:08:07.253|Update check failed:-->
        <!--java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?pageID=update.properties&kitID=ehcache.default&id=-1407972861&os-name=Mac+OS+X&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.8.0_131&platform=x86_64&tc-version=2.6.11&tc-product=Ehcache+Core+2.6.11&source=Ehcache+Core&uptime-secs=1&patch=UNKNOWN-->
    
    
        <!--
         属性说明:
             diskStore:指定数据在磁盘中的存储位置。
             defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
            以下属性是必须的:
                 maxElementsInMemory - 在内存中缓存的element的最大数目
                 maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
                 eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,
                        如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
                 overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
                以下属性是可选的:
                 timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,
                    这些数据便会删除,默认值是0,也就是可闲置时间无穷大
                 timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
                diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
                 diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
                 diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。
                    每个120s,相应的线程会进行一次EhCache中数据的清理工作
                 memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候,
                    移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
         -->
        <diskStore path="/data/ehcache"/>
        <defaultCache
                maxElementsInMemory="1000"
                maxElementsOnDisk="10000000"
                eternal="false"
                overflowToDisk="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    
        <cache name="baseCache" eternal="true" maxElementsInMemory="1000" maxElementsOnDisk="10000"
               overflowToDisk="true" diskPersistent="false" timeToIdleSeconds="0"
               timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>
    
    </ehcache>
    
    • 编写log4j.properties,日志配置文件
    log4j.rootLogger=DEBUG,file,stdout
    #log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    #log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    # Pattern to output the caller's file name and line number.
    #log4j.appender.stdout.layout.ConversionPattern=[%p-server] %d{yyyy/MM/dd,HH:mm:ss.SSS}|%m%n
    #log4j.appender.stdout.Threshold=debug
    ### 输出到日志文件 ###
    log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.file.File=/data/www/file/logs/mico/server.log
    log4j.appender.file.Append=true
    log4j.appender.file.com.coding=INFO
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p-server] %d{yyyy/MM/dd,HH:mm:ss.SSS}|%m%n
    #
    #log4j.logger.java.sql.Statement = debug
    #log4j.logger.java.sql.PreparedStatement = debug
    #log4j.logger.java.sql.ResultSet =debug
    #log4j.logger.java.sql.Connection =debug
    #log4j.logger.com.ibatis =debug
    #
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    #log4j.appender.stdout.java.sql.Statement = info
    #log4j.appender.stdout.java.sql.PreparedStatement = debug
    #log4j.appender.stdout.java.sql.ResultSet =debug
    #log4j.appender.stdout.java.sql.Connection =debug
    log4j.appender.stdout.com.coding=debug
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=[%p-console] %d{yyyy/MM/dd,HH:mm:ss.SSS}|%m%n
    
    • 编写拦截器记录请求参数,请求地址,请求ip,计算请求处理时间
    package com.mico.emptyspring.interceptor;
    
    
    import com.mico.emptyspring.http.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.HashMap;
    import java.util.Map;
    
    public class CommonInterceptor implements HandlerInterceptor {
    
        private static Logger log = LoggerFactory.getLogger(CommonInterceptor.class);
    
        public CommonInterceptor() {
            super();
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            response.setHeader("Access-Control-Allow-Origin", "*");
            request.setAttribute(SpringMVCConstant.PROCESS_START, System.currentTimeMillis());
            Map params = new HashMap();
            request.getParameterMap().keySet().forEach(key -> {
                params.put(key, request.getParameterMap().get(key));
            });
            request.setAttribute(SpringMVCConstant.REQUEST_PARAMS, params);
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
            if (exception == null) {
                LogContent lc = new LogContent(
                        System.currentTimeMillis() - (Long) request.getAttribute(SpringMVCConstant.PROCESS_START) + "ms",
                        request.getAttribute(SpringMVCConstant.REQUEST_PARAMS),
                        request.getAttribute(SpringMVCConstant.RESULT_FOR_LOG),
                        request.getServletPath(),
                        SpringMVCContext.getIp(request));
                String logContent = GlobalObject.getJsonMapper().writeValueAsString(lc);
                log.info(logContent);
            } else {
                LogContentWithException lc = new LogContentWithException(
                        System.currentTimeMillis() - (Long) request.getAttribute(SpringMVCConstant.PROCESS_START) + "ms",
                        request.getAttribute(SpringMVCConstant.REQUEST_PARAMS),
                        request.getAttribute(SpringMVCConstant.RESULT_FOR_LOG),
                        request.getServletPath(),
                        SpringMVCContext.getIp(request),
                        exception);
                String logContent = GlobalObject.getJsonMapper().writeValueAsString(lc);
                log.error(logContent, exception);
    
                GlobalObject.getJsonMapper().writeValue(response.getOutputStream(), new HttpResult(HttpStatus.SERVER_FAIL));
                response.getOutputStream().close();
            }
        }
    
    
        private class LogContent {
            public String consumed;
            public Object params;
            public Object result;
            public String url;
            public String ip;
    
            public LogContent(String consumed, Object params, Object result, String url, String ip) {
                this.consumed = consumed;
                this.params = params;
                this.result = result;
                this.url = url;
                this.ip = ip;
            }
        }
    
        private class LogContentWithException extends LogContent {
            public String throwable;
    
            public LogContentWithException(String consumed, Object params, Object result, String url, String ip, Exception throwable) {
                super(consumed, params, result, url, ip);
                this.throwable = throwable.toString();
            }
        }
    
    
    }
    
    
    • 编写消息转换器[@ResponseBody后才选择消息转换器]
    package com.mico.emptyspring.convert;
    
    import com.mico.emptyspring.http.GlobalObject;
    import com.mico.emptyspring.http.SpringMVCConstant;
    import com.mico.emptyspring.http.SpringMVCContext;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.http.converter.HttpMessageNotWritableException;
    
    import java.io.IOException;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @auther coding on 2018/3/9.
     * email: ldscube@gmail.com
     */
    public class MessageConverter extends AbstractHttpMessageConverter {
        private static final Log log = LogFactory.getLog(MessageConverter.class);
        private List supportedMediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON_UTF8);
    
    
        @Override
        public List<MediaType> getSupportedMediaTypes() {
            return supportedMediaTypes;
        }
    
        @Override
        protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
            return null;
        }
    
    
        @Override
        protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
            log.info("writeInternal:"+o.toString());
            SpringMVCContext.getRequest().setAttribute(SpringMVCConstant.RESULT_FOR_LOG, o);
            GlobalObject.getJsonMapper().writeValue(httpOutputMessage.getBody(), o);
        }
    
        @Override
        protected boolean supports(Class aClass) {
            return true;
        }
    
    }
    
    • 创建数据表
    create table role
    (
      id int auto_increment
        primary key,
      name varchar(255) null
    )
    ;
    
    create table user
    (
      id int auto_increment
        primary key,
      username varchar(20) null,
      password varchar(526) null
    )
    ;
    
    create table user_roles
    (
      user_id int not null,
      roles_id int not null
    )
    ;
    
    create index FK55itppkw3i07do3h7qoclqd4k
      on user_roles (user_id)
    ;
    
    create index FKj9553ass9uctjrmh0gkqsmv0d
      on user_roles (roles_id)
    ;
    
    
    INSERT INTO test.role (id,name) VALUES (1,'User');
    INSERT INTO test.user (id,username, password) VALUES (1,'root', '$2a$10$TeayMIrpuDwrpLHL5QsNpOcPeE/Kx3c4UYbi4NQzNkfKgf9YtL6F2');
    INSERT INTO test.user_roles (user_id, roles_id) VALUES (1, 1);
    
    java -jar ./src/main/resources/utils/mybatis-generator-core-1.3.7.jar -configfile ./src/main/resources/mybatis-generator.xml -overwrite
    
    • 生成的UserMapper如下所示
    package com.mico.emptyspring.dao;
    
    import com.mico.emptyspring.entity.User;
    import org.apache.ibatis.annotations.*;
    import org.apache.ibatis.type.JdbcType;
    
    import java.util.List;
    
    public interface UserMapper {
        @Delete({
                "delete from user",
                "where id = #{id,jdbcType=INTEGER}"
        })
        int deleteByPrimaryKey(Integer id);
    
        @Insert({
                "insert into user (id, username, ",
                "password)",
                "values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, ",
                "#{password,jdbcType=VARCHAR})"
        })
        int insert(User record);
    
        @Select({
                "select",
                "id, username, password",
                "from user",
                "where id = #{id,jdbcType=INTEGER}"
        })
        @Results({
                @Result(column = "id", property = "id", jdbcType = JdbcType.INTEGER, id = true),
                @Result(column = "username", property = "username", jdbcType = JdbcType.VARCHAR),
                @Result(column = "password", property = "password", jdbcType = JdbcType.VARCHAR)
        })
        User selectByPrimaryKey(Integer id);
    
        @Select({
                "select",
                "id, username, password",
                "from user"
        })
        @Results({
                @Result(column = "id", property = "id", jdbcType = JdbcType.INTEGER, id = true),
                @Result(column = "username", property = "username", jdbcType = JdbcType.VARCHAR),
                @Result(column = "password", property = "password", jdbcType = JdbcType.VARCHAR)
        })
        List<User> selectAll();
        @Update({
                "update user",
                "set username = #{username,jdbcType=VARCHAR},",
                "password = #{password,jdbcType=VARCHAR}",
                "where id = #{id,jdbcType=INTEGER}"
        })
        int updateByPrimaryKey(User record);
    }
    
    • 该操作生成6个java文件:


      mybatis-generator
    • 创建UserService接口
    package com.mico.emptyspring.service;
    
    
    import com.mico.emptyspring.entity.User;
    
    public interface UserProcessService {
         boolean login(User user);
         User findByUserId(User u);
    }
    
    
    • 创建UserService实现
    package com.mico.emptyspring.service;
    
    import com.mico.emptyspring.dao.UserMapper;
    import com.mico.emptyspring.entity.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    
    @Service
    @Transactional(rollbackFor = Exception.class)
    public class UserProcessServiceImpl implements UserProcessService {
    
        @Autowired
        private UserMapper userDao;
    
    
        public boolean login(User userParam) {
    //      Page page = PageHelper.startPage(pageNum, pageSize, true);
            User user = findByUserId(userParam);
            boolean loginSuccess = user == null ? false : true;
            return loginSuccess;
        }
    
        /**
         * Cacheable 会缓存方法的返回值,
         *
         * @param u
         * @return
         *
         */
    
    //    @Cacheable(value = "baseCache", key = "#u.id")
        public User findByUserId(User u) {
            User user = userDao.selectByPrimaryKey(u.getId());
            return user;
        }
    
    
    }
    
    
    • 创建UserProcessController控制器
    package com.mico.emptyspring.controller;
    
    
    import com.mico.emptyspring.entity.User;
    import com.mico.emptyspring.service.UserProcessService;
    import com.mico.emptyspring.utils.EhcacheUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.ehcache.EhCacheCacheManager;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    
    
    @Controller
    @RequestMapping("/user")
    public class UserProcessController {
    
        @Autowired
        private UserProcessService userProcessService;
        @Resource
        private EhCacheCacheManager ehcacheManager;
    
        @RequestMapping("/index")
        public String index() {
            User user = new User();
            user.setId(1);
            userProcessService.findByUserId(user);
            EhcacheUtils.listAllCache(ehcacheManager);
            return "inner";
        }
    
        /**
         *
         * @responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,
         *  写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,
         *  在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,
         *  他的效果等同于通过response对象输出指定格式的数据。@ResponseBody都会在异步获取数据时使用,
         *  被其标注的处理方法返回的数据将输出到相应流中,客户端获取并显示数据。
         * @return
         */
        @ResponseBody
        @RequestMapping("/json")
        public User json() {
            User user = new User();
            user.setId(1);
            User userId = userProcessService.findByUserId(user);
            return userId;
        }
    }
    
    
    • 如何使用ehcache开启mybatis的二级缓存?
      • 要缓存的entity先implements Serializable
      • mybatis注解形式接口,比如@Insert,@Selet,在接口上添加@CacheNamespace注解即可,比如
        package com.mico.emptyspring.dao;
      
        @CacheNamespace
        public interface UserMapper {
           ......
        }
      
      怎么才说明hit中了缓存?eg:
      Cache Hit Ratio [com.mico.emptyspring.dao.UserMapper]: 0.5
      • mybatis接口+mapper.xml文件,在mapper.xml文件中,添加配置,官方文档
      <mapper namespace="org.acme.FooMapper">
      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      ...
      </mapper>
      
    • entity中如何设置主键自增?
            @Options(useGeneratedKeys = true,keyProperty = "id")
            @Insert({
                    "insert into user (id, username, ",
                    "password)",
                    "values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, ",
                    "#{password,jdbcType=VARCHAR})"
            })
            int insert(User record);
    
    • 日志中为什么频繁出现Creating a new SqlSession?
      • Spring与MyBatis整合时,MyBatis的一级缓存在没有事务存在的时候失效。
      • 在未开启事务的情况之下,每次查询,spring都会关闭旧的sqlSession而创建新的sqlSession,因此此时的一级缓存是没有启作用的;
      • 在开启事务的情况之下,spring使用threadLocal获取当前资源绑定同一个sqlSession,因此此时一级缓存是有效的。
    • 为什么在spring-mvc.xml只扫描控制层,spring-mybatis.xml中排除了控制层?
      • 只在spring-mybatis.xml中配置<context:component-scan base-package="com.mico.emptyspring"/>
        启动正常,但是任何请求都不会被拦截,简而言之就是@Controller失效

      • 只在spring-mvc.xml中配置上述配置
        启动正常,请求也正常,但是事物失效,也就是不能进行回滚

      • 在spring-mvc.xml和spring-mybatis.xml中都配置上述信息
        启动正常,请求正常,也是事物失效,不能进行回滚

      • 在spring-mybatis.xml中配置如下<context:component-scan base-package="com.mico.emptyspring" />在spring-mvc.xml中配置如下<context:component-scan base-package="com.mico.emptyspring.controller" />此时启动正常,请求正常,事物也正常了。

      • 结论:org.springframework.web.servlet.DispatcherServletcontextConfigLocation属性配置的xml中只需要扫描所有带@Controller注解的类,在其他配置中可以扫描所有其他带有注解的类(也可以过滤掉带@Controller注解的类)。

    相关文章

      网友评论

          本文标题:Java Maven+Idea 构建SpringMvc+Myba

          本文链接:https://www.haomeiwen.com/subject/wommjqtx.html