spring-web搭建

目录

1:spring-web是什么原理以及组成

2:spring-web的搭建(以普通tomcat服务器做基础)

1->环境(自己去建一个普通web项目)

2->WEB-INF的web.xml配置(程序入口)

 3->commons-loggings配置(这个log4j.properties必须放在classes下,也就是src那一层)

4->写个controller(C)

5->写个Model(M),上文的sysAdminService

a:service

b:dao

c:entity(这里不需要写建表语句,通过spring hibernate的hibernate.hbm2ddl.auto自动更新维护表)

6->写个拦截器 (这个拦截器会优先拦截所有请求,只有return true才会允许给相应controller处理)

7->写个异常处理器

3:敲黑板 

4:下一章docker

5:本章资源下载链接


先说说这篇文章看完你能学习到什么吧

1:spring-web是什么原理以及组成,2:spring-web的搭建(以普通tomcat服务器做基础),3:敲黑板
1:spring-web是什么原理以及组成

spring-web是spring中提供给用户搭建web服务的框架,其中的spring-webmvc是其核心部分,webmvc采用MVC架构,M是model层,V是view层,C是controller层。那我们从这三个层次去讲解这部分。首先我们用一整套访问请求来讲解原理:

1->用户请求到服务器。

用户作为使用者,是用游览器(客户端)输入网址通过域名解析访问到我们的域名主机,然后域名主机会找到相应对的发布的服务(java服务,php服务)等,我们是java服务器,部署在tomcat上,那么tomcat就会根据程序上下文(application context)或者说它里面的Server.xml找到Host节点里面的Context节点找到项目的发布目录。

2->spring web的映射请求地址到相应的controller(C)。

tomcat调用java服务的Servlet,java第一道防线就是DispatcherServlet(中央调度器)。DispatcherServlet请求handlerMapping,返回相应的执行器handler,DispatcherServlet通过HandlerAdapter调用相应的handler执行器。执行器handler根据url找到相应的处理器调用controller

3->controller(c)进行数据处理,返回数据modelAndView(M)给DispatcherServlet。

controller接下来会进行数据处理,比如调用service去保存或者查询数据,service去通过dao进行持久化数据,然后返回数据给controller,controller把数据和需要返回的url给DispatcherServlet(也就是返回modelAndView对象)

4->ViewResolve进行视图解析返回view(V)

DispatcherServlet拿到modelAndView去找ViewResolver解析视图,返回view给DispatcherServlet。

5->DispatcherServlet响应数据,视图渲染

DispatcherServlet拿到view数据,返回给tomcat,tomcat相应游览器请求,并让其渲染视图。

这就是一整套请求和渲染流程,采用mvc架构

2:spring-web的搭建(以普通tomcat服务器做基础)
1->环境(自己去建一个普通web项目)
【jdk8】,【spring-web5.2.3】,【spring-webmvc5.2.3】,【intellj idea2018】
2->WEB-INF的web.xml配置(程序入口)

a:配置DispatcherServlet(中央调度器)



    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
               
            /WEB-INF/spring-mvc.xml
        
        1
    
    
        dispatcher
        /
    

b:spring-mvc.xml配置HandlerMapping和HandlerAdapter和ViewResolver和Controller以及bean的扫描路径,这里多配置了个访问拦截器。各位可以针对访问权限进行拦截



   
   
   
   
   
   
   

   

   
   
   
   
      
         
            
         
      
   

   
   
   
   
      
         
            
         
      
   

   
   

   
   
      
         104857600
      
   
   
   
   
   
      
      
         
         
      
   

c:配置程序上下文



    
        contextConfigLocation
        /WEB-INF/applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            /WEB-INF/spring-mvc.xml
        
        1
    
    
        dispatcher
        /
    

 d:applicationContext.xml配置数据库连接池和持久层框架以及事务



    
    

    
        
        
        
        
        
    

    
    
        
        
            
                true
                org.hibernate.dialect.MySQLDialect
                
                
                update
            
        
        
    

    
    
        
    

    
        
    

    

