亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring?Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制

 更新時(shí)間:2022年06月20日 10:22:34   作者:MacroZheng  
這篇文章主要為大家介紹了Spring?Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

SpringBoot實(shí)戰(zhàn)電商項(xiàng)目mall(30k+star)地址:https://github.com/macrozheng/mall

摘要

權(quán)限控管理作為后臺(tái)管理系統(tǒng)中必要的功能,mall項(xiàng)目中結(jié)合Spring Security實(shí)現(xiàn)了基于路徑的動(dòng)態(tài)權(quán)限控制,可以對(duì)后臺(tái)接口訪問進(jìn)行細(xì)粒度的控制,今天我們來講下它的后端實(shí)現(xiàn)原理。

前置知識(shí)

學(xué)習(xí)本文需要一些Spring Security的知識(shí),對(duì)Spring Security不太了解的朋友可以看下以下文章。

數(shù)據(jù)庫(kù)設(shè)計(jì)

權(quán)限管理相關(guān)表已經(jīng)重新設(shè)計(jì),將原來的權(quán)限拆分成了菜單和資源,菜單管理用于控制前端菜單的顯示和隱藏,資源管理用來控制后端接口的訪問權(quán)限。

數(shù)據(jù)庫(kù)表結(jié)構(gòu)

其中ums_admin、ums_role、ums_admin_role_relation為原來的表,其他均為新增表。

數(shù)據(jù)庫(kù)表介紹

接下來我們將對(duì)每張表的用途做個(gè)詳細(xì)介紹。

ums_admin

后臺(tái)用戶表,定義了后臺(tái)用戶的一些基本信息。

create table ums_admin
(
   id                   bigint not null auto_increment,
   username             varchar(64) comment '用戶名',
   password             varchar(64) comment '密碼',
   icon                 varchar(500) comment '頭像',
   email                varchar(100) comment '郵箱',
   nick_name            varchar(200) comment '昵稱',
   note                 varchar(500) comment '備注信息',
   create_time          datetime comment '創(chuàng)建時(shí)間',
   login_time           datetime comment '最后登錄時(shí)間',
   status               int(1) default 1 comment '帳號(hào)啟用狀態(tài):0->禁用;1->啟用',
   primary key (id)
);

ums_role

后臺(tái)用戶角色表,定義了后臺(tái)用戶角色的一些基本信息,通過給后臺(tái)用戶分配角色來實(shí)現(xiàn)菜單和資源的分配。

create table ums_role
(
   id                   bigint not null auto_increment,
   name                 varchar(100) comment '名稱',
   description          varchar(500) comment '描述',
   admin_count          int comment '后臺(tái)用戶數(shù)量',
   create_time          datetime comment '創(chuàng)建時(shí)間',
   status               int(1) default 1 comment '啟用狀態(tài):0->禁用;1->啟用',
   sort                 int default 0,
   primary key (id)
);

ums_admin_role_relation

后臺(tái)用戶和角色關(guān)系表,多對(duì)多關(guān)系表,一個(gè)角色可以分配給多個(gè)用戶。

create table ums_admin_role_relation
(
   id                   bigint not null auto_increment,
   admin_id             bigint,
   role_id              bigint,
   primary key (id)
);

ums_menu

后臺(tái)菜單表,用于控制后臺(tái)用戶可以訪問的菜單,支持隱藏、排序和更改名稱、圖標(biāo)。

create table ums_menu
(
   id                   bigint not null auto_increment,
   parent_id            bigint comment '父級(jí)ID',
   create_time          datetime comment '創(chuàng)建時(shí)間',
   title                varchar(100) comment '菜單名稱',
   level                int(4) comment '菜單級(jí)數(shù)',
   sort                 int(4) comment '菜單排序',
   name                 varchar(100) comment '前端名稱',
   icon                 varchar(200) comment '前端圖標(biāo)',
   hidden               int(1) comment '前端隱藏',
   primary key (id)
);

ums_resource

后臺(tái)資源表,用于控制后臺(tái)用戶可以訪問的接口,使用了Ant路徑的匹配規(guī)則,可以使用通配符定義一系列接口的權(quán)限。

create table ums_resource
(
   id                   bigint not null auto_increment,
   category_id          bigint comment '資源分類ID',
   create_time          datetime comment '創(chuàng)建時(shí)間',
   name                 varchar(200) comment '資源名稱',
   url                  varchar(200) comment '資源URL',
   description          varchar(500) comment '描述',
   primary key (id)
);

