shiro 整合 spring 实战及源码详解

这篇具有很好参考价值的文章主要介绍了shiro 整合 spring 实战及源码详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序言

前面我们学习了如下内容:

5 分钟入门 shiro 安全框架实战笔记

shiro 整合 spring 实战及源码详解

相信大家对于 shiro 已经有了最基本的认识,这一节我们一起来学习写如何将 shiro 与 spring 进行整合。

spring 整合

maven 依赖

<dependencies>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.13.RELEASE</version>
    </dependency>
</dependencies>

服务类定义

定义一个简单的服务类,用于演示 @RequiresPermissions 注解的权限校验。

package com.github.houbb.shiro.inaction02.springalone;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * Simple Service with methods protected with annotations.
 */
@Component
public class SimpleService {

    private static Logger log = LoggerFactory.getLogger(SimpleService.class);

    @RequiresPermissions("write")
    public void writeRestrictedCall() {
        log.info("executing method that requires the 'write' permission");
    }

    @RequiresPermissions("read")
    public void readRestrictedCall() {
        log.info("executing method that requires the 'read' permission");
    }
}

快速开始

我们对原来的 Quick Start 进行改造如下:

package com.github.houbb.shiro.inaction02.springalone;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * Simple Bean used to demonstrate subject usage.
 */
@Component
public class QuickStart {

    private static Logger log = LoggerFactory.getLogger(QuickStart.class);

    @Autowired
    private SecurityManager securityManager;

    @Autowired
    private SimpleService simpleService;

    /**
     * Sets the static instance of SecurityManager. This is NOT needed for web applications.
     */
    @PostConstruct
    private void initStaticSecurityManager() {
        SecurityUtils.setSecurityManager(securityManager);
    }

    public void run() {
        // get the current subject
        Subject subject = SecurityUtils.getSubject();

        // Subject is not authenticated yet
        System.out.println(!subject.isAuthenticated());

        // login the subject with a username / password
        UsernamePasswordToken token = new UsernamePasswordToken("joe.coder", "password");
        subject.login(token);

        // joe.coder has the "user" role
        subject.checkRole("user");

        // joe.coder does NOT have the admin role
        System.out.println(!subject.hasRole("admin"));

        // joe.coder has the "read" permission
        subject.checkPermission("read");

        // current user is allowed to execute this method.
        simpleService.readRestrictedCall();

        try {
            // but not this one!
            simpleService.writeRestrictedCall();
        }
        catch (AuthorizationException e) {
            log.info("Subject was NOT allowed to execute method 'writeRestrictedCall'");
        }

        // logout
        subject.logout();
        System.out.println(!subject.isAuthenticated());
    }

}

这里最核心的区别是 SecurityManager 是直接通过 @Autowired 注入得到的。

也没有看到我们以前初始化 SecurityManager 的 ini 文件,这些在下面的配置文件中。

配置类

package com.github.houbb.shiro.inaction02.springalone;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration;
import org.apache.shiro.spring.config.ShiroBeanConfiguration;
import org.apache.shiro.spring.config.ShiroConfiguration;
import org.springframework.context.annotation.*;

/**
 * Application bean definitions.
 */
@Configuration
@Import({ShiroBeanConfiguration.class,
         ShiroConfiguration.class,
         ShiroAnnotationProcessorConfiguration.class})
@ComponentScan("com.github.houbb.shiro.inaction02.springalone")
public class CliApp {

    /**
     * Example hard coded Realm bean.
     * @return hard coded Realm bean
     */
    @Bean
    public Realm realm() {
        TextConfigurationRealm realm = new TextConfigurationRealm();
        realm.setUserDefinitions("joe.coder=password,user\n" +
                                 "jill.coder=password,admin");

        realm.setRoleDefinitions("admin=read,write\n" +
                                 "user=read");
        realm.setCachingEnabled(true);
        return realm;
    }

}

这里通过 @Bean 的方式声明了用户角色等信息,可以简单理解为和 Ini 文件初始化是等价的。

@Import 导入了 3 个配置类,我们后面进行介绍。

启动

