首页 文章 精选 留言 我的

精选列表

搜索[SpringBoot],共4255篇文章
优秀的个人博客,低调大师

30分钟极速通关react mobx react-router及打通springboot

内容导航 简单开发react 将react与mobx结合开发 使用react-router进行多页面开发 将项目打包到后端项目中进行部署 将完成的项目做成脚手架,避免重复的环境搭建 需要环境 确保node已经安装 确保npm已经安装 创建项目 github项目地址 npx create-react-app test # test 为你需要创建项目的名字,会在命令当前目录下创建test的目录,包含项目所有的文件 你已经完成了创建,开始跑起来 npm start 你可以看到react已经能够在local host:3000访问了,只有一个欢迎页面 目录结构 node_modules 是当前目录安装的模块存放的地方 public index.html 是单页面的入口 src 可存放自己编写的代码,App是默认生成的欢迎页面逻辑,index 是js的主入口 开始更改你的代码 A. react简单开发 1.将App.js的代码更改如下 import React, {Component} from 'react'; import './App.css'; class App extends Component { constructor(props) { super(props) this.state = {todos: [{checked: false, text: "hello"}, {checked: true, text: "world"}]} this.handleClick=this.handleClick.bind(this) } handleClick(index) { let todos = this.state.todos todos[index].checked = !todos[index].checked this.setState({todos:todos}) } render() { let todos = this.state.todos let todosDiv = todos.map((item, index) => { return (<Todo index={index} checked={item.checked} text={item.text} handleClick={this.handleClick}/>) }) return ( <div className="App"> {todosDiv} </div> ); } } class Todo extends Component { constructor(props){ super(props) this.handleClick=this.handleClick.bind(this) } handleClick() { let index = this.props.index this.props.handleClick(index) }; render() { return ( <p><input type={'checkbox'} checked={this.props.checked} onClick={this.handleClick}/> {this.props.text}:{this.props.index} </p> ) } } export default App; 再次npm start一下看看效果吧~ 可以看到我们组件已经能够响应点击了 B. 引入mobx作为状态管理 提出问题 在上面我们可以看到想要更改状态是比较困难的,首先要将handClick方法由子组件传给父组件,再进行处理。如果我们的组件是四五层组件的时候得一步一步的往上级传递,这就会导致组件传递写的很臃肿。这个时候就需要一个将状态(即state这个值)独立开来。 react有很多状态管理的组件,比如redux,mobx。但redux写起来还是不如mobx简单明了。下面我们就来接入mobx。 接入步骤 安装依赖 npm install mobx --save npm install mobx-react --save 启用装饰器语法 # 如果有git的话,要将没有保存的文件上传之后或者删除之后才能跑eject命令 yarn run eject npm install --save-dev babel-preset-mobx 在package.json中找到babel项目,在presets里面增加"mobx" "babel": { "presets": [ "react-app", "mobx" ]}, 加入core-decorators npm install core-decorators --save 在src下增加store.AppStore.js文件 import {action, observable} from "mobx"; class AppStore { @observable todos; constructor() { this.todos = [{checked: false, text: "hello"}, {checked: true, text: "world"}] } @action.bound handleClick(index) { let todos = this.todos todos[index].checked = !todos[index].checked } } export default AppStore; 改写index.js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import {Provider} from "mobx-react"; import AppStore from './store/AppStore' let rootStore = {} rootStore['app'] = new AppStore() ReactDOM.render( <Provider {...rootStore}> <App/> </Provider>, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister(); 改写App.js import React, {Component} from 'react'; import './App.css'; import {inject, observer} from "mobx-react"; import {autobind} from "core-decorators"; @inject("app") @autobind @observer class App extends Component { constructor(props) { super(props) } render() { let todos = this.props.app.todos let todosDiv = todos.map((item, index) => { return (<Todo index={index}/>) }) return ( <div className="App"> {todosDiv} </div> ); } } @inject("app") @autobind @observer class Todo extends Component { constructor(props) { super(props) } handleClick() { let index = this.props.index this.props.app.handleClick(index) }; render() { let index = this.props.index let todo = this.props.app.todos[index] return ( <p><input type={'checkbox'} checked={todo.checked} onClick={this.handleClick}/> {todo.text}:{index} </p> ) } } export default App; npm start一下,来看看效果吧 简要说明 @inject("app")表示注入在index.js中的rootStore的属性app。是由这个标签来实现动态的注入的 @autobind 将组件之间的绑定自动完成 @observer mobx用来将react组件转换为响应式组件的注解,详情查看mobx的文档 上面可以看出,将原本的state的属性抽离到AppStore中了,对值得更改方法也是直接调用AppStore的方法,从而避免了react组件的一级一级往上传递 C. 引入react-router作为多页面管理 提出问题 上面我们完成了单页面的开发。当需要多个页面时我们就需要使用react-router来对不同路径进行渲染了 接入react-router步骤 安装依赖 npm install react-router mobx-react-router --save 增加新的页面,在src中增加component/Test.js import * as React from "react"; class Test extends React.Component{ render() { return(<p>welcome!</p>) } } export default Test; 更改index.js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import {Provider} from "mobx-react"; import AppStore from './store/AppStore' import {Route, Router, Switch} from "react-router"; import {RouterStore, syncHistoryWithStore} from "mobx-react-router"; import createHashHistory from "history/createHashHistory" import Test from "./component/Test" let rootStore = {} const hashHistory = createHashHistory() const routerStore = new RouterStore() const history = syncHistoryWithStore(hashHistory, routerStore) rootStore['app'] = new AppStore() routerStore['routing'] = routerStore ReactDOM.render( <Provider {...rootStore}> <Router history={history}> <p>here is the menu</p> <Switch> <Route path={"/test"} component={Test}/> <Route path={"/"} component={App}/> </Switch> </Router> </Provider>, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister(); npm start一下,访问下/#/test,和/#/路径,看看效果吧 简要说明 createHashHistory是单页面的访问,会在url加个#号作为定位,这个对于要打包到后台作为页面时是很方便的。 如果你直接使用node部署的话可以直接使用createBrowserHistory,url就会是没有#号的url。 D. 结合ui框架 接入步骤 找到一个合适的react ui框架,install之后按照ui框架的教程就可以开发一个相对比较好看的页面了 常见的框架有semantic,bootstrap,ant等。 E. 结合maven打包进spring boot项目 提出问题 当我们需要跟spring boot等后端项目结合,而又不想单独部署前端页面时,就需要打包进后端项目了 接入步骤 新建一个多模块的maven项目 按照之前创建的步骤,创建前端的模块,假设模块名字为view,并在前端模块的目录下增加pom.xml <build> <plugins> <plugin> <groupId>com.github.eirslett</groupId> <artifactId>frontend-maven-plugin</artifactId> <version>1.2</version> <executions> &lt;-- Install our node and npm version to run npm/node scripts--> <execution> <id>install node and npm</id> <goals> <goal>install-node-and-npm</goal> </goals> <configuration> &lt;-- 指定node的版本例如 v6.9.1 --> <nodeVersion>${nodeVersion}</nodeVersion> <npmVersion>${npmVersion}</npmVersion> <nodeDownloadRoot>https://npm.taobao.org/mirrors/node/</nodeDownloadRoot> <npmDownloadRoot>http://registry.npmjs.org/npm/-/</npmDownloadRoot> </configuration> </execution> &lt;-- Set NPM Registry --> <execution> <id>npm set registry</id> <goals> <goal>npm</goal> </goals> <configuration> &lt;--<arguments>config set registry https://registry.npmjs.org</arguments>--> <arguments>config set registry https://registry.npm.taobao.org</arguments> </configuration> </execution> &lt;-- Set SSL privilege --> <execution> <id>npm set non-strict ssl</id> <goals> <goal>npm</goal> </goals> &lt;-- Optional configuration which provides for running any npm command --> <configuration> <arguments>config set strict-ssl false</arguments> </configuration> </execution> &lt;-- Install all project dependencies --> <execution> <id>npm install</id> <goals> <goal>npm</goal> </goals> &lt;-- optional: default phase is "generate-resources" --> <phase>generate-resources</phase> &lt;-- Optional configuration which provides for running any npm command --> <configuration> <arguments>install</arguments> </configuration> </execution> &lt;-- Build and minify static files --> <execution> <id>npm run build</id> <goals> <goal>npm</goal> </goals> <configuration> <arguments>run build</arguments> </configuration> </execution> </executions> </plugin> </plugins> </build> 当进行mvn package时就会在目录下生成build目录,包含所有的页面和脚本了。 在spring boot后端项目中,将前端打包好的页面拷贝到后端目录中 <build> <plugins> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>Copy App Content</id> <phase>generate-resources</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>src/main/resources/public</outputDirectory> <overwrite>true</overwrite> <resources> <resource> <directory>${project.parent.basedir}/view/build</directory> <includes> <include>static/</include> <include>index.html</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 其中outputDirectory指明要放入的文件夹 directory指明要拷贝哪里的资源文件,需要根据你的前端模块名进行相应的修改 mvn package 一下,后端模块的打包jar里面就会有相应的资源文件啦 F. 前后端联调 步骤 在前端项目package.json中指明接口的代理 "proxy":"http://localhost:8080/" 如果servletPath不为/,则需要在后面补上相应的servletPath 当你的后端项目有设置servletPath的时候,需要相应配置前端的打包的servletPath,否则默认为/的servletpath 方法1: package.json 增加 "homepage": "." 方法2: config.paths.js文件下修改配置 function getServedPath(appPackageJson) { const publicUrl = getPublicUrl(appPackageJson); //将/修改为./ const servedUrl = envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : './'); return ensureSlash(servedUrl, true); } G. 将你创建好的项目做成脚手架 提出问题 如果每个项目都要经历上面的步骤,才能完成,那前期工作量是在太繁琐又重复 借助maven的archetype来帮你自动生成一个初始项目吧 接入步骤 按照上面的流程我们已经建好了项目 在项目目录下执行 mvn archetype:create-from-project,生成的target就是你的脚手架项目 cd target/generated-sources/archetype 目录下,执行mvn install 就把archetype放入了本地仓库了,可以进行使用了 为了deploy到远程仓库中,需要在target/generated-sources/archetype 目录下的pom.xml中加入自己的远程仓库的地址,然后在target/generated-sources/archetype 目录下mvn deploy就可以了 屏蔽掉部分不想打包进archetype的文件 要屏蔽部分文件夹时在pom中加入plugin <plugin> <artifactId>maven-archetype-plugin</artifactId> <version>3.0.1</version> <configuration> <propertyFile>archetype.properties</propertyFile> </configuration> </plugin> 新建archetype.properties文件,配置要忽略的通配符excludePatterns=/.idea/,**.iml 怎么使用archetype 创建项目在idea中,在点击file-> new-> project后弹出的对话框中选择maven 在create from archetype打勾,点击Add archetype加入创建好的archetype 填写对应的groupId,artifaceId,version后在列表中选择已有的archetype 按引导进行后续步骤的创建,然后就会自动生成跟你项目一样的啦 跨store的访问 什么是跨store访问 在上面我们有这样的代码 const routerStore = new RouterStore() rootStore['app'] = new AppStore() routerStore['routing'] = routerStore 有时候我们往往需要在一个store的方法中去访问下别的store的内容,这个时候就是跨store的访问,就需要在初始化时将rootStore传给这个store,通过rootStore去访问,改写index.js rootStore['app'] = new AppStore(rootStore) 改写AppStore.js,增加构造函数 constructor(rootStore) { this.rootStore = rootStore } 这样就可以在AppStore.js的函数中通过this.rootStore 去获取所有store的json,从而访问所有的store了