ums_resource_category

后臺(tái)資源分類表,在細(xì)粒度進(jìn)行權(quán)限控制時(shí),可能資源會(huì)比較多,所以設(shè)計(jì)了個(gè)資源分類的概念,便于給角色分配資源。

create table ums_resource_category
(
   id                   bigint not null auto_increment,
   create_time          datetime comment '創(chuàng)建時(shí)間',
   name                 varchar(200) comment '分類名稱',
   sort                 int(4) comment '排序',
   primary key (id)
);

ums_role_menu_relation

后臺(tái)角色菜單關(guān)系表,多對(duì)多關(guān)系,可以給一個(gè)角色分配多個(gè)菜單。

create table ums_role_menu_relation
(
   id                   bigint not null auto_increment,
   role_id              bigint comment '角色I(xiàn)D',
   menu_id              bigint comment '菜單ID',
   primary key (id)
);

ums_role_resource_relation

后臺(tái)角色資源關(guān)系表,多對(duì)多關(guān)系,可以給一個(gè)角色分配多個(gè)資源。

create table ums_role_resource_relation
(
   id                   bigint not null auto_increment,
   role_id              bigint comment '角色I(xiàn)D',
   resource_id          bigint comment '資源ID',
   primary key (id)
);

結(jié)合Spring Security實(shí)現(xiàn)

實(shí)現(xiàn)動(dòng)態(tài)權(quán)限是在原mall-security模塊的基礎(chǔ)上進(jìn)行改造完成的,原實(shí)現(xiàn)有不清楚的可以自行參照前置知識(shí)中的文檔來學(xué)習(xí)。

以前的權(quán)限控制

以前的權(quán)限控制是采用Spring Security的默認(rèn)機(jī)制實(shí)現(xiàn)的,下面我們以商品模塊的代碼為例來講講實(shí)現(xiàn)原理。

首先我們?cè)谛枰獧?quán)限的接口上使用@PreAuthorize注解定義好需要的權(quán)限;

/**
 * 商品管理Controller
 * Created by macro on 2018/4/26.
 */
@Controller
@Api(tags = "PmsProductController", description = "商品管理")
@RequestMapping("/product")
public class PmsProductController {
    @Autowired
    private PmsProductService productService;
    @ApiOperation("創(chuàng)建商品")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("hasAuthority('pms:product:create')")
    public CommonResult create(@RequestBody PmsProductParam productParam, BindingResult bindingResult) {
        int count = productService.create(productParam);
        if (count > 0) {
            return CommonResult.success(count);
        } else {
            return CommonResult.failed();
        }
    }
}

然后將該權(quán)限值存入到權(quán)限表中,當(dāng)用戶登錄時(shí),將其所擁有的權(quán)限查詢出來;

/**
 * UmsAdminService實(shí)現(xiàn)類
 * Created by macro on 2018/4/26.
 */
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
    @Override
    public UserDetails loadUserByUsername(String username){
        //獲取用戶信息
        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            List<UmsPermission> permissionList = getPermissionList(admin.getId());
            return new AdminUserDetails(admin,permissionList);
        }
        throw new UsernameNotFoundException("用戶名或密碼錯(cuò)誤");
    }
}

之后Spring Security把用戶擁有的權(quán)限值和接口上注解定義的權(quán)限值進(jìn)行比對(duì),如果包含則可以訪問,反之就不可以訪問;

但是這樣做會(huì)帶來一些問題,我們需要在每個(gè)接口上都定義好訪問該接口的權(quán)限值,而且只能挨個(gè)控制接口的權(quán)限,無法批量控制。其實(shí)每個(gè)接口都可以由它的訪問路徑唯一確定,我們可以使用基于路徑的動(dòng)態(tài)權(quán)限控制來解決這些問題。

基于路徑的動(dòng)態(tài)權(quán)限控制

接下來我們?cè)敿?xì)介紹下如何使用Spring Security實(shí)現(xiàn)基于路徑的動(dòng)態(tài)權(quán)限。

首先我們需要?jiǎng)?chuàng)建一個(gè)過濾器,用于實(shí)現(xiàn)動(dòng)態(tài)權(quán)限控制,這里需要注意的是doFilter方法,對(duì)于OPTIONS請(qǐng)求直接放行,否則前端調(diào)用會(huì)出現(xiàn)跨域問題。對(duì)于配置在IgnoreUrlsConfig中的白名單路徑我也需要直接放行,所有的鑒權(quán)操作都會(huì)在super.beforeInvocation(fi)中進(jìn)行。