e:config.properties的数据

mysql_url=jdbc:mysql://127.0.0.1:3306/spring-web?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&useOldAliasMetadataBehavior=true
mysql_username=root
mysql_password=
 3->commons-loggings配置(这个log4j.properties必须放在classes下,也就是src那一层)
log4j.rootLogger=info,A

#系统输出
log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.Target=System.out
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t:%p] %c %m%n

4->写个controller(C)
package com.wanzi.controller.sys;

import com.wanzi.annotation.UnAuth;
import com.wanzi.entity.SysAdmin;
import com.wanzi.service.SysAdminService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@UnAuth
@Slf4j
@RequestMapping("/sys/test")
public class TestController {

    @Autowired
    private SysAdminService sysAdminService;

    @RequestMapping(value = "/test")
    @ResponseBody
    public Object test(){
        log.error("权限检验通过");
        return "test";
    }

    @RequestMapping(value = "/index")
    public String index(Model model){
        log.error("权限检验通过");
        SysAdmin sysAdmin = sysAdminService.getSysAdminById(1);
        model.addAttribute("test","我是测试传参");
        return "/sys/test/index";
    }
}
5->写个Model(M),上文的sysAdminService
a:service
package com.wanzi.service;

import com.wanzi.dao.SysAdminDao;
import com.wanzi.entity.SysAdmin;
import com.wanzi.template.SimpleHibernateTemplate;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 系统管理员service
 */
@Service
@Transactional
public class SysAdminService {

   @Autowired
   private SysAdminDao sysAdminDao;

   private SimpleHibernateTemplate sysAdminDaoH;

   @Autowired
   public void setSessionFactory (SessionFactory sessionFactory) {
      sysAdminDaoH = new SimpleHibernateTemplate<>(sessionFactory, SysAdmin.class);
   }

   /**
    * 根据id获取系统管理员信息
    * 
    * @param id
    *            void
    */
   public SysAdmin getSysAdminById (int id) {
      return sysAdminDao.getSysAdminById(id);
//    return this.sysAdminDaoH.get(id);
   }

}
b:dao

这里dao我用了两种方法,一种是用系统HibernateTemplate实现增删改查,一种是用Session实现增删改查。

b->1:HibernateTemplate方式

上面的applicationContext.xml中有配置名字为hibernateTemplate的bean,传统方式就是操作这个类实现增删改查,下面是一个例子Dao

package com.wanzi.dao;

import com.wanzi.entity.SysAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class SysAdminDao {
    @Autowired
    private HibernateTemplate hibernateTemplate;

    public SysAdmin getSysAdminById (int id) {
        return hibernateTemplate.get(SysAdmin.class,id);
    }

}

 b->2:灵活的Session

上文中service操作的SimpleHibernateTemplate是自定义的dao

package com.wanzi.template;

import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
R
 * Hibernate的范型基类.
 * 
 * 可以在service类中直接创建使用.也可以继承出DAO子类,在多个Service类中共享DAO操作.
 * 参考Spring2.5自带的Petlinc例子,取消了HibernateTemplate.
 * 通过Hibernate的sessionFactory.getCurrentSession()获得session,直接使用Hibernate原生API.
 *
 * @param  DAO操作的对象类型
 * @param  主键类型
 */
public class SimpleHibernateTemplate {

   protected SessionFactory sessionFactory;

   protected Class entityClass;

   public SimpleHibernateTemplate(SessionFactory sessionFactory, Class entityClass) {
      this.sessionFactory = sessionFactory;
      this.entityClass = entityClass;
   }
   
   /**
    * 采用@Autowired按类型注入SessionFactory,当有多个SesionFactory的时候Override本函数.
    * 这个注解会生效
    */
   @Autowired
   public void setSessionFactory(final SessionFactory sessionFactory) {
      this.sessionFactory = sessionFactory;
   }

   public Session getSession() {
      return sessionFactory.getCurrentSession();
   }