spring 应用的启动:

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CliApp.class);
    context.getBean(QuickStart.class).run();
}

测试日志如下:

十二月 31, 2020 10:33:02 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6267c3bb: startup date [Thu Dec 31 10:33:02 CST 2020]; root of context hierarchy
十二月 31, 2020 10:33:03 上午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'org.apache.shiro.spring.config.ShiroBeanConfiguration' of type [org.apache.shiro.spring.config.ShiroBeanConfiguration$$EnhancerBySpringCGLIB$$fbe016b3] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
...
信息: Bean 'org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration' of type [org.apache.shiro.spring.config.ShiroAnnotationProcessorConfiguration$$EnhancerBySpringCGLIB$$f9d46e86] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
十二月 31, 2020 10:33:03 上午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'eventBus' of type [org.apache.shiro.event.support.DefaultEventBus] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
十二月 31, 2020 10:33:03 上午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'org.apache.shiro.spring.config.ShiroConfiguration' of type [org.apache.shiro.spring.config.ShiroConfiguration$$EnhancerBySpringCGLIB$$3db21503] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
...
true
true
true

ShiroBeanConfiguration 配置类

@Import 共计导入了 3 个配置类,我们接下来逐一分析下这 3 个配置类。

源码

@Configuration
public class ShiroBeanConfiguration extends AbstractShiroBeanConfiguration {

    @Bean
    @Override
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return super.lifecycleBeanPostProcessor();
    }

    @Bean
    @Override
    protected EventBus eventBus() {
        return super.eventBus();
    }

    @Bean
    @Override
    public ShiroEventBusBeanPostProcessor shiroEventBusAwareBeanPostProcessor() {
        return super.shiroEventBusAwareBeanPostProcessor();
    }
}

这 3 个方法都是继承自父类,直接调用的父类方法。

  • AbstractShiroBeanConfiguration.java
public class AbstractShiroBeanConfiguration {

    protected LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    protected EventBus eventBus() {
        return new DefaultEventBus();
    }

    protected ShiroEventBusBeanPostProcessor shiroEventBusAwareBeanPostProcessor() {
        return new ShiroEventBusBeanPostProcessor(eventBus());
    }
}

实际上这里初始化了 3 个对象:LifecycleBeanPostProcessor/DefaultEventBus/ShiroEventBusBeanPostProcessor。

LifecycleBeanPostProcessor

这个类实际上比较简单,主要做了 2 件事情。

(1)执行 init() 和 destory()。

(2)指定对应的优先级,默认为最低。

核心部分如下:

  • init 初始化
public Object postProcessBeforeInitialization(Object object, String name) throws BeansException {
    if (object instanceof Initializable) {
        try {
            if (log.isDebugEnabled()) {
                log.debug("Initializing bean [" + name + "]...");
            }
            ((Initializable) object).init();
        } catch (Exception e) {
            throw new FatalBeanException("Error initializing bean [" + name + "]", e);
        }
    }
    return object;
}
  • destory 销毁
public void postProcessBeforeDestruction(Object object, String name) throws BeansException {
    if (object instanceof Destroyable) {
        try {
            if (log.isDebugEnabled()) {
                log.debug("Destroying bean [" + name + "]...");
            }
            ((Destroyable) object).destroy();
        } catch (Exception e) {
            throw new FatalBeanException("Error destroying bean [" + name + "]", e);
        }
    }
}

DefaultEventBus

这个类如其名,就是默认的事件总线类。

接口的如下:

public interface EventBus {
    void publish(Object var1);

    void register(Object var1);

    void unregister(Object var1);
}

分别对应的是事件的发布,注册和取消注册。

实现部分实际就是调用对应的 EventListener 类,并且通过读写锁保证并发安全,暂时不做展开。

ShiroEventBusBeanPostProcessor

这个类实际上是配合 EventBus 使用的,核心实现如下:

public class ShiroEventBusBeanPostProcessor implements BeanPostProcessor {

    final private EventBus eventBus;