/**
 * 動(dòng)態(tài)權(quán)限過濾器,用于實(shí)現(xiàn)基于路徑的動(dòng)態(tài)權(quán)限過濾
 * Created by macro on 2020/2/7.
 */
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
        super.setAccessDecisionManager(dynamicAccessDecisionManager);
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
        //OPTIONS請(qǐng)求直接放行
        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            return;
        }
        //白名單請(qǐng)求直接放行
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String path : ignoreUrlsConfig.getUrls()) {
            if(pathMatcher.match(path,request.getRequestURI())){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }
        }
        //此處會(huì)調(diào)用AccessDecisionManager中的decide方法進(jìn)行鑒權(quán)操作
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }
    @Override
    public void destroy() {
    }
    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }
    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return dynamicSecurityMetadataSource;
    }
}

在DynamicSecurityFilter中調(diào)用super.beforeInvocation(fi)方法時(shí)會(huì)調(diào)用AccessDecisionManager中的decide方法用于鑒權(quán)操作,而decide方法中的configAttributes參數(shù)會(huì)通過SecurityMetadataSource中的getAttributes方法來獲取,configAttributes其實(shí)就是配置好的訪問當(dāng)前接口所需要的權(quán)限,下面是簡(jiǎn)化版的beforeInvocation源碼。

public abstract class AbstractSecurityInterceptor implements InitializingBean,
		ApplicationEventPublisherAware, MessageSourceAware {
protected InterceptorStatusToken beforeInvocation(Object object) {
        //獲取元數(shù)據(jù)
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);
		Authentication authenticated = authenticateIfRequired();
		//進(jìn)行鑒權(quán)操作
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));
			throw accessDeniedException;
		}
	}
}

知道了鑒權(quán)的原理,接下來我們需要自己實(shí)現(xiàn)SecurityMetadataSource接口的getAttributes方法,用于獲取當(dāng)前訪問路徑所需資源。

/**
 * 動(dòng)態(tài)權(quán)限數(shù)據(jù)源,用于獲取動(dòng)態(tài)權(quán)限規(guī)則
 * Created by macro on 2020/2/7.
 */
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    private static Map<String, ConfigAttribute> configAttributeMap = null;
    @Autowired
    private DynamicSecurityService dynamicSecurityService;
    @PostConstruct
    public void loadDataSource() {
        configAttributeMap = dynamicSecurityService.loadDataSource();
    }
    public void clearDataSource() {
        configAttributeMap.clear();
        configAttributeMap = null;
    }
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        if (configAttributeMap == null) this.loadDataSource();
        List<ConfigAttribute>  configAttributes = new ArrayList<>();
        //獲取當(dāng)前訪問的路徑
        String url = ((FilterInvocation) o).getRequestUrl();
        String path = URLUtil.getPath(url);
        PathMatcher pathMatcher = new AntPathMatcher();
        Iterator<String> iterator = configAttributeMap.keySet().iterator();
        //獲取訪問該路徑所需資源
        while (iterator.hasNext()) {
            String pattern = iterator.next();
            if (pathMatcher.match(pattern, path)) {
                configAttributes.add(configAttributeMap.get(pattern));
            }
        }
        // 未設(shè)置操作請(qǐng)求權(quán)限,返回空集合
        return configAttributes;
    }
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

由于我們的后臺(tái)資源規(guī)則被緩存在了一個(gè)Map對(duì)象之中,所以當(dāng)后臺(tái)資源發(fā)生變化時(shí),我們需要清空緩存的數(shù)據(jù),然后下次查詢時(shí)就會(huì)被重新加載進(jìn)來。

這里我們需要修改UmsResourceController類,注入DynamicSecurityMetadataSource,當(dāng)修改后臺(tái)資源時(shí),需要調(diào)用clearDataSource方法來清空緩存的數(shù)據(jù)。

/**
 * 后臺(tái)資源管理Controller
 * Created by macro on 2020/2/4.
 */
@Controller
@Api(tags = "UmsResourceController", description = "后臺(tái)資源管理")
@RequestMapping("/resource")
public class UmsResourceController {
    @Autowired
    private UmsResourceService resourceService;
    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
    @ApiOperation("添加后臺(tái)資源")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult create(@RequestBody UmsResource umsResource) {
        int count = resourceService.create(umsResource);
        dynamicSecurityMetadataSource.clearDataSource();
        if (count &gt; 0) {
            return CommonResult.success(count);
        } else {
            return CommonResult.failed();
        }
    }
 }

