众筹项目之环境配置(二)

7. 日志系统

7.1 重要意义

系统在运行过程中出了问题就需要通过日志来进行排查,所以我们在上手任何新技术的时候,都要习惯性的关注一下它是如何打印日志的。
在这里插入图片描述

7.2 技术选型

7.2.1 总体介绍

在这里插入图片描述

7.2.2 不同日志系统的整合

在这里插入图片描述
在这里插入图片描述

7.3 具体操作

7.3.1 初始状态

Spring 使用 commons-logging 日志包。打印的日志是下面这样的。不用细看,截图放在这是为了和后面日志打印的情况对比
在这里插入图片描述

7.3.2 加入 slf4j+logback

在这里插入图片描述

<!-- 日志 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>

在这里插入图片描述

7.3.3 我们主动打印的日志

更换框架的日志系统之前
在这里插入图片描述
7.3.4 更换框架的日志系统之后

在这里插入图片描述
在这里插入图片描述
※使用日志打印信息和使用 sysout 打印信息的区别:sysout 如果不删除,那么执行到这里必然会打印;如果使用日志方式打印,可以通过日志级别控制信息是否打印。

7.3.4 更换框架的日志系统

-第一步:排除 commons-logging
在这里插入图片描述

1.选中该模块
2.点击show dependenties
3.切换试图
4.选中要排除的依赖,右击
5.选择Execlude,然后选择需要在哪个模块添加排除依赖
6.完成

也可以在这找对应的模块进行操作
在这里插入图片描述
效果图:
在这里插入图片描述
第二步:加入转换包
在这里插入图片描述

<!-- 其他日志框架的中间转换包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>

7.3.5 logback 配置文件

logback 工作时的具体细节可以通过 logback.xml 来配置
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
<!--指定日志输出的位置-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <!--日志输出的格式-->
        <!--按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行-->
        <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
    </encoder>
</appender>
    <!--设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR-->
    <!--指定任何一个日志级别都只打印当前级别和后面级别的日志。-->
    <root level="DEBUG">
        <!--指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender-->
        <appender-ref ref="STDOUT"/>
    </root>
        <!--根据特殊需求指定局部日志级别-->
        <logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>

8. 声明式事务

8.1 目标

从事务角度:一个事务方法中包含的多个数据库操作,要么一起提交、要么一起回滚。也就是说事务方法中的多个数据库操作,有任何一个失败,整个事务全部回滚。从声明式角度:由 Spring 来全面接管数据库事务。用声明式代替编程式。
在这里插入图片描述

try {
// 核心操作前:开启事务(关闭自动提交)
// 对应 AOP 的前置通知
connection.setAutoCommit(false);
// 核心操作
adminService.updateXxx(xxx, xxx);
// 核心操作成功:提交事务
// 对应 AOP 的返回通知
connection.commit();
}catch(Exception e){
// 核心操作失败:回滚事务
// 对应 AOP 的异常通知
connection.rollBack();
}finally{
// 不论成功还是失败,核心操作终归是结束了
// 核心操作不管是怎么结束的,都需要释放数据库连接
// 对应 AOP 的后置通知
if(connection != null){
connection.close();
}
}

8.2 思路

在这里插入图片描述
在这里插入图片描述

8.3 操作

8.3.1 加入 AOP 依赖包

</dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <!-- AOP 所需依赖 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <!-- AOP 所需依赖 -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
        </dependency>

8.3.2 第一步:创建 Spring 配置文件

8.3.3 第二步:配置事务管理器

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

8.3.4 第三步:配置 AOP

<!-- 配置事务切面-->
    <aop:config>
        <!-- 考虑到后面我们整合SpringSecurity, 避免把UserDetailsService加入事务控制,让切入点表达式定位到serviceImpl-->
        <aop:pointcut expression="execution(* *..*ServiceImpl.*(..))" id="txPointcut"/>

        <!-- 将切入点表达式和事务通知关联起来-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