    public ShiroEventBusBeanPostProcessor(EventBus eventBus) {
        this.eventBus = eventBus;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //1. 如果实现了 EventBusAware 接口
        if (bean instanceof EventBusAware) {
            ((EventBusAware) bean).setEventBus(eventBus);
        }
        //2. 如果有 Subscribe 注解信息
        else if (isEventSubscriber(bean)) {
            eventBus.register(bean);
        }

        return bean;
    }

}

这里会把实现了 EventBusAware 接口,和指定了 @Subscribe 注解的对象,注解对应的 eventbus。

ShiroConfiguration 配置

思考

我们 QuickStart 中自动注入了 SecurityManager 对象,这个对象是在哪里初始化的呢?

核心源码

核心部分如下:

@Configuration
@Import({ShiroBeanConfiguration.class})
public class ShiroConfiguration extends AbstractShiroConfiguration {

    @Bean
    @Override
    protected SessionsSecurityManager securityManager(List<Realm> realms) {
        return super.securityManager(realms);
    }

    @Bean
    @Override
    protected SessionManager sessionManager() {
        return super.sessionManager();
    }

    //... 省略其他组件
}

这里可以发现实际上已经导入了 ShiroBeanConfiguration 配置类,所以官方的 demo 可以简化如下:

@Configuration
@Import({ShiroConfiguration.class,
         ShiroAnnotationProcessorConfiguration.class})
@ComponentScan("com.github.houbb.shiro.inaction02.springalone")
public class CliApp{}

实际测试了一下,也是通过的。

SecurityManager 初始化

我简单的看了下 SecurityManager 实现子类还是比较多得。断点可以发现默认的类型是 DefaultSecurityManager

这些都可以在 AbstractShiroConfiguration 类中找到答案。

  • AbstractShiroConfiguration.java

核心实现如下:

public class AbstractShiroConfiguration {

    @Autowired
    protected EventBus eventBus;

    protected SessionsSecurityManager securityManager(List<Realm> realms) {
        SessionsSecurityManager securityManager = createSecurityManager();
        securityManager.setEventBus(eventBus);
        
        // 省略其他属性设置
        return securityManager;
    }

    protected SessionsSecurityManager createSecurityManager() {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        securityManager.setSubjectDAO(subjectDAO());
        securityManager.setSubjectFactory(subjectFactory());

        RememberMeManager rememberMeManager = rememberMeManager();
        if (rememberMeManager != null) {
            securityManager.setRememberMeManager(rememberMeManager);
        }

        return securityManager;
    }

}

securityManager(List<Realm> realms) 方法会把我们 CliApp 中定义的 Realm 对象当作参数传入。

createSecurityManager() 方法就会初始化 DefaultSecurityManager 对象。

ShiroAnnotationProcessorConfiguration 配置

思考

我们在 SampleService 中使用了注解 @RequiresPermissions("write"),就可以校验对应的权限了。

但是这一切是如何被自动实现的呢?

源码

@Configuration
public class ShiroAnnotationProcessorConfiguration extends AbstractShiroAnnotationProcessorConfiguration{

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    protected DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return super.defaultAdvisorAutoProxyCreator();
    }

    @Bean
    protected AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        return super.authorizationAttributeSourceAdvisor(securityManager);
    }

}

本身没有什么源码,主要看下父类。

AbstractShiroAnnotationProcessorConfiguration

public class AbstractShiroAnnotationProcessorConfiguration {

    protected DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }

    protected AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

DefaultAdvisorAutoProxyCreator 是 spring 中的自动代理实现类,此处不做展开。

我们重点看一下 AuthorizationAttributeSourceAdvisor 对象:

AuthorizationAttributeSourceAdvisor

这里主要做了两件事:

(1)设置对应的 securityManager

(2)处理有 RequiresPermissions 等 shiro 的内置注解的方法。

@SuppressWarnings({"unchecked"})
public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {

    private static final Logger log = LoggerFactory.getLogger(AuthorizationAttributeSourceAdvisor.class);

    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
            };

    protected SecurityManager securityManager = null;

    /**
     * Create a new AuthorizationAttributeSourceAdvisor.
     */
    public AuthorizationAttributeSourceAdvisor() {
        setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
    }

    public SecurityManager getSecurityManager() {
        return securityManager;
    }

    public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

}