之后我們需要實(shí)現(xiàn)AccessDecisionManager接口來實(shí)現(xiàn)權(quán)限校驗(yàn),對(duì)于沒有配置資源的接口我們直接允許訪問,對(duì)于配置了資源的接口,我們把訪問所需資源和用戶擁有的資源進(jìn)行比對(duì),如果匹配則允許訪問。

/**
 * 動(dòng)態(tài)權(quán)限決策管理器,用于判斷用戶是否有訪問權(quán)限
 * Created by macro on 2020/2/7.
 */
public class DynamicAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 當(dāng)接口未被配置資源時(shí)直接放行
        if (CollUtil.isEmpty(configAttributes)) {
            return;
        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //將訪問所需資源或用戶擁有資源進(jìn)行比對(duì)
            String needAuthority = configAttribute.getAttribute();
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("抱歉,您沒有訪問權(quán)限");
    }
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

我們之前在DynamicSecurityMetadataSource中注入了一個(gè)DynamicSecurityService對(duì)象,它是我自定義的一個(gè)動(dòng)態(tài)權(quán)限業(yè)務(wù)接口,其主要用于加載所有的后臺(tái)資源規(guī)則。

/**
 * 動(dòng)態(tài)權(quán)限相關(guān)業(yè)務(wù)類
 * Created by macro on 2020/2/7.
 */
public interface DynamicSecurityService {
    /**
     * 加載資源ANT通配符和資源對(duì)應(yīng)MAP
     */
    Map<String, ConfigAttribute> loadDataSource();
}

接下來我們需要修改Spring Security的配置類SecurityConfig,當(dāng)有動(dòng)態(tài)權(quán)限業(yè)務(wù)類時(shí)在FilterSecurityInterceptor過濾器前添加我們的動(dòng)態(tài)權(quán)限過濾器。

這里在創(chuàng)建動(dòng)態(tài)權(quán)限相關(guān)對(duì)象時(shí),還使用了@ConditionalOnBean這個(gè)注解,當(dāng)沒有動(dòng)態(tài)權(quán)限業(yè)務(wù)類時(shí)就不會(huì)創(chuàng)建動(dòng)態(tài)權(quán)限相關(guān)對(duì)象,實(shí)現(xiàn)了有動(dòng)態(tài)權(quán)限控制和沒有這兩種情況的兼容。

/**
 * 對(duì)SpringSecurity的配置的擴(kuò)展,支持自定義白名單資源路徑和查詢用戶邏輯
 * Created by macro on 2019/11/5.
 */
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired(required = false)
    private DynamicSecurityService dynamicSecurityService;
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //有動(dòng)態(tài)權(quán)限配置時(shí)添加動(dòng)態(tài)權(quán)限校驗(yàn)過濾器
        if(dynamicSecurityService!=null){
            registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);
        }
    }
    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
        return new DynamicAccessDecisionManager();
    }
    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicSecurityFilter dynamicSecurityFilter() {
        return new DynamicSecurityFilter();
    }
    @ConditionalOnBean(name = "dynamicSecurityService")
    @Bean
    public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() {
        return new DynamicSecurityMetadataSource();
    }
}

這里還有個(gè)問題需要提下,當(dāng)前端跨域訪問沒有權(quán)限的接口時(shí),會(huì)出現(xiàn)跨域問題,只需要在沒有權(quán)限訪問的處理類RestfulAccessDeniedHandler中添加允許跨域訪問的響應(yīng)頭即可。

/**
 * 自定義返回結(jié)果:沒有權(quán)限訪問時(shí)
 * Created by macro on 2018/4/26.
 */
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       AccessDeniedException e) throws IOException, ServletException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Cache-Control","no-cache");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
        response.getWriter().flush();
    }
}

當(dāng)我們其他模塊需要?jiǎng)討B(tài)權(quán)限控制時(shí),只要?jiǎng)?chuàng)建一個(gè)DynamicSecurityService對(duì)象就行了,比如在mall-admin模塊中我們啟用了動(dòng)態(tài)權(quán)限功能。