8.3.5 第四步:配置事务属性

 <!-- 配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="txManager">

        <!-- 配置事务属性-->
        <tx:attributes>

            <!-- 查询方法:配置只读属性,让数据库知道这是一个查询操作,能够进行一定优化-->
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="count*" read-only="true"/>

            <!-- 增删改方法:配置事务传播行为、回滚异常-->
            <!--
                 propagation属性:
                     REQUIRED: 默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,如果已经有了, 那么就使用这个已有的事务。
                        顾虑:用别人的事务有可能“被”回滚
                     REQUIRES_NEW:建议使用的值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务。就算是已经有了,也在自己开启的事务中运行。
                                   (简洁:不管线程上有没有事务,都要自己开事务,在自己的事务中运行。)
                            好处:不会受到其他事务回滚的影响
            -->
            <!--
                  rollback-for属性: 配置事务方法针对什么样的异常回滚
                        默认:运行时异常回滚
                        建议:编译时异常和运行时异常都回滚
            -->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="remove*" propagation="REQUIRED"/>
            <tx:method name="batch*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

细节:
在这里插入图片描述
在这里插入图片描述

8.4 spring-persist-tx.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>
    <!-- 配置自动扫描的包:主要是为了把Service扫描到IOC容器中-->
    <context:component-scan base-package="com.atguigu.crowd.service"/>
    <!-- 配置事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 装配数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 配置事务切面-->
    <aop:config>
        <!-- 考虑到后面我们整合SpringSecurity, 避免把UserDetailsService加入事务控制,让切入点表达式定位到serviceImpl-->
        <aop:pointcut expression="execution(* *..*ServiceImpl.*(..))" id="txPointcut"/>

        <!-- 将切入点表达式和事务通知关联起来-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

    <!-- 配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="txManager">

        <!-- 配置事务属性-->
        <tx:attributes>

            <!-- 查询方法:配置只读属性,让数据库知道这是一个查询操作,能够进行一定优化-->
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="count*" read-only="true"/>

            <!-- 增删改方法:配置事务传播行为、回滚异常-->
            <!--
                 propagation属性:
                     REQUIRED: 默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,如果已经有了, 那么就使用这个已有的事务。
                        顾虑:用别人的事务有可能“被”回滚
                     REQUIRES_NEW:建议使用的值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务。就算是已经有了,也在自己开启的事务中运行。
                                   (简洁:不管线程上有没有事务,都要自己开事务,在自己的事务中运行。)
                            好处:不会受到其他事务回滚的影响
            -->
            <!--
                  rollback-for属性: 配置事务方法针对什么样的异常回滚
                        默认:运行时异常回滚
                        建议:编译时异常和运行时异常都回滚
            -->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="remove*" propagation="REQUIRED"/>
            <tx:method name="batch*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
</beans>

8.5 添加对应的类 以及各类都加上空(有)参方法、get方法、set方法、toString方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.6 AdminServiceImpl

在这里插入图片描述

@Service
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    private Logger logger = LoggerFactory.getLogger(AdminServiceImpl.class);

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void saveAdmin(Admin admin) {

        // 1.密码加密
        String userPswd = admin.getUserPswd();
//        userPswd = CrowdUtil.md5(userPswd);
        userPswd = passwordEncoder.encode(userPswd);
        admin.setUserPswd(userPswd);

        // 2.生成创建时间
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String createTime = format.format(date);
        admin.setCreateTime(createTime);

        // 3.执行保存
        try {
            adminMapper.insert(admin);
        } catch (Exception e) {
            e.printStackTrace();

            logger.info("异常全类名" + e.getClass().getName());

            if (e instanceof DuplicateKeyException){
                throw new  LoginAcctAlreadyInUseException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);
            }
        }
    }

8.7 LoginAcctAlreadyInUseException.java

在这里插入图片描述

/**
 * 保存或更新Admin时如果检测到登录账号重复抛出这个异常
 */
public class LoginAcctAlreadyInUseException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public LoginAcctAlreadyInUseException() {
    }

    public LoginAcctAlreadyInUseException(String message) {
        super(message);
    }
    }

测试

在这里插入图片描述在这里插入图片描述
效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意

在这里插入图片描述
在这里插入图片描述
事务没有了
在这里插入图片描述
在这里插入图片描述