   /**
    * 按id获取对象.
    */
   public T get(final PK id) {
      return (T) getSession().get(entityClass, id);
   }
}
c:entity(这里不需要写建表语句,通过spring hibernate的hibernate.hbm2ddl.auto自动更新维护表)

package com.wanzi.entity;

import lombok.Data;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * table : sys_admin # 系统管理员数据表
 * 
 * @author zwm
 */
@Data
@Table(name = "sys_admin")
@Entity
public class SysAdmin {

   /**
    * 管理员状态:0、停用
    */
   public static final int STATUS_DISABLE = 0;
   
   /**
    * 管理员状态:1、启用
    */
   public static final int STATUS_ENABLE = 1;

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;

   @Column(name = "name")
   private String name;

   @Column(name = "password")
   private String password;

   @Column(name = "md5_password")
   private String md5Password;
   
   /**
    * 使用者
    */
   @Column(name = "real_name")
   private String realName;

   @Column(name = "status")
   private int status;

   @Column(name = "phone_num")
   private String phoneNum;

   @Column(name = "remark")
   private String remark;

   @Column(name = "last_login_time")
   private Date lastLoginTime;

   @Column(name = "last_login_ip")
   private String lastLoginIp;

}

d:扩展知识

d->1:和数据库mysql相连,java采用线程池,现如今java有很多好用线程池,本例子采用dbcp2

,在applicationContext.xml中有配置dataSource,其他的数据源还有:

Apache Commons DBCP:Apache Commons DBCP(数据库连接池)是Apache软件基金会的一个项目,提供了一个稳定和可靠的连接池。它是许多Java应用程序的首选选择。

HikariCP:HikariCP是一个轻量级、高性能的数据库连接池,被广泛认为是目前性能最好的连接池之一。它专注于快速的连接获取和释放,适用于高并发的应用程序。

C3P0:C3P0是一个开源的数据库连接池,具有许多配置选项,可用于调整连接池的行为。它是一种稳定的连接池,被许多Java应用程序使用。

Tomcat JDBC Pool:Tomcat JDBC Pool是Apache Tomcat项目的一部分,提供了一个性能良好的连接池。它设计用于与Tomcat集成,但也可以作为独立的连接池使用。

H2 Database:H2 Database是一个嵌入式数据库,它包含了一个内置的连接池,适用于小型应用程序和测试用例。

Druid:Druid是一个开源的数据库连接池,具有监控和统计功能,可以帮助开发人员分析数据库连接的使用情况和性能。Bitronix Transaction Manager:Bitronix是一个支持分布式事务的连接池和事务管理器,适用于需要强大事务支持的应用程序。

二、当讨论连接词(连接池)时,可以通过生活中的一个例子来形象地理解它们的区别:
例子:自来水供应

Apache Commons DBCP 可以类比为一个城市自来水公司,它负责向一座城市供水。这个水公司(连接池)管理着水的分配和流动,确保城市里的每户家庭都可以获得他们所需的水(数据库连接)。虽然它提供了稳定和可靠的供水服务,但它的运作可能不一定是最高效的,因为它可能需要一些时间来响应不同地区的需求。

HikariCP 就像是一家高效的自来水公司,它专注于提供最快速的自来水供应。它的供水管道(连接池)设计得非常流畅,可以迅速响应城市中不同区域的需求,确保每家每户都能够获得所需的水。这家自来水公司以其高性能和响应速度而著称。

C3P0 可以比作另一家自来水公司,它提供了许多不同的自来水供应计划,可以根据客户的需求进行定制。这家公司提供了灵活的供水方案,允许客户根据他们的特定需求来调整水的流量和质量。

Tomcat JDBC Pool 好像是一个城市自来水部门的子部门,专门为城市的一座公共建筑提供自来水服务。虽然它与整个城市的自来水系统相互关联,但它也可以独立运行,提供高效的供水服务。