/**
 * mall-security模塊相關(guān)配置
 * Created by macro on 2019/11/9.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MallSecurityConfig extends SecurityConfig {
    @Autowired
    private UmsAdminService adminService;
    @Autowired
    private UmsResourceService resourceService;
    @Bean
    public UserDetailsService userDetailsService() {
        //獲取登錄用戶信息
        return username -&gt; adminService.loadUserByUsername(username);
    }
    @Bean
    public DynamicSecurityService dynamicSecurityService() {
        return new DynamicSecurityService() {
            @Override
            public Map&lt;String, ConfigAttribute&gt; loadDataSource() {
                Map&lt;String, ConfigAttribute&gt; map = new ConcurrentHashMap&lt;&gt;();
                List&lt;UmsResource&gt; resourceList = resourceService.listAll();
                for (UmsResource resource : resourceList) {
                    map.put(resource.getUrl(), new org.springframework.security.access.SecurityConfig(resource.getId() + ":" + resource.getName()));
                }
                return map;
            }
        };
    }
}

權(quán)限管理功能演示

具體參考:大家心心念念的權(quán)限管理功能,這次安排上了!

mall項(xiàng)目更新源碼地址 https://github.com/macrozheng/mall

以上就是Spring Security權(quán)限管理實(shí)現(xiàn)接口動(dòng)態(tài)權(quán)限控制的詳細(xì)內(nèi)容,更多關(guān)于Spring Security接口動(dòng)態(tài)權(quán)限控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring實(shí)現(xiàn)定時(shí)任務(wù)的幾種方式總結(jié)

    Spring實(shí)現(xiàn)定時(shí)任務(wù)的幾種方式總結(jié)

    Spring Task 是 Spring 框架提供的一種任務(wù)調(diào)度和異步處理的解決方案,可以按照約定的時(shí)間自動(dòng)執(zhí)行某個(gè)代碼邏輯它可以幫助開發(fā)者在 Spring 應(yīng)用中輕松地實(shí)現(xiàn)定時(shí)任務(wù)、異步任務(wù)等功能,提高應(yīng)用的效率和可維護(hù)性,需要的朋友可以參考下本文
    2024-07-07
  • SpringBoot整合Mybatis簡(jiǎn)單實(shí)現(xiàn)增刪改查

    SpringBoot整合Mybatis簡(jiǎn)單實(shí)現(xiàn)增刪改查

    這篇文章主要介紹了SpringBoot整合Mybatis簡(jiǎn)單實(shí)現(xiàn)增刪改查,文章為圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • MyBatis?實(shí)現(xiàn)多對(duì)多中間表插入數(shù)據(jù)

    MyBatis?實(shí)現(xiàn)多對(duì)多中間表插入數(shù)據(jù)

    這篇文章主要介紹了MyBatis?實(shí)現(xiàn)多對(duì)多中間表插入數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Spring框架七大模塊簡(jiǎn)單介紹

    Spring框架七大模塊簡(jiǎn)單介紹

    這篇文章主要介紹了Spring框架七大模塊簡(jiǎn)單介紹,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Spring boot JPA實(shí)現(xiàn)分頁和枚舉轉(zhuǎn)換代碼示例

    Spring boot JPA實(shí)現(xiàn)分頁和枚舉轉(zhuǎn)換代碼示例

    這篇文章主要介紹了Spring boot JPA實(shí)現(xiàn)分頁和枚舉轉(zhuǎn)換代碼示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 一分鐘掌握J(rèn)ava?ElasticJob分布式定時(shí)任務(wù)

    一分鐘掌握J(rèn)ava?ElasticJob分布式定時(shí)任務(wù)

    ElasticJob?是面向互聯(lián)網(wǎng)生態(tài)和海量任務(wù)的分布式調(diào)度解決方案,本文主要通過簡(jiǎn)單的示例帶大家深入了解ElasticJob分布式定時(shí)任務(wù)的相關(guān)知識(shí),需要的可以參考一下
    2023-05-05
  • mybatis注解之@Mapper和@MapperScan的使用

    mybatis注解之@Mapper和@MapperScan的使用

    這篇文章主要介紹了mybatis注解之@Mapper和@MapperScan的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 深入探究HashMap二次Hash原因

    深入探究HashMap二次Hash原因

    在java開發(fā)中,HashMap是最常用、最常見的集合容器類之一,文中通過示例代碼介紹HashMap為啥要二次Hash,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Quarkus集成open api接口使用swagger ui展示

    Quarkus集成open api接口使用swagger ui展示

    這篇文章主要為大家介紹了Quarkus集成open?api接口使用swagger?ui的展示示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • java實(shí)現(xiàn)簡(jiǎn)單快遞系統(tǒng)

    java實(shí)現(xiàn)簡(jiǎn)單快遞系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單快遞系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論