AopAllianceAnnotationsAuthorizingMethodInterceptor

这个名字起的,好家伙,真长。

这里就是对于注解的响应方法 aop 拦截器实现。

package org.apache.shiro.spring.security.interceptor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.shiro.aop.AnnotationResolver;
import org.apache.shiro.authz.aop.*;
import org.apache.shiro.spring.aop.SpringAnnotationResolver;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Allows Shiro Annotations to work in any <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a>
 * specific implementation environment (for example, Spring).
 *
 * @since 0.2
 */
public class AopAllianceAnnotationsAuthorizingMethodInterceptor
        extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor {

    public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
        List<AuthorizingAnnotationMethodInterceptor> interceptors =
                new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);

        //use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
        //raw JDK resolution process.
        AnnotationResolver resolver = new SpringAnnotationResolver();
        //we can re-use the same resolver instance - it does not retain state:
        interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
        interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
        interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
        interceptors.add(new UserAnnotationMethodInterceptor(resolver));
        interceptors.add(new GuestAnnotationMethodInterceptor(resolver));

        setMethodInterceptors(interceptors);
    }
    
    //省略 invoke 部分
}

到这里实际上就比较简单了,相信聪明如你一定已经知道整个 spring-shiro 面纱背后的秘密了。

我们直接看一下 PermissionAnnotationMethodInterceptor 的实现。

PermissionAnnotationMethodInterceptor

import org.apache.shiro.aop.AnnotationResolver;

public class PermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {

    public PermissionAnnotationMethodInterceptor() {
        super( new PermissionAnnotationHandler() );
    }

    /**
     * @param resolver
     * @since 1.1
     */
    public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
        super( new PermissionAnnotationHandler(), resolver);
    }
}

处理类实现如下:

package org.apache.shiro.authz.aop;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;

import java.lang.annotation.Annotation;


public class PermissionAnnotationHandler extends AuthorizingAnnotationHandler {

    public PermissionAnnotationHandler() {
        super(RequiresPermissions.class);
    }

    
    // 获取对应的注解值
    protected String[] getAnnotationValue(Annotation a) {
        RequiresPermissions rpAnnotation = (RequiresPermissions) a;
        return rpAnnotation.value();
    }

    // 校验当前主题,是否拥有对应的权限。
    public void assertAuthorized(Annotation a) throws AuthorizationException {
        if (!(a instanceof RequiresPermissions)) return;

        RequiresPermissions rpAnnotation = (RequiresPermissions) a;
        String[] perms = getAnnotationValue(a);
        Subject subject = getSubject();

        if (perms.length == 1) {
            subject.checkPermission(perms[0]);
            return;
        }
        if (Logical.AND.equals(rpAnnotation.logical())) {
            getSubject().checkPermissions(perms);
            return;
        }
        if (Logical.OR.equals(rpAnnotation.logical())) {
            // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
            boolean hasAtLeastOnePermission = false;
            for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
            // Cause the exception if none of the role match, note that the exception message will be a bit misleading
            if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);
            
        }
    }
}

小结

这一节我们讲解了如何整合 spring 与 shiro,下一节我们将实战整合 springboot 与 shiro,感兴趣的小伙伴可以关注一波不迷路。

为了便于大家学习,所有源码都已开源:

https://gitee.com/houbinbin/shiro-inaction/tree/master/shiro-inaction-02-springalone

希望本文对你有所帮助,如果喜欢,欢迎点赞收藏转发一波。

我是老马,期待与你的下次相遇。

参考资料

10 Minute Tutorial on Apache Shiro

https://shiro.apache.org/reference.html

https://shiro.apache.org/session-management.html

本文由博客一文多发平台 OpenWrite 发布!文章来源地址https://www.toymoban.com/news/detail-836765.html