H2 Database 就像是一个小村庄的自来水泵站,它供应水给村里的居民。虽然规模较小,但它是自足的,提供了足够的水供应来满足小村庄的需求。Druid 就像是一个有水质检测设备的自来水公司,它不仅提供水供应,还监控水的质量,并提供统计数据以帮助客户了解他们的水消耗情况。

d->2: 事务

@Transactional注解是Spring框架中用于声明式事务管理的关键注解,这个方法可以作用于类和方法,作用于类是整个类都开启该方法,作用于方法的话,该方法会被归纳到事务管理,事务管理有以下几个特性原子性,一致性,隔离性,持久性。原子性表示一个事务里所有操作要么全部成功,要么全部失败,因为最后都是事务管理统一提交的。一致性表示事务提交前后数据库状态一致。隔离性表示多个事务提交是并行,互不影响。持久性表示事务一旦提交修改数据会永久保存。那么对于事务用户有哪些要注意的呢,第一事务不能作用于非public的方法,第二事务注解@Transactional不能被作用于多个方法调用,也就是外部没有这个注解,调用有这个注解的方法,那么事务是无法开启,第三方法开启了事务,但是你自己捕捉了异常,那么事务就不会被异常回滚.
6->写个拦截器 (这个拦截器会优先拦截所有请求,只有return true才会允许给相应controller处理)
package com.wanzi.interceptor;

import java.lang.reflect.Method;

import com.wanzi.annotation.MenuAuth;
import com.wanzi.annotation.UnAuth;
import com.wanzi.bean.SysLoginAdmin;
import com.wanzi.entity.SysAdmin;
import com.wanzi.service.SysAdminService;
import com.wanzi.util.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * 系统端权限登录校验
 */
@Slf4j
public class SysAuthInterceptor extends HandlerInterceptorAdapter {

   @Autowired
   private SysAdminService adminService;

   /**
    * This implementation always returns {@code true}.
    */
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      MenuAuth menuAuth = null;
      log.error("后端权限开始判断");
      HandlerMethod handlerMethod = (HandlerMethod) handler;
      Class clasz = handlerMethod.getBean().getClass();
      //不需要检验授权的可以跳过
      if (clasz.isAnnotationPresent(UnAuth.class)) {
         return true;
      }
      //通过匹配找到url匹配的需要检验的方法注解MenuAuth
      Method[] methods = clasz.getMethods();
      for (Method method : methods) {
         if (method.isAnnotationPresent(RequestMapping.class)) {
            if (method.isAnnotationPresent(UnAuth.class)) {
               RequestMapping mapping = method.getAnnotation(RequestMapping.class);
               for (String str : mapping.path()) {
                  if (request.getRequestURI().indexOf(str) != -1) {
                     return true;
                  }
               }
            } else if (method.isAnnotationPresent(MenuAuth.class)) { // 菜单校验获取菜单
               RequestMapping mapping = method.getAnnotation(RequestMapping.class);
               for (String str : mapping.path()) {
                  if (request.getRequestURI().indexOf(str) != -1) {
                     menuAuth = method.getAnnotation(MenuAuth.class);

                     break;
                  }
               }
               if (menuAuth == null) {
                  for (String str : mapping.value()) {
                     if (request.getRequestURI().indexOf(str) != -1) {
                        menuAuth = method.getAnnotation(MenuAuth.class);
                        break;
                     }
                  }
               }
               if (menuAuth != null) {
                  break;
               }
            }
         }
      }
      log.error("进入权限判断");
      SysLoginAdmin loginAdmin = WebUtils.getSysLoginAdmin();
      if (loginAdmin != null) {
         SysAdmin sysAdmin = adminService.getSysAdminById(loginAdmin.getId());
         if (sysAdmin != null) {
            if (sysAdmin.getStatus() == SysAdmin.STATUS_DISABLE) {
               throw new Exception("账号已停用,请联系管理员");
            }
            if (menuAuth != null
                  && !loginAdmin.checkMenuAuth(request.getRequestURI(), menuAuth)) {
               throw new Exception("无访问权限");
            }
            return true;
         }
         throw new Exception("账号不存在,请重新登录");
      }
      throw new Exception("账号未登录,请重新登录");
   }
}
7->写个异常处理器
package com.wanzi.handler;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;


