01-环境搭建 尚筹网

环境搭建

一、项目结构搭建

​ 模块关系

​ parent模块仅仅用来确定各个Maven依赖的版本

​ webui、component、entity模块继承自parent模块

​ util、reverse模块属于独立工程,不参与继承与聚合

​ 且webui依赖于component,component依赖于entity、util。
在这里插入图片描述
各个工程的打包方式:

<!--parent-->
    <groupId>org.example</groupId>
    <artifactId>crowdfunding01-admin-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!--webui-->
	<packaging>war</packaging>
<!--其他的模块都是默认打包方式(jar)-->

​ IDEA中创建多模块工程:

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

​ 由于webui模块,需要打成war包,因此需要有web工程的目录结构,可以通过在Modules中添 加,产生对应的结构:
在这里插入图片描述
​ 模块创建时应该选择好父模块

二、数据库构建与逆向

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中导入依赖
<!-- 控制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>8.0.15</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中进行逆向工程的方法:
在这里插入图片描述
运行完后,应当对产生的所有文件各归各位(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来打印,会给项目上线带来很多问题
​ 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 标签内部
base 标签必须在所有“带具体路径”的标签的前面
​ serverName 部分 EL 表达式和 serverPort 部分 EL 表达式之间必须写“:”
serverPort 部分 EL 表达式和 contextPath 部分 EL 表达式之间绝对不能写“/”
​ 原因:contextPath 部分 EL 表达式本身就是“/”开头,如果多写一个“/”会干扰 Cookie 的工作机制
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:
在这里插入图片描述
​ 测试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请求。

在这里插入图片描述
根据这一特点,编写一个工具类,其中一个静态方法用来判断请求是否是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、引入需要的静态资源
在这里插入图片描述
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的一个勾选

在这里插入图片描述
这样可以算是“暂时”解决了问题。


尝试使用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>

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值