环境搭建

总体目标
image.png

一、项目结构搭建

  1. 模块关系
  2. parent模块仅仅用来确定各个Maven依赖的版本
  3. webuicomponententity模块继承自parent模块
  4. utilreverse模块属于独立工程,不参与继承与聚合
  5. webui依赖于componentcomponent依赖于entityutil

image.png

  1. 各个工程的打包方式:
  1. <!--parent-->
  2. <groupId>org.example</groupId>
  3. <artifactId>crowdfunding01-admin-parent</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. <packaging>pom</packaging>
  6. <!--webui-->
  7. <packaging>war</packaging>
  8. <!--其他的模块都是默认打包方式(jar)-->
IDEA中创建多模块工程:

    先创建一个Empty Project(空工程),创建完成后一个个添加各个Maven模块

    由于webui模块,需要打成war包,因此需要有web工程的目录结构,可以通过在Modules中添        加,产生对应的结构:

image.png

 <packaging>pom</packaging>
    <modules>
        <module>crowdfunding02-admin-webui</module>
        <module>crowdfunding03-admin-component</module>
        <module>crowdfunding04-admin-entity</module>
    </modules>


    <properties>
        <fall.spring.version>4.3.20.RELEASE</fall.spring.version>
        <fall.spring.security.version>4.2.10.RELEASE</fall.spring.security.version>
    </properties>


    <dependencyManagement>
        <dependencies>

            <!-- Spring 依赖 -->
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${fall.spring.version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${fall.spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${fall.spring.version}</version>
            </dependency>
            <!-- Spring AOP -->
            <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/cglib/cglib -->
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2</version>
            </dependency>

            <!-- 数据库依赖 -->
            <!-- MySQL 驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.15</version>
            </dependency>
            <!-- 数据源 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <!-- <version>1.0.31</version>-->
                <version>1.1.17</version>
            </dependency>
            <!-- MyBatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.2.8</version>
            </dependency>
            <!-- MyBatis 与 Spring 整合 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.2.2</version>
            </dependency>
            <!-- MyBatis 分页插件 -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>4.0.0</version>
            </dependency>

            <!-- 日志 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.7</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
            <!-- 其他日志框架的中间转换包 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jul-to-slf4j</artifactId>
                <version>1.7.25</version>
            </dependency>

            <!-- Spring 进行 JSON 数据转换依赖 -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.11.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.11.0</version>
            </dependency>

            <!-- JSTL 标签库 -->
            <dependency>
                <groupId>jstl</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>

            <!-- junit 测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>

            <!-- 引入 Servlet 容器中相关依赖 -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>

            <!-- JSP 页面使用的依赖 -->
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.1.3-b06</version>
                <scope>provided</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.8.5</version>
            </dependency>


            <!-- SpringSecurity 依赖配置 -->

            <!-- SpringSecurity 对 Web 应用进行权限管理 -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${fall.spring.security.version}</version>
            </dependency>
            <!-- SpringSecurity 配置 -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${fall.spring.security.version}</version>
            </dependency>
            <!-- SpringSecurity 标签库 -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-taglibs</artifactId>
                <version>${fall.spring.security.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>
    模块创建时应该选择好父模块

二、数据库构建与逆向

1、建表:

CREATE DATABASE project_rowd CHARACTER SET utf8;

USE project_rowd;
drop table if exists t_admin;                # 如果存在t_admin则删除存在的表
    CREATE TABLE t_admin (
    id INT NOT NULL auto_increment,            # 主键
    login_acct VARCHAR ( 255 ) NOT NULL,    # 登录账号
    user_pswd CHAR ( 32 ) NOT NULL,            # 登录密码
    user_name VARCHAR ( 255 ) NOT NULL,        # 昵称
    email VARCHAR ( 255 ) NOT NULL,            # 邮件地址
    create_time CHAR ( 19 ),                # 创建时间
    PRIMARY KEY ( id )                         # 设置主键
);
进行基于Maven的逆向工程(根据已存在的表,在项目中逆向生成对应的实体类、Mapper文件、Mapper接口)

2、在reverse模块中进行逆向:

1)、pom.xml中导入依赖

 <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>
    </dependencies>
    <!-- 控制Maven在构建过程中的相关配置 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>

                <dependencies>
                    <!-- 逆向工程核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!--MySQL驱动-->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.49</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

2)、编写generatorConfig.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="atguiguTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 数据库链接URL、用户名、密码 -->
        <jdbcConnection
                driverClass="com.mysql.cj.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/project_rowd?serverTimezone=UTC"
                userId="root"
                password="root">
        </jdbcConnection>

        <!--
        默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer
            true,把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal
        -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--
        生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径,如./src/main/java,
        也可以使用“MAVEN”来自动生成,这样生成的代码会在target/generatord-source目录下
        -->
        <!--<javaModelGenerator targetPackage="com.joey.mybaties.test.pojo" targetProject="MAVEN">-->
        <javaModelGenerator targetPackage="crowd.entity" targetProject=".\src\main\java">
            <!--是否让schema作为包的后缀-->
            <property name="enableSubPackages" value="false"/>
            <!-- 从数据库返回的值被清理前后的空格  -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--对应的mapper.xml文件  -->
        <sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\java">
            <!--是否让schema作为包的后缀-->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 对应的Mapper接口类文件 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="org.fall.mapper" targetProject=".\src\main\java">
            <!--是否让schema作为包的后缀-->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 数据库表名与需要的实体类对应映射的指定 -->
        <table tableName="t_admin" domainObjectName="Admin"/>
    </context>
</generatorConfiguration>
        在IDEA中进行逆向工程的方法:

1环境搭建 - 图4

运行完后,应当对产生的所有文件各归各位(Mapper接口放入component的mapper包下;实体类放入entity模块的entity包;xxxMapper.xml放入webui的resources文件夹下(xml放在web模块下方便寻找)

三、通过父工程管理依赖版本

在父工程通过dependencyManagement标签管理依赖版本,但是在子工程正式通过dependencies标签导入依赖前,这些依赖并不会生效

<!--通过properties标签指定一些需要重用的版本号,方便在后面调用-->
<properties>
    <fall.spring.version>4.3.20.RELEASE</fall.spring.version>
    <fall.spring.security.version>4.2.10.RELEASE</fall.spring.security.version>
</properties>

<!--依赖管理-->
<dependencyManagement>
    <dependencies>

        <!-- Spring 依赖 -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${fall.spring.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${fall.spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${fall.spring.version}</version>
        </dependency>
        <!-- Spring AOP -->
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>

        <!-- 数据库依赖 -->
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>
        <!-- MyBatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>
        <!-- MyBatis 与 Spring 整合 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <!-- MyBatis 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.0.0</version>
        </dependency>

        <!-- 日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!-- 其他日志框架的中间转换包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>

        <!-- Spring 进行 JSON 数据转换依赖 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.0</version>
        </dependency>

        <!-- JSTL 标签库 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- junit 测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- 引入 Servlet 容器中相关依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

        <!-- JSP 页面使用的依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1.3-b06</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

        <!-- SpringSecurity 对 Web 应用进行权限管理 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${fall.spring.security.version}</version>
        </dependency>
        <!-- SpringSecurity 配置 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${fall.spring.security.version}</version>
        </dependency>
        <!-- SpringSecurity 标签库 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${fall.spring.security.version}</version>
        </dependency>

    </dependencies>
</dependencyManagement>

四、Spring整合MyBatis

1、配置Maven依赖

component模块的pom.xml配置一些必要的依赖

<dependencies>
    <!--依赖entity-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>crowdfunding04-admin-entity</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!--依赖util-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>crowdfunding05-common-util</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- Spring 依赖 -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>commons-logging</artifactId>
                <groupId>commons-logging</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
    <!-- Spring AOP -->
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
    </dependency>

    <!-- 数据库依赖 -->
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
    </dependency>
    <!-- MyBatis 与 Spring 整合 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
    </dependency>
    <!-- MyBatis 分页插件 -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
    </dependency>
    <!-- Spring 进行 JSON 数据转换依赖 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- JSTL 标签库 -->
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>
</dependencies>
    **webui模块**配置依赖于component,并引入一些scope为test的依赖(如junit)
<dependencies>
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>crowdfunding03-admin-component</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>
</dependencies>
2、创建各配置文件

    ①resources/mybatis/**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>
        <!--Spring与MyBatis整合后,MyBatis的配置文件可有可不有-->
    </configuration>
    ②resources/**spring-persist-mybatis.xml**(Spring配置文件,用于整合MyBatis)
    <!--引入properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--数据源配置-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
    </bean>

    <!--配置SqlSessionFactory(设置mybatis配置文件的路径、mapper.xml文件的路径、注入数据源)-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置与mapper.xml对应的mapper接口的包路径-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.fall.mapper"/>
    </bean>
    ③resources/**jdbc.properties**(存放数据库连接信息)
    jdbc.user=root
    jdbc.password=root
    jdbc.url=jdbc:mysql://localhost:3306/project_rowd?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
    jdbc.driver=com.mysql.cj.jdbc.Driver

2、测试整合是否成功

/**
 * RunWith与ContextConfiguration指定xml的作用与
 * ApplicationContext context = new ClassPathXmlApplicationContext("spring-persist-mybatis.xml");类似
 * 前者通过让测试在Spring容器环境下执行,使得DataSource可以被自动注入,后者导入Spring配置文件
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"})
public class TestConnection {
    @Autowired
    DataSource dataSource;

    @Test
    public void test01() throws SQLException {
        //ApplicationContext context = new ClassPathXmlApplicationContext("spring-persist-mybatis.xml");
        //DataSource dataSource = context.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
    }

可以打印出connection的数据,而不是报空指针异常,得出结论:整合成功。

五、配置日志系统

使用日志的原因:

在实际开发中,如果在所有想查看的地方都通过System.out来打印,会给项目上线带来很多问题<br />    System.out本质是一个IO操作,通常IO操作比较消耗性能。如果项目中过多的System.out,则可能对性能造成影响,而如果使用日志系统,就可以通过控制日    志级别,来**批量控制打印信息**。

这里使用slf4j+logback代替Spring默认使用的commons-loggin日志包。

1、在component中增加依赖

<!-- 日志 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
</dependency>

2、使用日志打印的方法

@Test
public void test03(){
    //获取Logger对象,这里传入的Class就是当前打印日志的类
    Logger logger = LoggerFactory.getLogger(TestConnection.class);
    //等级 DEBUG < INFO < WARN < ERROR
    logger.debug("I am DEBUG!!!");

    logger.info("I am INFO!!!");

    logger.warn("I am WARN!!!");

    logger.error("I am ERROR!!!");

}

六、配置声明式事务

配置声明式事务的**目的**:希望指定的事务方法中,如果存在多个数据库操作,则要么一起提交,要么一起回滚(rollback),即:事务方法中多个数据库操作,只要有一个失败,则所有操作回滚。

1、声明式事务所依赖的包

(前面已经加入了Maven依赖,不需要多次操作了)

<!-- Spring AOP -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
</dependency>

2、配置针对事务的Spring配置文件

spring-persist-tx.xml

事务方法一般定义在service层
<!--将org.fall.service包中的组件扫描入容器,因为事务方法一般定义在service层-->
<context:component-scan base-package="org.fall.service"/>

<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置AOP-->
<aop:config>
    <!--配置切入点表达式-->
    <!--public void org.fall.service.impl.adminServiceImpl(Admin admin)-->
    <aop:pointcut id="txPointcut" expression="execution(* *..*ServiceImpl.*(..))"/>

    <!--关联事务通知与切入点-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!--name属性指定当前要配置的事务方法的方法名,符合名字的配置对应规则-->
        <!--查询方法通常设置为只读,便于数据库根据只读属性进行性能优化-->
        <tx:method name="get*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="find*" read-only="true"/>
        <tx:method name="count*" read-only="true"/>

        <!--涉及增删改查操作的方法的配置-->
        <!--propagation属性配置事务方法的传播行为-->
            <!--
                默认行为:REQUIRED,表示当前方法必须运行在事务中,如果没有事务,则开启事务,在自己的事务中运行。
                    如果已经有了已开启的事务,则在当前事务中运行。有可能和其他方法共用同一个事务
                建议设置:REQUIRES_NEW,表示当前方法必须运行在事务中,如果没有事务,则开启事务,在自己的事务中运行。
                    和 REQUIRED 的区别是就算现在已经有了已开启的事务,也一定要开启自己的事务,避免和其他方法共用同一个事务。
            -->
        <!--rollback-for:表示触发什么异常时,进行回滚;默认值:运行时异常,建议设置为运行时异常+编译期异常-->
        <tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
        <tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
        <tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
        <!--需要了解的是,如果方法名没有匹配的name,则该方法的事务不会生效-->
    </tx:attributes>
</tx:advice>
配置完成后,在org.fall.service包下,进行数据库操作时,触发异常,即发生回滚,不会依然执行

七、表述层环境搭建

首先需要确定已经导入了SpringMVC的依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
</dependency>

1、配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置ContextLoaderListener,加载Spring配置文件-->
    <!--contextConfigLocation需要的内容-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-persist-*.xml</param-value>
    </context-param>
    <!--将ContextLoaderListener加入容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--配置CharacterEncodingFilter,解决乱码问题-->
    <!--如果web.xml中存在多个Filter,则此Filter必须作为过滤器链的第一个Filter-->
    <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>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--强制响应进行编码-->
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!--设置过滤器过滤的请求的路径(/*表示全部请求)-->
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置DispatcherServlet(即配置SpringMVC的前端控制器)-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--指定SpringMVC配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-web-mvc.xml</param-value>
        </init-param>

        <!--使DispatcherServlet在Web应用启动时就创建对象并初始化-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--根据请求的扩展名决定是否交给SpringMVC来处理-->
        <!--
            请求的扩展名应与预计的响应体格式相同
            要求json数据则后缀.json;    要求页面则后缀.html
        -->
        <url-pattern>*.html</url-pattern>
        <url-pattern>*.json</url-pattern>
    </servlet-mapping>
</web-app>

2、配置SpringMVC配置文件

<!--配置包扫描,这里将controller层放在mvc包下,因此配置扫描mvc-->
<context:component-scan base-package="org.fall.mvc"/>

<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/"/>
    <property name="suffix" value=".jsp"/>
</bean>

<!--启动注解驱动-->
<mvc:annotation-driven/>

3、测试SSM整合是否成功

**Controller控制器代码:**
@Controller
public class TestHandler {

    @Autowired
    AdminService adminService;

    @RequestMapping("/test/ssm.html")
    public String testSSM(Model model){
        //Admin admin = adminService.queryAdmin(1);
        List<Admin> admins = adminService.getAll();
        model.addAttribute("admins", admins);
        return "target";
    }
}
**前端代码:**index.jsp
<%-- 一、无base标签:--%>
    <a href="${pageContext.request.contextPath}/test/ssm.html">测试页面</a>

<%-- 二、有base标签:--%>
<head>
    <base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
</head>
<body>
    <a href="test/ssm.html">测试页面</a>
</body>
**Base标签的要求:**

    base 标签必须写在 head 标签内部<br />base 标签必须在所有“带具体路径”的标签的前面<br />        serverName 部分 EL 表达式和 serverPort 部分 EL 表达式之间必须写“:”<br />serverPort 部分 EL 表达式和 contextPath 部分 EL 表达式之间绝对不能写“/”<br />        原因:contextPath 部分 EL 表达式本身就是“/”开头,如果多写一个“/”会干扰 Cookie 的工作机制<br />serverPort 部分 EL 表达式后面必须写“/”

八、SpringMVC环境下的Ajax请求

检查依赖的包:
<!-- Spring 进行 JSON 数据转换依赖 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
加入jQuery:

1环境搭建 - 图5

测试Ajax(基于jQuery):

    jQuery代码:
<script src="jquery/jquery-3.4.1.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {

        //btn1
        //此方式可以在浏览器看到发送的请求体是Form Data(表单数据)
        $("#btn1").click(function () {
            $.ajax({
                url: "send/array/one.html",         //请求目标资源地址
                type: "post",                       //请求方式
                data: arrayStr,                     //发送的请求参数
                dataType: "text",                   //表示如何对待服务器返回的数据
                success: function (response) {
                    alert(response);
                },
                error: function (response) {
                    alert(response);
                }

            });
        });

        //btn2
        //此方式可以在浏览器看到发送的请求体是Request Payload(请求负载)
        $("#btn2").click(function () {
            //准备要发送的数据
            var array=[5,8,12];
            //必须先将目标转换成JSON字符串
            var arrayStr = JSON.stringify(array);
            $.ajax({
                url: "send/array/two.html",         
                type: "post",                       
                data: arrayStr,                     
                dataType: "text",                   
                contentType: "application/json;charset=UTF-8",  //告诉服务器端当前请求的请求体是JSON格式
                success: function (response) {
                    alert(response);
                },
                error: function (response) {
                    alert(response);
                }

            });
        });

        //btn3
        //传输复杂对象
        $("#btn3").click(function () {
            var student = {
                "name":"Fall",
                "id":21,
                "address":{
                    "province":"浙江",
                    "city":"宁波"
                },
                "subjects":[
                    {
                        "subjectName":"Java",
                        "score":96
                    },
                    {
                        "subjectName":"Data Struct",
                        "score":93
                    }
                ],
                "map":{
                    "key1":"value1",
                    "key2":"value2"
                }
            };   //student end
            var studentStr = JSON.stringify(student);
            $.ajax({
                url: "send/compose/object.html",         
                type: "post",                       
                data: studentStr,                     
                dataType: "text",                   
                contentType: "application/json;charset=UTF-8",  
                success: function (response) {
                    alert(response);                //在浏览器控制台打印返回的信息
                },
                error: function (response) {
                    alert(response);
                }
            });

        });     //btn3

        //btn4
        //使用ResultEntity,统一返回的格式
        $("#btn4").click(function () {
            var student = {
                //...与前面相同
            };   
            var studentStr = JSON.stringify(student);
            $.ajax({
                url: "send/compose/object.json",    //此时是json,表示返回的数据是json格式的
                type: "post",                       
                data: studentStr,                    
                dataType: "json",                   //此时服务端返回的数据是json格式
                contentType: "application/json;charset=UTF-8",  
                success: function (response) {
                    console.log(response);            //在浏览器控制台打印返回的信息
                },
                error: function (response) {
                    console.log(response);
                }
            });

        });
        //btn4
    });
</script>
    对应的四个按钮:
<button id="btn1">Test Ajax One</button>
<br/><br/>
<button id="btn2">Test Ajax Two</button>
<br/><br/>
<button id="btn3">Test Compose Object</button>
<br/><br/>
<button id="btn4">Test ResultEntity</button>
    对应的控制层的四个方法:
    //通过@RequestParam接收数组
    @ResponseBody
    @RequestMapping("/send/array/one.html")
    public String testAjax01(@RequestParam("array") Integer[] array){
        for(Integer num : array){
            System.out.println("num:"+num);
        }
        return "success";
    }

    //通过@RequestBody接收数组
    @ResponseBody
    @RequestMapping("/send/array/two.html")
    public String testAjax02(@RequestBody Integer[] array){
        for(Integer num : array){
            System.out.println("num:"+num);
        }
        return "success";
    }

    //通过@RequestBody接收复杂对象
    @ResponseBody
    @RequestMapping("/send/compose/object.html")
    public String testSendComposeObject(@RequestBody Student student){
        System.out.println(student);
        return "success";
    }

    //通过一个工具类(ResultEntity<T>)统一返回数据的格式
    @ResponseBody
    @RequestMapping("/send/compose/object.json")
    public ResultEntity<Student> testResultEntity(@RequestBody Student student){
        return ResultEntity.successWithData(student);
    }

如果希望通过@RequestBody接收前端发来的JSON数据,则前端发来的数据需要进行以下几步:

  1. 准备好要发送的数据(需要是JSON对象或JSON数组)
  2. 通过JSON.stringify()方法,转换成JSON字符串
  3. 将JSON字符串直接赋值给data属性
  4. 必须设置contentType: “application/json;charset=UTF-8”

此时后端在加入了json依赖,并开启mvc注解驱动,就可以使用RequestBody接收JSON数据。

统一后端返回数据格式

通过工具类,实现统一后端返回的数据格式

工具类代码:
public class ResultEntity<T> {
    //设置两个常量
    public static final String SUCCESS = "SUCCESS";
    public static final String FAILED = "FAILED";

    //请求错误时,返回的错误信息,对应SUCCESS与FAILED
    private String message;

    //要返回的数据
    private T data;

    //封装当前请求的处理结果是成功还是失败
    private String result;

    //请求处理成功并且不向前端返回数据时,使用的静态方法
    //第一个<Type>表示声明一个泛型Type,第二个和return中的<Type>表示使用该泛型
    public static <Type> ResultEntity<Type> successWithoutData(){
        return new ResultEntity<Type>(null,null,SUCCESS);
    }

    //请求处理成功并且向前端返回数据时,使用的静态方法
    public static <Type> ResultEntity<Type> successWithData(Type data){
        return new ResultEntity<Type>(null,data,SUCCESS);
    }

    //请求处理失败,需要返回错误信息时,使用的静态方法
    public static <Type> ResultEntity<Type> failed(String message){
        return new ResultEntity<>(message,null,FAILED);
    }

    /** Getter,Setter,constructor and toString **/
}
工具类中静态方法的泛型Type,代表了在前端请求后,后端返回的数据的类型;只是因为静态方法中不能调用非静态的属性,因此需要重新声明一个泛型,名    字为Type(其他也可以)。

    即想要返回一个Student对象时,就通过方法
@RequestMapping(...)
@ResponseBody
public ResultEntity<Student> getStudent(){
    //...例如进行数据库查询操作等
    return ResultEntity.successWithData(student);
}

九、异常处理

目标:统一管理项目中的异常。

  • 抛出异常
  • 返回给前端异常信息
    • 普通请求:在返回的页面上显示异常信息
    • Ajax请求:返回JSON格式的数据

观察浏览器的开发者工具中,发现可以根据客户端发送的请求类型,区别是普通请求还是Ajax请求。

1环境搭建 - 图6

根据这一特点,编写一个工具类,其中一个静态方法用来判断请求是否是JSON请求

public class CrowdUtil {

    /**
     * @param request
     * @return true==json请求 ; false==普通页面请求
     */
    public static boolean judgeRequestType(HttpServletRequest request){
        String accept = request.getHeader("Accept");
        String header = request.getHeader("X-Requested-With");
        return (accept != null && accept.contains("application/json"))
                ||
                (header != null && header.equals("XMLHttpRequest"));
    }
}

异常映射实现的方式

一、通过XML配置实现

在mvc的配置文件中将SimpleMappingExceptionResolver加入容器,在exceptionMappings中配置异常与错误页面的映射

但是此时并未实现区别JSON请求与普通请求的要求。

<!--基于XML的异常映射-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" id="simpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.Exception">system-error</prop>
        </props>
    </property>
</bean>

二、通过注解实现

这里通过使用前面编写的工具类的判断方法,实现对不同请求类型进行不同处理。

//注解标明该类是基于注解的异常处理器类
@ControllerAdvice
public class CrowdExceptionResolver {

    //处理空指针异常
    @ExceptionHandler(value = {NullPointerException.class})
    public ModelAndView resolveNullPointerException(NullPointerException exception,
            HttpServletRequest request, HttpServletResponse response
    ) throws IOException {
        return commonCode(exception,request,response,"system-error");
    }

    //处理数学异常,这里如果内部操作相同,跳转页面也相同,其实可以放在上面一个方法中,此处只是为了演示
    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView resolveArithmeticException(ArithmeticException exception,
            HttpServletRequest request,HttpServletResponse response) throws IOException {
        return commonCode(exception,request,response,"system-error");

    }

    //整理出的不同异常的可重用代码
    private ModelAndView commonCode(
            //触发的异常,此处借助多态
            Exception exception,
            //客户器端的请求
            HttpServletRequest request,
            //服务端的响应
            HttpServletResponse response,
            //指定普通页面请求时,去的错误页面
            String viewName
    ) throws IOException {
        boolean judgeRequestType = CrowdUtil.judgeRequestType(request);
        if (judgeRequestType){
            //if判断-是json请求
            ResultEntity<Object> failed = ResultEntity.failed(exception.getMessage());
            //创建Gson对象
            Gson gson = new Gson();
            //将ResultEntity对象转换成json格式
            String json = gson.toJson(failed);
            //通过原生servlet的response传回异常内容
            response.getWriter().write(json);
            //此时只需要返回null(因为是通过json格式返回数据)
            return null;
        } else {
            //if判断-是普通页面请求
            //创建ModelAndView对象
            ModelAndView modelAndView = new ModelAndView();
            //设置触发异常跳转的页面(会自动被视图解析器加上前后缀)
            modelAndView.setViewName(viewName);
            //将异常信息加入
            modelAndView.addObject(CrowdConstant.ATTR_NAME_EXCEPTION, exception);
            //返回设置完成的ModelAndView
            return modelAndView;
        }
    }

}

十、以常量管理常用的属性

对于经常使用到且存在复用的属性名,可以单独设置一个常量类。这样的好处是可以减少因拼写导致的错误。

//常量类
public class CrowdConstant {

    public static final String ATTR_NAME_EXCEPTION = "exception";
    public static final String MESSAGE_LOGIN_FAILED = "登录失败!请确认账号密码是否正确";

}

十一、前端页面引入

引入登录页面

1、引入需要的静态资源

1环境搭建 - 图7

2、将登录页面的代码复制入创建的admin-login.jsp文件,并进行适当的修改(还未完全修改)

主要是修改了编码方式为统一的UTF-8;添加了base标签;提前设置form表单的action和method;设置了账号密码的name属性。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="keys" content="">
    <meta name="author" content="">
    <base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
    <link rel="stylesheet" href="bootstrap/css/bootstrap.css">
    <link rel="stylesheet" href="css/font-awesome.min.css">
    <link rel="stylesheet" href="css/login.css">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <div><a class="navbar-brand" href="index.html" style="font-size:32px;">尚筹网-创意产品众筹平台</a></div>
        </div>
    </div>
</nav>

<div class="container">

    <form action="admin/login/login.html" method="post" class="form-signin" role="form">
        <h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户登录</h2>
        <div class="form-group has-success has-feedback">
            <input type="text" name="login-user" class="form-control" id="inputSuccess4" placeholder="请输入登录账号" autofocus>
            <span class="glyphicon glyphicon-user form-control-feedback"></span>
        </div>
        <div class="form-group has-success has-feedback">
            <input type="text" name="login-pwd" class="form-control" id="inputSuccess4" placeholder="请输入登录密码" style="margin-top:10px;">
            <span class="glyphicon glyphicon-lock form-control-feedback"></span>
        </div>
        <div class="checkbox" style="text-align:right;"><a href="reg.html">我要注册</a></div>
        <button type="submit" class="btn btn-lg btn-success btn-block">登录</button>
    </form>
</div>
<script src="jquery/jquery-2.1.1.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
</body>
</html>

遇到的奇怪的问题

在mvc配置文件中通过view-controller,使通过path指定的路径,访问登录页面(因为只需要单纯的访问而不需要附带数据等操作,因此使用此方法相比handler方法转发更加方便)

<!--通过view-controller 来设置一些直接的页面跳转-->
<mvc:view-controller path="/admin/login/page.html" view-name="admin-login"/>
但是最后测试下来,发现访问时会**发生404错误**:

我使用的默认是Edge Beta浏览器,当使用Edge浏览器时,可以通过链接访问登录页面,也可以直接使用`localhost:8888/admin/login/page.html`访问登录页面;但是当使用了chrome浏览器,使用地址栏输入...page.html时,发生奇怪的事情:报404错误,且之后edge也不能访问登录页面。最后尝试了很久,觉得应该是Tomcat缓存的问题,但尝试了多种清理缓存的方法,还是没有效果,最后**去掉了IDEA中对tomcat的一个勾选**:

1环境搭建 - 图8

这样可以算是“暂时”解决了问题。


尝试使用layer弹层组件

1、网上下载layer.js(此处是3.3.1版本)

2、引入与测试使用layer组件

注意:layer组件依赖于jquery,因此layer的引入必须在jquery之后,否则会出错。
<script src="jquery/jquery-3.4.1.js" type="text/javascript"></script>
<script src="layer/layer.js" type="text/javascript"></script>
<script>
    $("#btn5").click(function () {
        layer.msg("test layer");
    });
</script>
<body>
     <button id="btn5">Test layer</button>
</body>

修饰错误页面

借助登录页面的样式,给错误页面换壳

使错误页面可以显示错误信息,并且提供了返回上一层的按钮。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <%-- 前面与登录页面相同 --%>
    <script src="jquery/jquery-3.4.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("button").click(function () {
                //用js的方法,完成回退的目的
                window.history.back();
            });
        });
    </script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <%-- 与登录页面同 --%>
</nav>

<div class="container">
    <h1 style="text-align: center;">请求出现了错误QAQ</h1>
    <h2 style="text-align: center;">错误消息:${requestScope.exception.message}</h2>
    <br/><br/>
    <button style="width:150px;margin: 0 auto;" class="btn btn-lg btn-success btn-block">返回上一步</button>
</div>

    <script src="bootstrap/js/bootstrap.min.js"></script>
</body>
</html>

最后此阶段完成时的项目目录结构图如下:

1环境搭建 - 图9