how2j.cn

-->
下载区
文件名 文件大小
shiro.rar 43k

解压rar如果失败,请用5.21版本或者更高版本的winrar

点击下载 winrar5.21
步骤 1 : springboot 概念   
步骤 2 : 数据   
步骤 3 : 先运行,看到效果,再学习   
步骤 4 : 模仿和排错   
步骤 5 : 运用前面的文件   
步骤 6 : 创建项目   
步骤 7 : 输入项目参数   
步骤 8 : pom.xml   
步骤 9 : application.properties   
步骤 10 : Application.java   
步骤 11 : src 下的 java 文件们   
步骤 12 : jsp们   
步骤 13 : ShiroConfiguration   
步骤 14 : SpringContextUtils.java   
步骤 15 : URLPathMatchingFilter.java   
步骤 16 : 重启后测试   

步骤 1 :

springboot 概念

edit
springboot 作为一个框架集成,把ssm的配置都简化了。
如果没有springboot基础,请事先学习 springboot 入门
通过前面的学习,数据估计又被修改了,执行下面这部分sql语句,还原数据,以便观察演示
DROP DATABASE IF EXISTS shiro; CREATE DATABASE shiro DEFAULT CHARACTER SET utf8; USE shiro; drop table if exists user; drop table if exists role; drop table if exists permission; drop table if exists user_role; drop table if exists role_permission; create table user ( id bigint auto_increment, name varchar(100), password varchar(100), salt varchar(100), constraint pk_users primary key(id) ) charset=utf8 ENGINE=InnoDB; create table role ( id bigint auto_increment, name varchar(100), desc_ varchar(100), constraint pk_roles primary key(id) ) charset=utf8 ENGINE=InnoDB; create table permission ( id bigint auto_increment, name varchar(100), desc_ varchar(100), url varchar(100), constraint pk_permissions primary key(id) ) charset=utf8 ENGINE=InnoDB; create table user_role ( id bigint auto_increment, uid bigint, rid bigint, constraint pk_users_roles primary key(id) ) charset=utf8 ENGINE=InnoDB; create table role_permission ( id bigint auto_increment, rid bigint, pid bigint, constraint pk_roles_permissions primary key(id) ) charset=utf8 ENGINE=InnoDB; INSERT INTO `permission` VALUES (1,'addProduct','增加产品','/addProduct'); INSERT INTO `permission` VALUES (2,'deleteProduct','删除产品','/deleteProduct'); INSERT INTO `permission` VALUES (3,'editeProduct','编辑产品','/editeProduct'); INSERT INTO `permission` VALUES (4,'updateProduct','修改产品','/updateProduct'); INSERT INTO `permission` VALUES (5,'listProduct','查看产品','/listProduct'); INSERT INTO `permission` VALUES (6,'addOrder','增加订单','/addOrder'); INSERT INTO `permission` VALUES (7,'deleteOrder','删除订单','/deleteOrder'); INSERT INTO `permission` VALUES (8,'editeOrder','编辑订单','/editeOrder'); INSERT INTO `permission` VALUES (9,'updateOrder','修改订单','/updateOrder'); INSERT INTO `permission` VALUES (10,'listOrder','查看订单','/listOrder'); INSERT INTO `role` VALUES (1,'admin','超级管理员'); INSERT INTO `role` VALUES (2,'productManager','产品管理员'); INSERT INTO `role` VALUES (3,'orderManager','订单管理员'); INSERT INTO `role_permission` VALUES (1,1,1); INSERT INTO `role_permission` VALUES (2,1,2); INSERT INTO `role_permission` VALUES (3,1,3); INSERT INTO `role_permission` VALUES (4,1,4); INSERT INTO `role_permission` VALUES (5,1,5); INSERT INTO `role_permission` VALUES (6,1,6); INSERT INTO `role_permission` VALUES (7,1,7); INSERT INTO `role_permission` VALUES (8,1,8); INSERT INTO `role_permission` VALUES (9,1,9); INSERT INTO `role_permission` VALUES (10,1,10); INSERT INTO `role_permission` VALUES (11,2,1); INSERT INTO `role_permission` VALUES (12,2,2); INSERT INTO `role_permission` VALUES (13,2,3); INSERT INTO `role_permission` VALUES (14,2,4); INSERT INTO `role_permission` VALUES (15,2,5); INSERT INTO `role_permission` VALUES (50,3,10); INSERT INTO `role_permission` VALUES (51,3,9); INSERT INTO `role_permission` VALUES (52,3,8); INSERT INTO `role_permission` VALUES (53,3,7); INSERT INTO `role_permission` VALUES (54,3,6); INSERT INTO `role_permission` VALUES (55,3,1); INSERT INTO `role_permission` VALUES (56,5,11); INSERT INTO `user` VALUES (1,'zhang3','a7d59dfc5332749cb801f86a24f5f590','e5ykFiNwShfCXvBRPr3wXg=='); INSERT INTO `user` VALUES (2,'li4','43e28304197b9216e45ab1ce8dac831b','jPz19y7arvYIGhuUjsb6sQ=='); INSERT INTO `user_role` VALUES (43,2,2); INSERT INTO `user_role` VALUES (45,1,1);
步骤 3 :

