【Grails4+spring security】

article/2025/9/13 18:14:40

Grails4+spring security实现单用户登录

    • 描述
    • 1、新建项目目录结构如图所示
    • 2、打开根目录下的build.gradle文件,dependencies中添加spring-security依赖
    • 3、创建用户、角色的domain
    • 4、创建登录控制器LoginController
    • 5、创建注销控制器 LogoutController
    • 6、自定义一个ConcurrentSingleSessionAuthenticationStrategy类实现SessionAuthenticationStrategy接口覆盖默认方法
    • 7、打开grails-app/conf/spring/resource.groovy,配置DSL
    • 8、在grails-app/conf目录下创建application.groovy类
    • 9、在grails-app/conf目录下创建application.yml类
    • 10、打开grails-app/init/BootStrap.groovy

描述

本文档将实现单用户登录,实际效果是:当一个用户在一个地方登录了之后,另一个地方也用该用户登录,前一个登录被迫下线,每次登录都会用新的session替换旧的session。

1、新建项目目录结构如图所示

在这里插入图片描述

2、打开根目录下的build.gradle文件,dependencies中添加spring-security依赖

compile "org.grails.plugins:spring-security-core:4.0.0"

3、创建用户、角色的domain

也可用命令快速生成域类:

grails s2-quickstart com.system UserInfo RoleInfo

在这里插入图片描述

  • 3.1 用户(UserInfo)
package com.systemimport groovy.transform.EqualsAndHashCode
import groovy.transform.ToString@EqualsAndHashCode(includes='username')
@ToString(includes='username', includeNames=true, includePackage=false)
class UserInfo implements Serializable {transient springSecurityServiceprivate static final long serialVersionUID = 1String usernameString passwordboolean enabled = trueboolean accountExpiredboolean accountLockedboolean passwordExpiredString nicknameSet<RoleInfo> getAuthorities() {(UserRole.findAllByUser(this) as List<UserRole>)*.role as Set<RoleInfo>}static constraints = {password blank: false, password: trueusername blank: false, unique: truenickname nullable: true, maxSize: 15}static mapping = {password column: '`password`'}def beforeInsert() {encodePassword()}def beforeUpdate() {if (isDirty('password')) {encodePassword()}}protected void encodePassword() {password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password}}
  • 3.2 RoleInfo(角色)
package com.systemimport groovy.transform.EqualsAndHashCode
import groovy.transform.ToString@EqualsAndHashCode(includes='authority')
@ToString(includes='authority', includeNames=true, includePackage=false)
class RoleInfo implements Serializable {private static final long serialVersionUID = 1String authorityString remarkstatic constraints = {authority blank: false, unique: trueremark blank: false}static mapping = {cache true}}
  • 3.3 用户-角色关联(UserRole)
package com.systemimport grails.gorm.DetachedCriteria
import groovy.transform.ToString
import org.codehaus.groovy.util.HashCodeHelper@ToString(cache=true, includeNames=true, includePackage=false)
class UserRole implements Serializable {private static final long serialVersionUID = 1UserInfo userRoleInfo role@Overrideboolean equals(other) {if (other instanceof UserRole) {other.userId == user?.id && other.roleId == role?.id}}@Overrideint hashCode() {int hashCode = HashCodeHelper.initHash()if (user) {hashCode = HashCodeHelper.updateHash(hashCode, user.id)}if (role) {hashCode = HashCodeHelper.updateHash(hashCode, role.id)}hashCode}static UserRole get(long userId, long roleId) {criteriaFor(userId, roleId).get()}static boolean exists(long userId, long roleId) {criteriaFor(userId, roleId).count()}private static DetachedCriteria criteriaFor(long userId, long roleId) {UserRole.where {user == UserInfo.load(userId) &&role == RoleInfo.load(roleId)}}static UserRole create(UserInfo user, RoleInfo role, boolean flush = false) {def instance = new UserRole(user: user, role: role)instance.save(flush: flush)instance}static boolean remove(UserInfo u, RoleInfo r) {if (u != null && r != null) {UserRole.where { user == u && role == r }.deleteAll()}}static int removeAll(UserInfo u) {u == null ? 0 : UserRole.where { user == u }.deleteAll() as int}static int removeAll(RoleInfo r) {r == null ? 0 : UserRole.where { role == r }.deleteAll() as int}static constraints = {role validator: { RoleInfo r, UserRole ur ->if (ur.user?.id) {UserRole.withNewSession {if (UserRole.exists(ur.user.id, r.id)) {return ['userRole.exists']}}}}}static mapping = {id composite: ['user', 'role']version false}
}