9. 表述层工作机制

9.1 启动过程

在这里插入图片描述

9.2 访问过程

在这里插入图片描述

9.3 装配文件关系

在这里插入图片描述
在这里插入图片描述

10. 表述层环境搭建

10.1加入依赖

使用 SpringMVC 环境引入 spring-webmvc 依赖即可,同时可以把 spring-context 依赖去掉,因为根据依赖的传递性,spring-webmvc 会依赖 spring-context。
在这里插入图片描述

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

10.2web.xml 配置

10.2.1ContextLoaderListene


             <!-- needed for ContextLoaderListener-->
          <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-persist-*.xml</param-value>
          </context-param>

          <!-- Bootstraps the root web application context before servlet initialization-->
          <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>

10.2.2CharacterEncodingFilte

<!-- 配置 CharacterEncodingFilter 解决 POST 请求的字符乱码问题 -->
          <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执行的顺序要在所有其他Filter前面-->
          <!-- 原因如下:-->
          <!-- request.setCharacterEncoding(encoding)必须在request.getParameter()前面-->
          <!-- response.setCharacterEncoding(encoding)必须在response.getWriter前面-->
          <filter-mapping>
            <filter-name>CharacterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
          </filter-mapping>

10.2.3HiddenHttpMethodFilter

遵循 RESTFUL 风格将 POST 请求转换为 PUT 请求、DELETE 请求时使用。
省略不配。

10.2.4DispatcherServlet

基本配置

 <!-- 配置 SpringMVC 的前端控制器 -->
          <!-- The front controller of this Spring Web application responsible for handing all application requests-->
          <servlet>
            <servlet-name>springDispatcherServlet</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>
            <!-- Servlet默认生命周期中, 创建对象是在第一次接受到请求时-->
            <!--DispatcherServlet创建对象后有大量的"框架初始化"工作,不适合在第一次请求时来做-->
            <!-- 设置oad-on-startup 就是为了让DispatcherServletWeb应用启动时创建对象 、初始化-->
            <load-on-startup>1</load-on-startup>
          </servlet>

         

10.3请求扩展名

10.3.1*.html 扩展名

举例
http://localhost:8080/atcrowdfunding02-admin-webui/save/emp.html
作用:伪静态
表面上看起来是一个访问静态资源的请求,但是实际上是由 SpringMVC
交给 handler 来处理的动态资源。
好处 1:有利于 SEO 优化让搜索引擎更容易找到我们的网站,有利于网站的推广
好处 2:隐藏后端技术实现细节给黑客入侵系统增加难度
好处 3:自动解决静态资源访问问题
访问 a.png 本身不符合*.html 这个 url-pattern,和 SpringMVC 完全没有关系,当前请求由 Tomcat 处理。如 果 url-pattern 映 射 了 “ / ”, 那 么 SpringMVC 中 还 需 要 配 置DefaultServletHandler。
缺陷:不符合 RESTFUL 风格

10.3.2*.json 扩展名

提出问题
在这里插入图片描述
描述问题
请求扩展名 http://localhost:8080/extra01-ajax/get/emp/by/ajax.html
服务器端打算返回的数据:JSON 格式二者不匹配!!!
分析问题
请求扩展名和响应体的数据格式不匹配!!!
解决问题
让请求扩展名和预计的响应体数据格式一致。
http://localhost:8080/extra01-ajax/get/emp/by/ajax.json
同时让 SpringMVC 在映射*.html 扩展名之外再映射*.json 扩展名,不然会返回 404

 <!-- Map all requests to the DispatcherServlet for handling-->
            <servlet-mapping>
                <servlet-name>springDispatcherServlet</servlet-name>
                <!-- DispatcherServlet 映射的 URL 地址 -->
                <!-- 大白话:什么样的访问地址会交给 SpringMVC 来处理 -->
                <!-- url-pattern配置方式一:/表示拦截所有请求 -->
                <!-- <url-pattern>/</url-pattern> -->
                <!-- url-pattern配置方式二:配置请求扩展名 -->
                <!-- 优点1: xxx.css、 xxx.js、 xxx.png等等静态资源完全不经过SpringMVC,不需要特殊处理-->
                <!-- 优点2: 可以实现伪静态效果。表面上看起来是访问一个HTML文件这样的静态资源,但是实际上是经过Java代码运算-->
                <!-- 伪静态作用1:给黑客入侵增加难度-->
                <!-- 伪静态作用2:有利于SEO优化(让百度、谷歌这样的搜索引擎更容易找到我们项目)-->
                <!--  缺点: 不符合RESTFul风格-->
                <url-pattern>*.html</url-pattern>
                <url-pattern>*.json</url-pattern>
                <!--  为什么要另外再配置json扩展名呢?-->
                <!--  如果一个Ajax请求扩展名是html,但是实际服务器给浏览器返回的是json数据,二者就不匹配了,会出现406错误-->
                <!--  为了让Ajax请求能够顺利拿到JSON格式的响应数据,我们另外配置json扩展名-->
                <!--<url-pattern>*.json</url-pattern>-->

            </servlet-mapping>