/**
 * 异常处理
 */
@Slf4j
public class ExceptionHandler implements HandlerExceptionResolver {

   @Override
   public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
         Exception ex) {
      try {
         Map model = new HashMap();
         model.put("exception", ex);
         model.put("uri", request.getRequestURI());
         // ajax处理数据
         if (isAjaxRequest(request)) {
            return null;
         }
         // 其他处理跳转统一异常页面
         if(request.getRequestURI().startsWith("/m")){
            return new ModelAndView("exception/error_mobile", model);
         }
         return new ModelAndView("exception/error", model);
      } catch (Exception e) {
         log.error(e.getMessage(),e);
      }
      return null;
   }

   /**
    * 是否ajax请求
    */
   public static boolean isAjaxRequest(HttpServletRequest request) {
      return request.getHeader("x-requested-with") != null
            && request.getHeader("x-requested-with").trim().toLowerCase().equals("xmlhttprequest");
   }

}
3:敲黑板 

1:上文entity的SysAdmin为什么没有get和set方法,用了什么技术吗?

答:没错,上文采用了lombok插件,这个插件会在编译代码时候根据@Data等注解自动生成相应的代码。

2:为什么上文的@Slf4j注解,然后就可以直接使用了log?

答:因为@Slf4j也是lombok的注解,也是在编译期间会自动生成Log log = LogFactory.getLog("");所以下文可以直接用log变量,但是你要使用这个log,还需要配置日志文件,本文用的普通的log4j,最新的有log4j2和logback。我的配置文件是log4j.properties,commons-logging要求我们把日志文件放在classes下,也就是src的根目录下。

3:上文controller的Model对象,调用model.addAttribute("test",sysAdmin.getName());是啥意思?

答:这个是controller层向DispatcherServlet传参的一种方式。DispatcherServlet会拿着参数去视图解析器生成view。

4:上文的@ResponseBody是干嘛用的?

答:@ResponseBody 表示该方法的返回结果直接写入 HTTP response body中,一般在异步获取数据时使用【也就是AJAX】。

5:restful风格开发是什么?

答:这是套web开发的风格方案,我们传统的请求请求传参无外乎是?id=1这种通过进行请求,不管delete还是post还是get。但是restful不一样,REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方 式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。如【删除id=1,是delete请求,user/1】【保存id=1,是post请求,user/1】【更新id=1,是put请求,user/1】【查询id=1,是get请求,user/1】,而是都是通用的user?id=1这样子

6:hibernate的hbm2ddl.auto这个参数是会根据entity自动生成和更新数据库建表语句吗?

答:对的,但是前面你要设置value是update或者create。

7:上面controller的index方法的return "/sys/test/index";是干嘛的?

答:这个是返回view的相对地址给DispacherServlet,上面用model传参了,这里返回view。你可以理解成两个相对应就是modelAndView。

8:关于tomcat的知识可以给我们讲讲吗?

答:我给你们讲讲我遇到的一些tomcat的常见问题吧

a->intelljidea启动tomcat,但是控制台的output输出乱码怎么解决?

这个只要在启动的tomcat参数加个VM options的参数-Dfile.encoding=UTF-8  

b->intelljidea启动tomcat服务器后,需要带项目名才能访问服务器,例如localhost/spring-web/test这样,那怎么直接把项目设置成根目录,而不是通过项目名访问?

这个的话,如果是在idea启动的项目,只要改下要启动的tomcat的deployment下面的application context为/就行。如果是在正式环境,需要去改动tomcat目录下的server.xml或者context.xml的Host下的context

4:下一章docker
5:本章资源下载链接

https://download.csdn.net/download/weixin_36667844/88797420

你可能感兴趣的:(2024-spring,spring,java,后端)