先运行,看到效果,再学习

edit
老规矩,先下载右上角的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
启动Application类,然后访问业务地址进行测试:

http://127.0.0.1:8080/shiro/index

权限配置地址,进行权限设置:

http://127.0.0.1:8080/shiro/config/listUser
在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程
步骤 5 :

运用前面的文件

edit
springboot,说到底,也就是配置方式变化了,该要的类,都要有,该要的jsp,也都要有。 所以本知识点会运用基于URL配置权限 中的源代码进行改进,一些要调整的地方会重点说一说
首先新建个 maven 项目
菜单 -> File -> New -> Other -> Maven -> Maven -> Maven Project -> New Maven Project
勾上这个 Create a simple project (skip archetype selection), 看吧,Springboot就是个简单的maven 项目

注: 这里用的是eclipse的,idea的做法请看 springboot-idea入门
创建项目
步骤 7 :

输入项目参数

edit
输入项目参数
修改pom.xml, 修改之后,记得刷新一下maven. 刷新办法: 项目右键->Maven->Update Project。
这个pom.xml里就提供了本项目需要的各种jar包
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.how2java</groupId> <artifactId>shiro</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shiro</name> <description>shiro</description> <packaging>war</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- servlet依赖. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- tomcat的支持.--> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <!-- 这个需要为 true 热部署才有效 --> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency> <!-- mybatis 逆向工程 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.5</version> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!-- shiro-web --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version> </dependency> </dependencies> <properties> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
步骤 9 :

application.properties

edit
在resources目录下新建文件application.properties
这里就提供了数据库链接参数, jsp重定位位置, mybatis配置以及项目访问的时候需要加上一个/shiro, 这样才和前面的教程保持一致。
application.properties
spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.jdbc.Driver mybatis.mapper-locations=classpath:com/how2java/mapper/*.xml mybatis.type-aliases-package=com.how2java.pojo server.context-path=/shiro
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

mybatis.mapper-locations=classpath:com/how2java/mapper/*.xml
mybatis.type-aliases-package=com.how2java.pojo

server.context-path=/shiro
步骤 10 :

Application.java

edit
这个是启动类。 需要注意几点:
1. 包一定要放在com.how2java 下,因为其他包,比如mapper,service什么的都是基于这个包名,SpringBoot会根据这个启动类所处与的包名去搜索其他类。 如果放其他包名下面,会折腾死个人的。
2. 还要通过这种方式指定mapper的位置,不然就找不到。 妈蛋~ 虽然application.properties 里指定了扫描mapper的位置,但是他是不听啊~ 这个也折腾了我很久~

@MapperScan(basePackages = "com.how2java.mapper")
package com.how2java; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = "com.how2java.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
package com.how2java;
 
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
@MapperScan(basePackages = "com.how2java.mapper")
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
 
}
步骤 11 :

src 下的 java 文件们

edit
然后就是上一个知识点基于URL配置权限 下载的项目里的src下的源代码。 仅仅是java源代码, 什么.xml, .properties等文件就不要了。
下载后,解压,然后把里面的src下的java源代码都复制过来。
复制过来后,记得刷新一下项目才看得到。
这些文件用用途在前面的知识点已经讲过了,这里就不再赘述,否则本页起码要有40个步骤,太过眼花缭乱。
所以,没有学习前面知识点的同学,还是回过头去学习一边,里面每个类的讲解也更加清晰详细
src 下的 java 文件们
然后就是jsp也复制过来。需要注意几点
1. WEB-INF目录要自己建
2. web.xml 文件不用复制
3. lib 目录不用复制
4. static 目录要复制过来
jsp们
步骤 13 :

ShiroConfiguration

edit
接着就是Shiro配置了。
按照Springboot的尿性,配置文件不要了, 用类来做。。。其实不是一样的么,还没有配置文件可读性强呢。。。
哎,咱们也这样做吧,以后看别人的springboot项目看得懂。
ShiroConfiguration 这个类就是shiro配置类,类名怎么命名都可以,就是要用@Configuration 注解表示它是一个配置类

@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}

这种写法就和 applicationContext-shiro.xml 中的

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

是同一个作用,只要用@Bean 注解了,就表示是被spring管理起来的对象了。

这个类里面就声明了SecurityManager,DatabaseRealm, HashedCredentialsMatcher,ShiroFilterFactoryBean 等等东西,只是写法变化了,其实和配置文件里是一样的。
需要注意一点,URLPathMatchingFilter 并没有用@Bean管理起来。 原因是Shiro的bug, 这个也是过滤器,ShiroFilterFactoryBean 也是过滤器,当他们都出现的时候,默认的什么anno,authc,logout过滤器就失效了。所以不能把他声明为@Bean。

public URLPathMatchingFilter getURLPathMatchingFilter() {
return new URLPathMatchingFilter();
}
package com.how2java.shiro; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.how2java.filter.URLPathMatchingFilter; import com.how2java.realm.DatabaseRealm; @Configuration public class ShiroConfiguration { @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 3、部分过滤器可指定参数,如perms,roles * */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){ System.out.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //自定义拦截器 Map<String, Filter> customisedFilter = new HashMap<>(); customisedFilter.put("url", getURLPathMatchingFilter()); //配置映射关系 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/index", "anon"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/config/**", "anon"); filterChainDefinitionMap.put("/doLogout", "logout");; filterChainDefinitionMap.put("/**", "url"); shiroFilterFactoryBean.setFilters(customisedFilter); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } public URLPathMatchingFilter getURLPathMatchingFilter() { return new URLPathMatchingFilter(); } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(getDatabaseRealm()); return securityManager; } @Bean public DatabaseRealm getDatabaseRealm(){ DatabaseRealm myShiroRealm = new DatabaseRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * 凭证匹配器 * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 所以我们需要修改下doGetAuthenticationInfo中的代码; * ) * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
步骤 14 :

SpringContextUtils.java

edit
继续上面的知识点,因为URLPathMatchingFilter 没有被声明为@Bean, 那么换句话说 URLPathMatchingFilter 就没有被Spring管理起来,那么也就无法在里面注入 PermissionService类了。
但是在业务上URLPathMatchingFilter 里面又必须使用PermissionService类,怎么办呢? 就借助SpringContextUtils 这个工具类,来获取PermissionService的实例。
这里提供工具类,下个步骤讲解如何使用这个工具类。
package com.how2java.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { SpringContextUtils.context = context; } public static ApplicationContext getContext(){ return context; } }
package com.how2java.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component  
public class SpringContextUtils implements ApplicationContextAware {  
    private static ApplicationContext context;  
  