10.4spring-web-mvc.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:mvc="http://www.springframework.org/schema/mvc"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               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-2.5.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        ">
                <!--    配置自动扫描的包:扫描handler-->
                <context:component-scan base-package="com.atguigu.crowd.mvc"/>
                <!-- 配置视图解析器 -->
                <!-- 拼接公式→前缀+逻辑视图+后缀=物理视图 -->
                <!--
                @RequestMapping("/xxx/xxx")
                public String xxx() {
                // 这个返回值就是逻辑视图
                return "target";
                }
                物理视图是一个可以直接转发过去的地址
                物理视图:"/WEB-INF/"+"target"+".jsp"
                转发路径:"/WEB-INF/target.jsp"
                -->
                <!--    配置SpringMVC的注解驱动-->
                <mvc:annotation-driven/>

                <!--    配置视图解析器-->
                <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <!-- 前缀:附加到逻辑视图名称前 -->
                    <property name="prefix" value="/WEB-INF/"/>
                    <!-- 后缀:附加到逻辑视图名称后 -->
                    <property name="suffix" value=".jsp"/>
                </bean>
                </beans>

10.5 页面上的 base 标签

在这里插入图片描述


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

                <!-- JSP页面使用的依赖 -->
                <dependency>
                    <groupId>javax.servlet.jsp</groupId>
                    <artifactId>jsp-api</artifactId>
                    <scope>provided</scope>
                </dependency>

10.5.2 作用

将页面上路径中的${pageContext.request.contextPath}部分提取到页面开头

10.5.3 写法

    <base href="http://${pageContext.request.serverName }:${pageContext.request.serverPort }${pageContext.request.contextPath }/"/>

10.5.4 需要注意的点

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

10.6 初步测试

10.6.1 编写:handler.java

在这里插入图片描述

@Controller
public class TestHandler {

    @Autowired
    private AdminService adminService;
@RequestMapping("/test/ssm.html")
    public String testSsm(ModelMap modelMap){

        List<Admin> adminList = adminService.getAll();
        modelMap.addAttribute("adminList", adminList);


        return "target";
    }
}

10.6.2 编写: AdminService.java

在这里插入图片描述


@Service
public class AdminServiceImpl implements AdminService {

    @Autowired
    private AdminMapper adminMapper;

    @Override
    public List<Admin> getAll() {
        return adminMapper.selectByExample(new AdminExample());
    }

10.6.3创建目标 JSP 页面(视图)

在这里插入图片描述

<%@ page language="java"  contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"  %>
<%-- isELIgnored="false"--%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>

</head>
<body>

<h1>Success</h1>

        ${requestScope.adminList }

</body>
</html>

10.6.4友情提示

跟着视频学习项目开发,表面上的现象错综复杂,一会儿在页面,一会儿写jQuery,一会儿写 handler,一会儿写 Service,一会儿写 SQL,一会儿写配置文件。背后有思路作为一根红线把所有的现象都穿起来,跟着思路走,就不会迷路。思路的背后是目标。思维一定要有层次!!!

效果图:

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值