优秀的个人博客,低调大师

这10道springboot常见面试题你需要了解下

1、什么是Spring Boot? 多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能。 如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置。 因此,开始一个新的spring项目需要很多努力,因为我们现在必须从头开始做所有事情。 Spring Boot是解决这个问题的方法。Spring Boot已经建立在现有spring框架之上。使用spring启动,我们避免了之前我们必须做的所有样板代码和配置。 因此,Spring Boot可以帮助我们以最少的工作量,更加健壮地使用现有的Spring功能。 2、Spring Boot有哪些优点? 减少开发,测试时间和努力。 使用JavaConfig有助于避免使用XML。 避免大量的Maven导入和各种版本冲突。 提供意见发展方法。 通过提供默认值快速开始开发。 没有单独的Web服务器需要。这意味着你不再需要启动Tomcat,Glassfish或其他任何东西。 需要更少的配置 因为没有web.xml文件。只需添加用@ Configuration注释的类,然后添加用@Bean注释的方法,Spring将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired添加到bean方法中,以使Spring自动装入需要的依赖关系中。 基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring将在(application{environment} .properties)中加载后续的应用程序属性文件。 3、什么是JavaConfig? Spring JavaConfig是Spring社区的产品,它提供了配置Spring IoC容器的纯Java方法。因此它有助于避免使用XML配置。使用JavaConfig的优点在于: 面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分利用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。 减少或消除XML配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在XML和Java之间来回切换。 JavaConfig为开发人员提供了一种纯Java方法来配置与XML配置概念相似的Spring容器。 从技术角度来讲,只使用JavaConfig配置类来配置容器是可行的,但实际上很多人认为将JavaConfig与XML混合匹配是理想的。 类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java 5.0对泛型的支持,现在可以按类型而不是按名称检索bean,不需要任何强制转换或基于字符串的查找。 4、如何重新加载Spring Boot上的更改,而无需重新启动服务器? 这可以使用DEV工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式tomcat将重新启动。 Spring Boot有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。 开发人员可以重新加载Spring Boot上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot在发布它的第一个版本时没有这个功能。 这是开发人员最需要的功能。DevTools模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供H2数据库控制台以更好地测试应用程序。 5、Spring Boot中的监视器是什么? Spring boot actuator是spring启动框架中的重要功能之一。Spring boot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。 有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。 6、如何在Spring Boot中禁用Actuator端点安全性? 默认情况下,所有敏感的HTTP端点都是安全的,只有具有ACTUATOR角色的用户才能访问它们。 安全性是使用标准的HttpServletRequest.isUserInRole方法实施的。 我们可以使用management.security.enabled = false 来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。 如何在自定义端口上运行Spring Boot应用程序? 为了在自定义端口上运行Spring Boot应用程序,您可以在application.properties中指定端口。 server.port = 8090 7、什么是YAML? YAML是一种人类可读的数据序列化语言。它通常用于配置文件。 与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 8、如何实现Spring Boot应用程序的安全性? 为了实现Spring Boot的安全性,我们使用 spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。 9、如何集成Spring Boot和ActiveMQ? 对于集成Spring Boot和ActiveMQ,我们使用spring-boot-starter-activemq 依赖关系。 它只需要很少的配置,并且不需要样板代码。 10、如何使用Spring Boot实现分页和排序? 使用Spring Boot实现分页非常简单。使用Spring Data-JPA可以实现将可分页的org.springframework.data.domain.Pageable传递给存储库方法。