    public void setApplicationContext(ApplicationContext context)  
            throws BeansException {  
        SpringContextUtils.context = context;  
    }  
  
    public static ApplicationContext getContext(){  
        return context;  
    }  
}  
步骤 15 :

URLPathMatchingFilter.java

edit
因为前面两个步骤的原因,既不能把 URLPathMatchingFilter.java 作为@Bean管理起来,又需要在里面使用 PermissionService,所以就用上一步的工具类 SpringContextUtils.java,通过如下方式获取 PermissionService了:

permissionService = SpringContextUtils.getContext().getBean(PermissionService.class);
package com.how2java.filter; import java.util.Arrays; import java.util.Set; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.PathMatchingFilter; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import com.how2java.service.PermissionService; import com.how2java.service.impl.PermissionServiceImpl; import com.how2java.util.SpringContextUtils; public class URLPathMatchingFilter extends PathMatchingFilter { @Autowired PermissionService permissionService; @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if(null==permissionService) permissionService = SpringContextUtils.getContext().getBean(PermissionService.class); String requestURI = getPathWithinApplication(request); System.out.println("requestURI:" + requestURI); Subject subject = SecurityUtils.getSubject(); // 如果没有登录,就跳转到登录页面 if (!subject.isAuthenticated()) { WebUtils.issueRedirect(request, response, "/login"); return false; } // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行) System.out.println("permissionService:"+permissionService); boolean needInterceptor = permissionService.needInterceptor(requestURI); if (!needInterceptor) { return true; } else { boolean hasPermission = false; String userName = subject.getPrincipal().toString(); Set<String> permissionUrls = permissionService.listPermissionURLs(userName); for (String url : permissionUrls) { // 这就表示当前用户有这个权限 if (url.equals(requestURI)) { hasPermission = true; break; } } if (hasPermission) return true; else { UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限"); subject.getSession().setAttribute("ex", ex); WebUtils.issueRedirect(request, response, "/unauthorized"); return false; } } } }
重新启动Application类,然后访问业务地址进行测试:

http://127.0.0.1:8080/shiro/index

权限配置地址,进行权限设置:

http://127.0.0.1:8080/shiro/config/listUser


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2021-12-26 学完了,都能走得通,但站长怎么没把shiro的会话管理写出来?难道这块不重要吗?可以写的全面一点吗?
xw541885486

学完了,都能走得通,但站长怎么没把shiro的会话管理写出来?难道这块不重要吗?可以写的全面一点吗?







回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2021-09-24 按照Springboot的尿性,配置文件不要了, 用类来做。。。其实不是一样的么,还没有配置文件可读性强呢。。。 哎,咱们也这样做吧,以后看别人的springboot项目看得懂。??
之旅

为什么不用配置文件做,用配置类做呢?




1 个答案

写bug的程序猿
答案时间:2023-03-22
配置类,我感觉我用着就舒服



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2021-04-28 个人认为这个逆向工程有点臭了
2020-11-22 正确的用户名和密码无法登陆
2020-09-10 关于shiro设置anno还是被拦截的解决方案


提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 14 条以前的提问,请 点击查看

提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 工具和中间件-Shiro-springboot 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 578362961
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图