4、创建登录控制器LoginController

package com.systemimport grails.converters.JSON
import grails.plugin.springsecurity.SpringSecurityUtils
import org.springframework.context.MessageSource
import org.springframework.security.access.annotation.Secured
import org.springframework.security.authentication.AccountExpiredException
import org.springframework.security.authentication.AuthenticationTrustResolver
import org.springframework.security.authentication.CredentialsExpiredException
import org.springframework.security.authentication.DisabledException
import org.springframework.security.authentication.LockedException
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.WebAttributesimport javax.servlet.http.HttpServletResponse@Secured('permitAll')
class LoginController {/** 依赖注入认证接口authenticationTrustResolver. */AuthenticationTrustResolver authenticationTrustResolver/** 依赖注入springSecurityService. */def springSecurityService/** 依赖注入messageSource. */MessageSource messageSource/** 若登录成功,直接跳转到首页,否则跳转到auth页面登录 */def index() {if (springSecurityService.isLoggedIn()) {redirect uri: conf.successHandler.defaultTargetUrl}else {redirect action: 'auth', params: params}}/**登录页面*/def auth() {def conf = getConf()if (springSecurityService.isLoggedIn()) {redirect uri: conf.successHandler.defaultTargetUrlreturn}String postUrl = request.contextPath + conf.apf.filterProcessesUrlrender view: 'auth', model: [postUrl: postUrl,rememberMeParameter: conf.rememberMe.parameter,usernameParameter: conf.apf.usernameParameter,passwordParameter: conf.apf.passwordParameter,gspLayout: conf.gsp.layoutAuth]}/** The redirect action for Ajax requests. */def authAjax() {response.setHeader 'Location', conf.auth.ajaxLoginFormUrlrender(status: HttpServletResponse.SC_UNAUTHORIZED, text: 'Unauthorized')}/** 普通请求拒绝访问 */def denied() {if (springSecurityService.isLoggedIn() && authenticationTrustResolver.isRememberMe(authentication)) {// have cookie but the page is guarded with IS_AUTHENTICATED_FULLY (or the equivalent expression)redirect action: 'full', params: paramsreturn}[gspLayout: conf.gsp.layoutDenied]}/** Login page for users with a remember-me cookie but accessing a IS_AUTHENTICATED_FULLY page. */def full() {def conf = getConf()render view: 'auth', params: params,model: [hasCookie: authenticationTrustResolver.isRememberMe(authentication),postUrl: request.contextPath + conf.apf.filterProcessesUrl,rememberMeParameter: conf.rememberMe.parameter,usernameParameter: conf.apf.usernameParameter,passwordParameter: conf.apf.passwordParameter,gspLayout: conf.gsp.layoutAuth]}/** ajax登录认证失败信息提示 */def authfail() {String msg = ''def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]if (exception) {if (exception instanceof AccountExpiredException) {msg = messageSource.getMessage('springSecurity.errors.login.expired', null, "Account Expired", request.locale)}else if (exception instanceof CredentialsExpiredException) {msg = messageSource.getMessage('springSecurity.errors.login.passwordExpired', null, "Password Expired", request.locale)}else if (exception instanceof DisabledException) {msg = messageSource.getMessage('springSecurity.errors.login.disabled', null, "Account Disabled", request.locale)}else if (exception instanceof LockedException) {msg = messageSource.getMessage('springSecurity.errors.login.locked', null, "Account Locked", request.locale)}else {msg = messageSource.getMessage('springSecurity.errors.login.fail', null, "Authentication Failure", request.locale)}}if (springSecurityService.isAjax(request)) {render([error: msg] as JSON)}else {flash.message = msgredirect action: 'auth', params: params}}/** ajax登录成功 */def ajaxSuccess() {render([success: true, username: authentication.name] as JSON)}/** ajaax拒绝访问 */def ajaxDenied() {render([error: 'access denied'] as JSON)}protected Authentication getAuthentication() {SecurityContextHolder.context?.authentication}protected ConfigObject getConf() {SpringSecurityUtils.securityConfig}/** 单用户登录(已登录返回给用户提示) */def already() {render view: "already"}
}

5、创建注销控制器 LogoutController

package com.systemimport grails.plugin.springsecurity.SpringSecurityUtils
import org.springframework.security.access.annotation.Secured
import org.springframework.security.web.RedirectStrategy@Secured('permitAll')
class LogoutController {/** 依赖注入RedirectStrategy. */RedirectStrategy redirectStrategy/*** 注销方法*/def index() {//        if (!request.post && SpringSecurityUtils.getSecurityConfig().logout.postOnly) {
//            response.sendError HttpServletResponse.SC_METHOD_NOT_ALLOWED // 405
//            return
//        }// TODO put any pre-logout code hereredirectStrategy.sendRedirect request, response, SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/logoff'response.flushBuffer()}
}

6、自定义一个ConcurrentSingleSessionAuthenticationStrategy类实现SessionAuthenticationStrategy接口覆盖默认方法

package com.sessionimport org.springframework.security.core.Authentication
import org.springframework.security.core.session.SessionRegistry
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy
import org.springframework.util.Assertimport javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse/*** 会话管理类*/
class ConcurrentSingleSessionAuthenticationStrategy implements SessionAuthenticationStrategy {private SessionRegistry sessionRegistry/*** @param 将新的会话赋值给sessionRegistry*/public ConcurrentSingleSessionAuthenticationStrategy(SessionRegistry sessionRegistry) {Assert.notNull(sessionRegistry, "SessionRegistry cannot be null")this.sessionRegistry = sessionRegistry}/*** 覆盖父类的onAuthentication方法* 用新的session替换就的session*/public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {def sessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false)def principals = sessionRegistry.getAllPrincipals()sessions.each {if (it.principal == authentication.getPrincipal()) {it.expireNow()}}}
}

(注:此类我是在src/main/groovy里面创建的,你也可以在其他地方创建)

7、打开grails-app/conf/spring/resource.groovy,配置DSL

在这里插入图片描述

import com.session.ConcurrentSingleSessionAuthenticationStrategy
import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter// Place your Spring DSL code here
beans = {sessionRegistry(SessionRegistryImpl)//很重要sessionFixationProtectionStrategy(SessionFixationProtectionStrategy){migrateSessionAttributes = truealwaysCreateSession = true}// "/login/already"为重定向请求concurrentSingleSessionAuthenticationStrategy(ConcurrentSingleSessionAuthenticationStrategy,ref('sessionRegistry'))registerSessionAuthenticationStrategy(RegisterSessionAuthenticationStrategy,ref('sessionRegistry'))sessionAuthenticationStrategy(CompositeSessionAuthenticationStrategy,[ref('concurrentSingleSessionAuthenticationStrategy'), ref('sessionFixationProtectionStrategy'), ref('registerSessionAuthenticationStrategy')])concurrentSessionFilter(ConcurrentSessionFilter, ref('sessionRegistry'), "/login/already")
}

8、在grails-app/conf目录下创建application.groovy类

在这里插入图片描述

//grails.plugin.springsecurity.successHandler.alwaysUseDefault = true
//grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/your-url' //登录成功后跳转地址
grails.plugin.springsecurity.userLookup.usernamePropertyName ="username"
grails.plugin.springsecurity.userLookup.passwordPropertyName ="password"
grails.plugin.springsecurity.authority.className="com.system.RoleInfo"
grails.plugin.springsecurity.userLookup.userDomainClassName="com.system.UserInfo"
grails.plugin.springsecurity.userLookup.authorityJoinClassName="com.system.UserRole"
grails.plugin.springsecurity.controllerAnnotations.staticRules = [[pattern: '/',                 access: ['permitAll']],[pattern: '/error',            access: ['permitAll']],[pattern: '/index',            access: ['permitAll']],[pattern: '/index.gsp',        access: ['permitAll']],[pattern: '/shutdown',         access: ['permitAll']],[pattern: '/assets/**',        access: ['permitAll']],[pattern: '/**/js/**',         access: ['permitAll']],[pattern: '/**/css/**',        access: ['permitAll']],[pattern: '/**/images/**',     access: ['permitAll']],[pattern: '/**/favicon.ico',   access: ['permitAll']],[pattern: '/login/already.gsp',access: ['permitAll']],[pattern: '/user/**',        access: 'ROLE_USER'],[pattern: '/admin/**',       access: ['ROLE_ADMIN', 'isFullyAuthenticated()']]
]
grails.plugin.springsecurity.interceptUrlMap = [[pattern: '/',               access: ['permitAll']],[pattern: '/error',          access: ['permitAll']],[pattern: '/index',          access: ['permitAll']],[pattern: '/index.gsp',      access: ['permitAll']],[pattern: '/shutdown',       access: ['permitAll']],[pattern: '/assets/**',      access: ['permitAll']],[pattern: '/**/js/**',       access: ['permitAll']],[pattern: '/**/css/**',      access: ['permitAll']],[pattern: '/**/images/**',   access: ['permitAll']],[pattern: '/**/favicon.ico', access: ['permitAll']],[pattern: '/login/**',       access: ['permitAll']],[pattern: '/login/already',  access: ['permitAll']],[pattern: '/logout/**',      access: ['permitAll']]
]grails.plugin.springsecurity.filterChain.filterNames = [ 'securityContextPersistenceFilter', 'logoutFilter', 'concurrentSessionFilter', 'rememberMeAuthenticationFilter', 'anonymousAuthenticationFilter', 'exceptionTranslationFilter', 'filterInvocationInterceptor' ]

9、在grails-app/conf目录下创建application.yml类

在这里插入图片描述

---
grails:profile: webcodegen:defaultPackage: longiwebgorm:reactor:# Whether to translate GORM events into Reactor events# Disabled by default for performance reasonsevents: falsecontrollers:upload:maxFileSize: 2000000000maxRequestSize: 2000000000
myParams:name: 'MrWt'age: '23'post: 'PM'NxLgg:appid: 'wx171071cfc94cb83a'appsecret: 'e86e2e3d51facec23vr27269b6d79bc6'merchantid: '1528776352'merchantkey: '0D0tsOh0wFyjvAppL06idNE29hedecac'RemoveIMEIPlat: '142,143,165,166,241,242,243,342,343'logFilePath: 'd:\\log'meterType: '1'unreported: '0'# temporaryTask / unreported: 1为开启,0为关闭temporaryTask: '0'maxTime: 20info:app:name: '@info.app.name@'version: '@info.app.version@'grailsVersion: '@info.app.grailsVersion@'
spring:jmx:unique-names: truemain:banner-mode: "off"groovy:template:check-template-location: falsedevtools:restart:additional-exclude:- '*.gsp'- '**/*.gsp'- '*.gson'- '**/*.gson'- 'logback.groovy'- '*.properties'
management:endpoints:enabled-by-default: false
---
grails:mime:disable:accept:header:userAgents:- Gecko- WebKit- Presto- Tridenttypes:all: '*/*'atom: application/atom+xmlcss: text/csscsv: text/csvform: application/x-www-form-urlencodedhtml:- text/html- application/xhtml+xmljs: text/javascriptjson:- application/json- text/jsonmultipartForm: multipart/form-datapdf: application/pdfrss: application/rss+xmltext: text/plainhal:- application/hal+json- application/hal+xmlxml:- text/xml- application/xmlurlmapping:cache:maxsize: 1000controllers:defaultScope: singletonconverters:encoding: UTF-8views:default:codec: htmlgsp:encoding: UTF-8htmlcodec: xmlcodecs:expression: nonescriptlet: htmltaglib: nonestaticparts: noneresources:pattern:'/**'
management:endpoints:jmx:unique-names: true
---
hibernate:cache:queries: falseuse_second_level_cache: falseuse_query_cache: false
dataSource:pooled: truejmxExport: truedriverClassName: org.h2.Driverusername: sapassword: ''
#redis:
#    poolConfig:
#        maxIdle: 10
#        doesnotexist: true
#    host: 192.168.0.86
#    port: 6379
#    timeout: 5000
#    password: ''
#    jedis:
#        pool:
#            # 最大空闲连接数
#            max-idle: 500
#            # 最小空闲连接数
#            min-idle: 50
#            # 等待可用连接的最大时间,负数为不限制
#            max-wait: -1
#            # 最大活跃连接数,负数为不限制
#            max-active: -1environments:
#    开发库连接development:dataSource:bySearch:testWhileIdle: truevalidationQuery: SELECT 1timeBestweenEvictionRunsMillis: 3600000 #每个小时确认连接是否可用dbCreate: nonedriverClassName: com.mysql.cj.jdbc.Driverusername: rootpassword: MrWt5678url: jdbc:mysql://192.168.0.81:3306/longistation?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&tinyInt1isBit=falseproperties:jmxEnabled: trueinitialSize: 10maxActive: 50maxIdle: 20minIdle: 10maxWait: 30000removeAbandoned: trueremoveAbandonedTimeout: 60000logAbandoned: falsetestOnBorrow: falsetestOnReturn: falsetestWhileIdle: truevalidationQuery: select 1#validationQueryTimeout: 5timeBetweenEvictionRunsMillis: 3600000minEvictableIdleTimeMillis: 3600000numTestsPerEvictionRun: 100jdbcInterceptors: ConnectionStatedefaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
#   测试库连接test:dataSource:dbCreate: nonedriverClassName: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456url: jdbc:mysql://192.168.0.173:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false&tinyInt1isBit=false
# 生产库链接,tomcat发布版production:dataSource:dbCreate: nonedriverClassName: com.mysql.cj.jdbc.Driverusername: longipassword:MrWt5678url: jdbc:mysql://192.168.0.8:3306/longistation?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&tinyInt1isBit=falseproperties:jmxEnabled: trueinitialSize: 10maxActive: 50  #最大连接数maxIdle: 20   #最大空闲连接minIdle: 10   #最小空闲连接maxWait: 30000   #超时等待时间以毫秒为单位removeAbandoned: true    # 当active连接快到maxActive连接的时候,是否回收无效的连接回收removeAbandonedTimeout: 60000  # 超时时间(以秒数为单位)logAbandoned: false      # 是否在log中打印出回收Connection的错误信息,包括在哪个地方用了Connection却忘记关闭了testOnBorrow: falsetestOnReturn: falsetestWhileIdle: truevalidationQuery: select 1#validationQueryTimeout: 5timeBetweenEvictionRunsMillis: 3600000 # 连接空闲了多久会被空闲连接回收器回收minEvictableIdleTimeMillis: 3600000    # 每timeBetweenEvictionRunsMills运行一次空闲连接回收器(独立线程)numTestsPerEvictionRun: 50            # 每次检查numTestsPerEvictionRun个连接jdbcInterceptors: ConnectionStatedefaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTEDdataSources:mysqlTest:dbCreate: nonedialect: org.hibernate.dialect.MySQLInnoDBDialectdriverClassName: com.mysql.cj.jdbc.Driverusername: rootpassword: MrWt123456url: jdbc:mysql://192.168.0.8:3306/mysql?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=falseproperties:jmxEnabled: trueinitialSize: 10maxActive: 50maxIdle: 20minIdle: 10maxWait: 30000removeAbandoned: trueremoveAbandonedTimeout: 60000logAbandoned: falsetestOnBorrow: falsetestOnReturn: falsetestWhileIdle: truevalidationQuery: select 1timeBetweenEvictionRunsMillis: 3600000minEvictableIdleTimeMillis: 3600000numTestsPerEvictionRun: 100jdbcInterceptors: ConnectionStatedefaultTransactionIsolation: 2oracleTest:dbCreate: nonedialect: org.hibernate.dialect.OracleDialectdriverClassName: oracle.jdbc.driver.OracleDriverusername: lggmrhotpassword: MrWt6666url: jdbc:oracle:thin:@192.168.0.89:1521:orclsqlServerTest:dbCreate: nonepooled: truejmxExport: truedriverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriverusername: oapassword: MrWt5678url: jdbc:sqlserver://192.168.0.3:1433;database=zkteco_database;sqlServerTest43:dbCreate: nonepooled: truejmxExport: truedriverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriverusername: oapassword: MrWt5678url: jdbc:sqlserver://192.168.0.3:1433;database=STCard;sqlServerTest14:dbCreate: nonepooled: truejmxExport: truedriverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriverusername: u8password: MrWt5678url: jdbc:sqlserver://192.168.0.1:1433;database=UFDATA_333_2017;

10、打开grails-app/init/BootStrap.groovy

在这里插入图片描述