优秀的个人博客,低调大师

java B2B2C Springboot多租户电子商城系统

大型企业分布式互联网电子商务平台技术列表 愿意了解源码的朋友直接企鹅求求:二一四七七七五六三三 技术列表: Spring Cloud Config配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion Spring Cloud Bus事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署 Eureka云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 Hystrix熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。 ZuulZuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。 Spring Cloud Security基于spring security的安全工具包,为你的应用程序添加安全控制。 FeignFeign是一种声明式、模板化的HTTP客户端。 通用架构: springmvc,spring boot,spring security,Oauth2.0,mybatis,mybatis plus 、kafka、zookeepre 前端框架:Bootstrap 4 、html5、css3 扁平化风格 技术架构图:资料和源码来源

优秀的个人博客,低调大师

SpringBoot+Shiro学习之数据库动态权限管理和Redis缓存

之前我们整合Shiro,完成了登录认证和权限管理的实现,登录认证没什么说的,需要实现AuthorizingRealm中的doGetAuthenticationInfo方法进行认证,但是我们在实现doGetAuthorizationInfo权限控制这个方法的时候发现以下两个问题: 第一个问题:我们在ShiroConfig中配置链接权限的时候,每次只要有一个新的链接,或则权限需要改动,都要在ShiroConfig.java中进行权限的修改。而且改动后还需要重新启动程序新的权限才会生效,很麻烦。解决办法就是将这些链接的权限存入数据库,在前端可以提供增删改查的功能,在配置文件中编写权限的时候从数据库读取,当权限发生变更的时候利用ShiroFilterFactoryBean的清空功能,先clear,再set。这样就可以做到到动态的管理权限了。 第二个问题:每次在访问设置了权限的页面时,都会去执行doGetAuthorizationInfo方法来判断当前用户是否具备访问权限,由于在实际情况中,权限是不会经常改变的。解决办法就是进行缓存处理。 第一个问题解决步骤 建立数据库 我们从ShiroConfig中的filterChainDefinitionMap.put("/add", "perms[权限添加]"); 配置可以看出,我们需要存储链接,和链接需要具备的权限这两个关键字段。还有这个权限的读取是有顺序的,所以还要进行排序控制,所以我新建表为: ------------------------------ --Tablestructureforsys_permission_init ------------------------------ DROPTABLEIFEXISTS`sys_permission_init`; CREATETABLE`sys_permission_init`( `id`varchar(255)NOTNULL, `url`varchar(255)DEFAULTNULLCOMMENT'链接地址', `permission_init`varchar(255)DEFAULTNULLCOMMENT'需要具备的权限', `sort`int(50)DEFAULTNULLCOMMENT'排序', PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8; 当然可以按实际情况进行表的设计,这里只做简单学习。 改造ShiroConfig.java 改造前: @Bean publicShiroFilterFactoryBeanshirFilter(SecurityManagersecurityManager){ ShiroFilterFactoryBeanshiroFilterFactoryBean=newShiroFilterFactoryBean(); //必须设置SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); //登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //拦截器. Map<String,String>filterChainDefinitionMap=newLinkedHashMap<String,String>(); //配置不会被拦截的链接顺序判断 filterChainDefinitionMap.put("/static/**","anon"); filterChainDefinitionMap.put("/ajaxLogin","anon"); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout","logout"); filterChainDefinitionMap.put("/add","perms[权限添加]"); //<!--过滤链定义,从上向下顺序执行,一般将/**放在最为下边-->:这是一个坑呢,一不小心代码就不好使了; //<!--authc:所有url都必须认证通过才可以访问;anon:所有url都都可以匿名访问--> filterChainDefinitionMap.put("/**","authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro拦截器工厂类注入成功"); returnshiroFilterFactoryBean; } 改造后: @Bean publicShiroFilterFactoryBeanshirFilter(SecurityManagersecurityManager){ ShiroFilterFactoryBeanshiroFilterFactoryBean=newShiroFilterFactoryBean(); //必须设置SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); //登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //权限控制map. Map<String,String>filterChainDefinitionMap=newLinkedHashMap<String,String>(); //从数据库获取 List<SysPermissionInit>list=sysPermissionInitService.selectAll(); for(SysPermissionInitsysPermissionInit:list){ filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro拦截器工厂类注入成功"); returnshiroFilterFactoryBean; } 这里的selectAll()就是从数据库查询之前创建的权限管理列表,这里就不贴具体的查询代码了。 添加权限 在数据库中添加权限如下图: 现在启动程序,在控制台可以发现启动的时候程序在数据库查询了权限的列表信息。做到这步之后还没有达到动态的目的,比如现在到数据库手动修改/add链接的权限,这时不重启程序,权限是不会修改的。 动态更改权限实现 ShiroService.java: /** * *@author作者:z77z *@date创建时间:2017年2月15日下午4:16:07 */ @Service publicclassShiroService{ @Autowired ShiroFilterFactoryBeanshiroFilterFactoryBean; @Autowired SysPermissionInitServicesysPermissionInitService; /** *初始化权限 */ publicMap<String,String>loadFilterChainDefinitions(){ //权限控制map.从数据库获取 Map<String,String>filterChainDefinitionMap=newLinkedHashMap<String,String>(); List<SysPermissionInit>list=sysPermissionInitService.selectAll(); for(SysPermissionInitsysPermissionInit:list){ filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } returnfilterChainDefinitionMap; } /** *重新加载权限 */ publicvoidupdatePermission(){ synchronized(shiroFilterFactoryBean){ AbstractShiroFiltershiroFilter=null; try{ shiroFilter=(AbstractShiroFilter)shiroFilterFactoryBean .getObject(); }catch(Exceptione){ thrownewRuntimeException( "getShiroFilterfromshiroFilterFactoryBeanerror!"); } PathMatchingFilterChainResolverfilterChainResolver=(PathMatchingFilterChainResolver)shiroFilter .getFilterChainResolver(); DefaultFilterChainManagermanager=(DefaultFilterChainManager)filterChainResolver .getFilterChainManager(); //清空老的权限控制 manager.getFilterChains().clear(); shiroFilterFactoryBean.getFilterChainDefinitionMap().clear(); shiroFilterFactoryBean .setFilterChainDefinitionMap(loadFilterChainDefinitions()); //重新构建生成 Map<String,String>chains=shiroFilterFactoryBean .getFilterChainDefinitionMap(); for(Map.Entry<String,String>entry:chains.entrySet()){ Stringurl=entry.getKey(); StringchainDefinition=entry.getValue().trim() .replace("",""); manager.createChain(url,chainDefinition); } System.out.println("更新权限成功!!"); } } } 这样,可以在修改权限之后,执行updatePermission()这个方法,权限就会先被clear,然后重新查询权限列表后再set。动态修改就实现了! 注意:在本学习项目里面,我在设置登录用户的权限的时候是写死了的,所以每个登录用户权限都是一样的,实际开发中在MyShiroRealm文件中设置登录用户的权限是从数据库获取的。还有在实际开发中sys_permission_init权限管理这种表是会在前端提供增删改查功能的,我学习的时候是直接在数据库手动修改。说到底,本人很懒! 第二个问题的解决步骤 我们知道Shiro 提供了一系列让我们自己实现的接口,包括org.apache.shiro.cache.CacheManager 、org.apache.shiro.cache.Cache 等接口。那么我们要对这些做实现,就实现了 Shiro 对 Session 和用户认证信息、用户缓存信息等的缓存,存储。我们可以用缓存,如 Redis 、 memcache 、 EHCache 等,甚至我们可以用数据库,如 Oracle 、 Mysql 等,都可以,只有效率的快慢问题,功能都可以达到。 那么我的教程是采用了 Redis ,而且是用了Jedis 。Jedis 可以实现pool 和hash 的集群Redis 。 本来我想是在网上学习学习,自己实现redis的集成。最后发现已经有大神已经做了这个插件,对shiro提供的CacheManager,Cache ,这些接口使用redis都有了很好的实现。我就不需要再费心学习了,我们就直接拿来用。 pom.xml依赖添加 <!--shiro+redis缓存插件--> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>2.4.2.1-RELEASE</version> </dependency> 改造ShiroConfig.java文件 /** *@author作者z77z *@date创建时间:2017年2月10日下午1:16:38 * */ @Configuration publicclassShiroConfig{ @Autowired SysPermissionInitServicesysPermissionInitService; @Value("${spring.redis.host}") privateStringhost; @Value("${spring.redis.port}") privateintport; @Bean publicShiroFilterFactoryBeanshirFilter(SecurityManagersecurityManager){ ShiroFilterFactoryBeanshiroFilterFactoryBean=newShiroFilterFactoryBean(); //必须设置SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); //登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //权限控制map. Map<String,String>filterChainDefinitionMap=newLinkedHashMap<String,String>(); //从数据库获取 List<SysPermissionInit>list=sysPermissionInitService.selectAll(); for(SysPermissionInitsysPermissionInit:list){ filterChainDefinitionMap.put(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit()); } shiroFilterFactoryBean .setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro拦截器工厂类注入成功"); returnshiroFilterFactoryBean; } @Bean publicSecurityManagersecurityManager(){ DefaultWebSecurityManagersecurityManager=newDefaultWebSecurityManager(); //设置realm. securityManager.setRealm(myShiroRealm()); //自定义缓存实现使用redis securityManager.setCacheManager(cacheManager()); //自定义session管理使用redis securityManager.setSessionManager(SessionManager()); returnsecurityManager; } /** *身份认证realm;(这个需要自己写,账号密码校验;权限等) * *@return */ @Bean publicMyShiroRealmmyShiroRealm(){ MyShiroRealmmyShiroRealm=newMyShiroRealm(); returnmyShiroRealm; } /** *配置shiroredisManager * *@return */ publicRedisManagerredisManager(){ RedisManagerredisManager=newRedisManager(); redisManager.setHost(host); redisManager.setPort(port); redisManager.setExpire(1800);//配置过期时间 //redisManager.setTimeout(timeout); //redisManager.setPassword(password); returnredisManager; } /** *cacheManager缓存redis实现 * *@return */ publicRedisCacheManagercacheManager(){ RedisCacheManagerredisCacheManager=newRedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); returnredisCacheManager; } /** *RedisSessionDAOshirosessionDao层的实现通过redis */ publicRedisSessionDAOredisSessionDAO(){ RedisSessionDAOredisSessionDAO=newRedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); returnredisSessionDAO; } /** *shirosession的管理 */ publicDefaultWebSessionManagerSessionManager(){ DefaultWebSessionManagersessionManager=newDefaultWebSessionManager(); sessionManager.setSessionDAO(redisSessionDAO()); returnsessionManager; } } 这里,因为使 这里,因为使用的是redis来做容器缓存,所以要创建redisManager来配置shiro,SessionManager(),cacheManager()这两个类都是插件给我们写好了的,里面就是对shiro提供的接口的redis实现方式。 使用插件就是这么简单,直接启动程序,多访问几次具有权限的页面,查看控制台发现,权限认证方法:MyShiroRealm.doGetAuthorizationInfo()会只执行了一次。说明我们的缓存生效了。 总结 到此,我们集成shiro和redis,学习了一下功能的实现: 用户必须要登陆之后才能访问定义链接,否则跳转到登录页面,被禁用户不能登录。并且对一些敏感操作链接设置权限,只有满足权限的才可以访问。 每个链接的权限信息保存在数据库,可以动态进行设置,并且热加载权限。 使用redis对shiro的用户信息进行缓存,不用每次都去执行MyShiroRealm.doGetAuthorizationInfo()权限认证方法。 之前有很多同学下载我的项目时,运行会报错,那是因为最近都在不断修改提交,有可能会出现版本问题,现在我在我的码云上面创建了stable_version分支,都是可以跑起来的。sqltable放在resource目录下面。

优秀的个人博客,低调大师

SpringBoot 2.0整合jackson配置日期格式化和反序列化

网上杂七杂八的说法不一,大多数都是抄来抄去,没有实践,近期在项目频繁遇到boot+jackson处理日期的问题,故开此贴。 首先是POM <?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>io.cj.learning</groupId> <artifactId>boot2exam</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>boot2exam</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 然后是yml文件 (当然yml这东西很多人不喜欢,我也写了个properties版本的) spring: jackson: #参数意义: #JsonInclude.Include.ALWAYS 默认 #JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化 #JsonInclude.Include.NON_EMPTY 属性为 空(””) 或者为 NULL 都不序列化 #JsonInclude.Include.NON_NULL 属性为NULL 不序列化 default-property-inclusion: ALWAYS time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss 上面配置对应的properties文件版本: #jackson相关配置 spring.jackson.date-format = yyyy-MM-dd HH:mm:ss #时区必须要设置 spring.jackson.time-zone= GMT+8 #ALWAYS的意思是即时属性为null,仍然也会输出这个key spring.jackson.default-property-inclusion=ALWAYS 然后来定义一个Controller和JAVA Bean Controller: package io.cj.learning.boot2exam.controller; import io.cj.learning.boot2exam.model.DateFormatTest; import org.springframework.web.bind.annotation.*; import java.text.SimpleDateFormat; import java.util.Date; @RestController @RequestMapping(value="/test") public class TestController { /** * 测试时间序列化, java.util.date 类型 -> String * @return */ @RequestMapping(value="/dateFormatTest", method = RequestMethod.GET) @ResponseBody public DateFormatTest dateFormatTest(){ DateFormatTest dateFormatTest = new DateFormatTest(); dateFormatTest.setIntProperties(100); dateFormatTest.setDateProperties(new Date()); return dateFormatTest; } /** * 测试时间反序列化 String -> java.util.date 类型 */ @RequestMapping(value="/dateFormatTest2" ,method = RequestMethod.POST) public void dateFormatTest2(@RequestBody DateFormatTest model){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(model.getIntProperties()); System.out.println(sdf.format(model.getDateProperties())); System.out.println(model.getStrProperties()); } } Java Bean: package io.cj.learning.boot2exam.model; import java.util.Date; /** * 一个model,里面带一个日期类型 */ public class DateFormatTest { private Integer intProperties; private Date dateProperties; private String strProperties; public Integer getIntProperties() { return intProperties; } public void setIntProperties(Integer intProperties) { this.intProperties = intProperties; } public Date getDateProperties() { return dateProperties; } public void setDateProperties(Date dateProperties) { this.dateProperties = dateProperties; } public String getStrProperties() { return strProperties; } public void setStrProperties(String strProperties) { this.strProperties = strProperties; } } 启动主类: package io.cj.learning.boot2exam; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Boot2examApplication { public static void main(String[] args) { SpringApplication.run(Boot2examApplication.class, args); } } 测试: 试一下,首先是日期序列化, 请求一下试试 然后是反序列化,也请求一个试试:

优秀的个人博客,低调大师

SpringCloud+SpringBoot+mybatis分布式微服务云架构开发Web应用

在完成配置之后,举一个简单的例子,在快速入门工程的基础上,举一个简单的示例来通过Thymeleaf渲染一个页面。 @Controller public class HelloController { @RequestMapping("/") public String index(ModelMap map) { // 加入一个属性,用来在模板中读取 map.addAttribute("host", "http://blog.didispace.com"); // return模板文件的名称,对应src/main/resources/templates/index.html return "index"; } } <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8" /> <title></title> </head> <body> <h1 th:text="${host}">Hello World</h1> </body> </html> 如上页面,直接打开html页面展现Hello World,但是启动程序后,访问http://localhost:8080/,则是展示Controller中host的值:http://blog.didispace.com,做到了不破坏HTML自身内容的数据逻辑分离。 更多Thymeleaf的页面语法,还请访问Thymeleaf的官方文档查询使用。 Thymeleaf的默认参数配置 如有需要修改默认配置的时候,只需复制下面要修改的属性到application.properties中,并修改成需要的值,如修改模板文件的扩展名,修改默认的模板路径等。 # Enable template caching. spring.thymeleaf.cache=true # Check that the templates location exists. spring.thymeleaf.check-template-location=true # Content-Type value. spring.thymeleaf.content-type=text/html # Enable MVC Thymeleaf view resolution. spring.thymeleaf.enabled=true # Template encoding. spring.thymeleaf.encoding=UTF-8 # Comma-separated list of view names that should be excluded from resolution. spring.thymeleaf.excluded-view-names= # Template mode to be applied to templates. See also StandardTemplateModeHandlers. spring.thymeleaf.mode=HTML5 # Prefix that gets prepended to view names when building a URL. spring.thymeleaf.prefix=classpath:/templates/ # Suffix that gets appended to view names when building a URL. spring.thymeleaf.suffix=.html spring.thymeleaf.template-resolver-order= # Order of the template resolver in the chain. spring.thymeleaf.view-names= # Comma-separated list of view names that can be resolved. 支持JSP的配置Spring Boot并不建议使用,但如果一定要使用,可以参考此工程作为脚手架:JSP支持 完整项目的源码来源 技术支持1791743380

优秀的个人博客,低调大师

Solon (可替换 SpringBoot)集成 Docker 实战:30分钟搞定容器化部署

作为 Java 开发者,你是否已经厌倦了 Spring Boot 容器化过程中的各种复杂配置和坑点?想要尝试更轻量、更高效的框架?那么 Solon 绝对值得你关注。今天我就带你实战 Solon 框架与 Docker 的集成,从环境准备到最终部署,全程避坑指南,让你 30 分钟内轻松搞定! 为什么选择 Solon + Docker? 在微服务架构盛行之下,应用容器化已成为标配。但传统的 Spring Boot 虽然功能强大,但在启动速度、内存占用和容器化体验上仍有优化空间。听一个老同事说,他们公司经常有1GB大小的 Spring Boot Jar 包。 Solon 的优势: 启动速度极快:Solon 应用的启动时间通常是 Spring Boot 的1/10到1/5 内存占用更小:基础镜像体积更小,运行时内存消耗更低。通常只有 Spring Boot 的1/10到1/2 配置更简洁:Docker 集成配置简单明了,减少踩坑概率 原生支持容器化:从设计之初就考虑了云原生场景 环境准备:三步搞定基础配置 在开始之前,确保你的本地环境满足以下要求: 1. 确认环境版本 JDK 版本:Solon 支持 JDK 8+,推荐使用 JDK 11 或 17 以获得更好的容器化支持 Docker 版本:Docker 20.10+,推荐使用 Docker Desktop 4.0+ Maven 版本:Maven 3.6+,确保插件兼容性 快速验证命令: java -version docker -v mvn -v 2. 创建 Solon 项目 如果你还没有 Solon 项目,可以通过网页版生成器快速创建: https://solon.noear.org/start/ 核心步骤:Docker 集成实战 1. 配置 Maven 插件 在pom.xml中添加 Docker 打包插件。这里我们使用经过验证的spotify插件: <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <!-- 镜像名称配置 --> <imageName>solon-demo</imageName> <imageTags> <imageTag>${project.version}</imageTag> <imageTag>latest</imageTag> </imageTags> <!-- 使用推荐的基础镜像 --> <baseImage>adoptopenjdk/openjdk11:jre-11.0.11_9-alpine</baseImage> <!-- 容器启动命令 --> <entryPoint>["java", "-jar", "/${project.build.finalName}.jar", "--server.port=8080", "--drift=1"]</entryPoint> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> 避坑提示: 使用 alpine 版本的 JDK 镜像可以显著减小镜像体积(如果不兼容,可以再换个别的) entryPoint 必须使用数组格式,确保参数传递正确 确保 finalName 与打包后的 jar 包名称一致 加上--drift=1表示当前环境ip会漂移的(如果有注册服务,当下线时要求不作健康检测)。这是 solon 对云原生的一种优化。 2.备选方案:使用 Dockerfile 如果你更喜欢传统的 Dockerfile 方式,可以在项目根目录创建 Dockerfile: # 使用轻量级基础镜像 FROM adoptopenjdk/openjdk11:jre-11.0.11_9-alpine # 设置工作目录 WORKDIR /app # 复制 jar 文件 COPY target/solon-demo-1.0.0.jar app.jar # 暴露端口(根据你的应用配置调整) EXPOSE 8080 # 启动应用 ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8080", "--drift=1"] 然后在 pom.xml 中配置插件使用 Dockerfile: <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <imageName>solon-demo</imageName> <dockerDirectory>${project.basedir}</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> 3. 构建和运行 构建 Docker 镜像: # 先打包应用 mvn clean package # 构建 Docker 镜像 mvn docker:build 构建成功后,验证镜像: docker images | grep solon-demo 运行容器: # 第一次运行 docker run -d -p 8080:8080 --name solon-app solon-demo # 查看运行状态 docker ps | grep solon-app # 查看日志 docker logs solon-app 容器管理命令: # 停止容器 docker stop solon-app # 重启容器 docker restart solon-app # 删除容器 docker rm solon-app 进阶技巧:优化和部署 1. 镜像标签管理和推送 为镜像打标签并推送到镜像仓库: # 打标签 docker tag solon-demo:latest your-repo/solon-demo:1.0.0 docker tag solon-demo:latest your-repo/solon-demo:latest # 推送到仓库 docker push your-repo/solon-demo:1.0.0 docker push your-repo/solon-demo:latest 2. 生产环境配置 对于生产环境,建议添加健康检查和资源限制: docker run -d \ -p 8080:8080 \ --name solon-app \ --memory=512m \ --cpus=1.0 \ solon-demo 常见问题排查 1. 容器启动后立即退出 检查应用启动日志:docker logs solon-app 确认 jar 包路径正确 验证端口是否被占用 2. 应用无法访问 检查端口映射:docker ps 确认端口映射关系 验证防火墙设置 检查应用监听的地址(确保是 0.0.0.0 而不是 127.0.0.1) 3. 镜像体积过大 使用 alpine 版本的基础镜像 多阶段构建去除构建依赖 使用 JRE 而不是完整的 JDK 总结 Solon 与 Docker 的集成相比传统框架更加轻量简洁,主要优势体现在: 配置简单:Maven 插件配置直观,减少出错概率 镜像小巧:基础镜像选择灵活,最终镜像体积更小 启动快速:容器启动速度更快,适合快速扩缩容 通过本文的实战指南,你应该能够在 30 分钟内完成 Solon 应用的 Docker 化。赶紧拿起你的 Solon 项目实践一下吧!如果在实践中遇到任何问题,欢迎在评论区交流讨论。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册