到了这里,关于shiro 整合 spring 实战及源码详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot整合shiro+jwt+redis详解

    三大核心组件:Subject、SecurityManager、Realm Subject 主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManag

    2023年04月09日
    浏览(45)
  • 一文详解RocketMQ-Spring的源码解析与实战

    摘要: 这篇文章主要介绍 Spring Boot 项目使用 rocketmq-spring SDK 实现消息收发的操作流程,同时笔者会从开发者的角度解读 SDK 的设计逻辑。 本文分享自华为云社区《RocketMQ-Spring : 实战与源码解析一网打尽》,作者:勇哥java实战分享。 RocketMQ 是大家耳熟能详的消息队列,开源项

    2023年04月24日
    浏览(36)
  • 【Spring Cloud Alibaba】(三)OpenFeign扩展点实战 + 源码详解

    【Spring Cloud Alibaba】(一)微服务介绍 及 Nacos注册中心实战 【Spring Cloud Alibaba】(二)微服务调用组件Feign原理+实战 书接上文,我们掌握了Feign的基本使用、核心原理,以及Spring Cloud Alibaba如何快速整合Feign,真的太简单了!你是不是觉得这样就够了?但在实际项目使用OpenF

    2024年02月20日
    浏览(41)
  • SSM+Shiro安全框架整合(完成安全认证--登录+权限授权)+ssm整合shiro前后端分离

    目录 1.搭建SSM框架  1.1.引入相关的依赖 1.2. spring配置文件 1.3. web.xml配置文件 1.4.配置Tomcat并启动 2.ssm整合shiro---认证功能  (1).引入依赖 (2).修改spring配置文件 (3).修改web.xml文件 (4).新建login.jsp(登录页面) (5).新建success.jsp(登录成功后跳转到此) (6).创建User实体类 (7).创建LoginVo

    2024年02月15日
    浏览(46)
  • Spring Boot 整合 MongoDB 实战

    MongoDB是一种NoSQL数据库,而Spring Boot是一个用于快速构建Java应用程序的开发框架。本文将介绍如何使用Spring Boot整合MongoDB,实现数据的持久化和操作。通过本文的学习,读者将了解到Spring Boot和MongoDB的基本概念和用途,并理解为什么选择使用它们进行整合。 在开始整合之前,

    2024年02月14日
    浏览(44)
  • SpringBoot与Shiro整合

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。 Authentication 认证 ---- 用户登录 Authorization 授权 ---- 用户具有哪些权限 C

    2024年02月08日
    浏览(33)
  • 详谈 springboot整合shiro

    上文学习了shrio 基本概念后,本章将进一步的落地实践学习,在springboot中如何去整合shrio,整个过程步骤有个清晰的了解。   1. 添加依赖:首先,在 pom.xml 文件中添加Spring Boot和Shiro的相关依赖。 2. 创建Shiro配置类:创建一个 ShiroConfig 类,用于配置Shiro的相关信息和组件。(

    2024年02月12日
    浏览(37)
  • Spring Boot进阶(96):轻松上手:实战Spring Boot整合Docker

      Docker 是一个开源的应用程序容器化工具,它可以将应用程序和依赖组件打包到一个容器中,实现应用程序的快速部署和运行。Spring Boot 是一个快速开发应用程序的框架,使用 Spring Boot 可以快速构建各种各样的应用程序。本文将介绍如何使用 Spring Boot 整合 Docker,实现应用

    2024年02月07日
    浏览(56)
  • spring5源码篇(9)——mybatis-spring整合原理

    spring-framework 版本:v5.3.19 spring和mybatis的整合无非主要就是以下几个方面: 1、SqlSessionFactory怎么注入? 2、Mapper代理怎么注入? 3、为什么要接管mybatis事务? 在mybatis-spring中,定义了一个新的factoryBean——SqlSessionFactoryBean,我们将其注入到spring容器即可。 其实也不难猜到,这

    2024年02月03日
    浏览(37)
  • SSM 整合 Shiro 安全框架【快速入门】

    更改web路径 创建所需目录 属性名与数据库字段名一 一对应 这里只是为了测试项目,还没有进行编码工作 启动tamcat服务 LoginVo

    2024年02月12日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包