  • 10.1 保存用户、角色、用户-角色信息
import com.system.RoleInfo
import com.system.UserInfo
import com.system.UserRoleclass BootStrap {def init = { servletContext ->//创建角色def role1 = new RoleInfo(authority: "ROLE_ADMIN", remark: "管理员").save()def role2 = new RoleInfo(authority: "ROLE_SUPSYS", remark: "超级管理员").save()def role3 = new RoleInfo(authority: "ROLE_USER", remark: "普通用户").save()//创建用户def user1 = new UserInfo(username: "admin", password: "admin").save()def user2 = new UserInfo(username: "super", password: "super").save()def user3 = new UserInfo(username: "user", password: "user").save()//用户角色关联UserRole.create user1, role1, trueUserRole.create user2, role2, trueUserRole.create user3, role3, true}def destroy = {}
}

最后到这里就完成了,可以启动项目进行测试了。快去创建自己的controller和gsp吧。


http://chatgpt.dhexx.cn/article/XH9HSa0Z.shtml

相关文章

Grails配置-基本配置-如何覆盖默认配置,在哪里进行自定义配置

文章目录 Grails配置基本配置⎮Grails4风格配置⎮Grails2风格的Groovy配置⎮使用GrailsApplication对象访问配置信息 在控制器中使用grailsApplication对象 能获取到的配置信息有哪些 在业务层中使用grailsApplication对象方式一 在业务层中使用grailsApplication对象方式二…

Grails 的插件开发

警告&#xff1a;本文还未整理&#xff0c;可读性差&#xff0c;还只是草稿 文档 Grails Plugin Develop Document grails-spring-security-core 插件文档 grails-spring-security-rest插件 文档 创建插件 执行命令行 grails create-plugin <<PLUGIN NAME>>即…

Grails配置-启动类配置,数据源配置,连接Mysql数据库

文章目录 Grails配置1. 启动类配置Application.groovy启动类中可以做哪些事情1. 启动Grails应用程序2. 自定义包扫描路径3. 注册bean4. 应用程序生命周期管理 2. 环境配置2.1 默认的多环境配置2.2 预设的环境2.3 打包和运行使用不同的环境2.4 在代码启动过程中判断当前环境2.5 …

java grails mave_Grails 与 Maven 集成

Grails 与 Maven 集成 杨 健 2013 年 9 月 09 日发布 概述 Grails 简介&#xff1a;Grails 是一个开源的 Web 开发框架&#xff0c;它采用动态语言 Groovy 进行开发&#xff0c;并且可以和用 Java 开发的项目进行集成。Grails 充分体现了约定优于配置的原则&#xff0c;将项目的…

Grails4.0.11入门

文章目录 Grails4.0.11介绍相关依赖入门⎮Java环境搭建⎮Grails环境搭建⎮如何创建grails应用⎮如何选择开发工具IDEA如何导入Grails项目 ⎮目录结构&#xff0c;约定优于配置⎮如何部署Grails项目1. 使用默认内嵌的Tomcat8部署2. 使用外部Tomtcat8部署3. 使用外部Tomcat7部署 …

grails java_Grails

十二、Grails 如今的Java Web开发对于需求来说已经变得过于复杂。当今众多Java领域的Web开发框架不仅使用复杂&#xff0c;而且并没有很好的遵循Don’t Repeat Yourself(DRY)原则。像Rails&#xff0c;Django和TurboGears这样的动态框架在Web开发领域开辟了一条新的道路&#x…

JAVA AIO编程

Asynchronous IO&#xff1a; 异步非阻塞的编程方式 与NIO不同&#xff0c;当进行读写操作时&#xff0c;只须直接调用API的read或write方法即可。这两种方法均为异步的&#xff0c;对于读操作而言&#xff0c;当有流可读取时&#xff0c;操作系统会将可读的流传入read方法的缓…

java aio_Java AIO详解

JDK1.7升级了NIO类库&#xff0c;升级后的NIO类库被称为NIO 2.0。 Java正式提供了异步文件I/O操作&#xff0c;同时提供了与UNIX网络编程事件驱动I/O对应的AIO。 AIO编程 NIO 2.0引入了新的异步通道的概念&#xff0c;并提供了异步文件通道和异步套接字通道的实现。 异步通道获…

Java I/O 模型之 AIO

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;Java 教程&#xff1a;从入门到精通 大家好&#xff0c;我是水滴~~ 文章目录 服务端API创建服务端套接字的异步通道接…

如何用AIO技术提高程序性能

写在前面 这是一篇关于 AIO 的文章。本篇文章详细对比了几个常见的I/O模型&#xff0c;并且介绍了AIO相关的一些API。 我把英文原文翻译过来整理成这篇文章。目的一个是自己学习&#xff0c;一个是方便不习惯看英文资料的同学进行学习。 英文原文地址&#xff1a; https://…

08【AIO编程】

八、AIO编程 8.1 AIO编程简介 8.1.1 AIO编程概述 AIO也叫异步非阻塞&#xff0c;JDK1.7之后的新特性&#xff0c;AIO引入异步通道的概念&#xff0c;采用了 Proactor 模式&#xff0c;简化了程序编写&#xff0c;有效的请求才启动线程&#xff0c;它的特点是先由操作系统完成…

linux aio进程简介,Linux AIO机制

Linux的I/O机制经历了一下几个阶段的演进&#xff1a; 1. 同步阻塞I/O: 用户进程进行I/O操作&#xff0c;一直阻塞到I/O操作完成为止。 2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK&#xff0c;I/O操作可以立即返回&#xff0c;但是并不保证I/O操作成功…

关于aio

AIO 是异步非阻塞 同步意味着&#xff0c;在进行读写操作时&#xff0c;线程需要等待结果&#xff0c;还是相当于闲置 异步意味着&#xff0c;在进行读写操作时&#xff0c;线程不必等待结果&#xff0c;而是将来由操作系统来通过回调方式由另外的线程来获得结果 AIO的read方…

linux AIO

AIO 是 Linux 下的异步读写模型&#xff0c;它是 2.6 内核提供的一个标准增强特性。对于文件的读写&#xff0c;即使以 O_NONBLOCK 方式来打开一个文件&#xff0c;也会处于 “阻塞” 状态&#xff0c;因为文件时时刻刻处于可读状态&#xff0c;而从磁盘到内存所等待的时间是惊…

AIO模型

目录 AIO模型介绍 AsynchronousServerSocketChannel&#xff1a;AIO中网络通信服务端的socket 1、future方法 2、callback回调方式 AIO 的回调方式编程 BIO、NIO、AIO的比较 1、释义 BIO&#xff1a;同步阻塞IO模型 NIO&#xff1a;同步非阻塞IO模型 AIO&#xff1a;…

java中IO模型-AIO模型

AIO模型介绍 AIO&#xff08;Asynchronous I/O&#xff09; 异步非阻塞模型&#xff0c; 在javajdk.17版本开始支持AIO&#xff0c;AIO模型需要操作系统的支持。 AIO最大的特性是异步能力&#xff0c;对socket和I/O起作用。 异步IO模型类似的 与NIO模型不同&#xff0c;读写操…

架构解密从分布式到微服务:深入理解网络,AIO

AIO AIO是I/O模型里一个很高的层次&#xff0c;体现了大道至简的软件美学理念。与NIO相比&#xff0c;AIO的框架和使用方法相对简单很多。 AIO包括两大部分:AIO Files解决了文件的异步处理问题&#xff0c;AIO Sockets解决了Socket的异步处理问题。AIO的核心概念为应用发起非…

BIO,NIO,AIO区别

BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候&#xff0c;不需要关心操作系统层面的知识&#xff0c;也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。 在讲 BIO,NIO,…

Apache CXF - 快速指南

Apache CXF - 简介 在当今的环境中&#xff0c;您可以使用多个选项来创建 Web 服务应用程序。您可以使用多种标准和广泛接受的协议中的一种或多种进行通信。例如SOAP、XML/HTTP、RESTful HTTP和CORBA&#xff08;通用对象请求代理架构&#xff0c;在过去非常流行&#xff0c;但…

java cxf 安全_CXF client在并发下的线程安全问题

这个是发生在上周周末的真实案例&#xff0c;因为cxf client 端线程安全导致的错误&#xff0c;总结出来希望其他使用cxf的兄弟注意。 首先描述一下背景&#xff0c;简单的说就是使用cxf作为web service的客户端&#xff0c;运行在weblogic上&#xff0c;连接外部的服务器。为了…