# SpringNote
**Repository Path**: yanpingdong/spring-note
## Basic Information
- **Project Name**: SpringNote
- **Description**: Spring使用笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-02-05
- **Last Updated**: 2025-12-13
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# [SpringRestful](SpringRestful/README.md)
如何快速搭建SpringBoot MVC;一些常见的用法总结;
## [controller写法](SpringRestful/README.md#RequestMapping初探)
## [Swagger3](SpringRestful/README.md#Swagger)
## [全局异常/数据处理](SpringRestful/README.md#全局异常/数据处理)
## [入参校验](SpringRestful/README.md#入参校验)
## [跨域问题](SpringRestful/README.md#跨域问题)
# [Log4j2](Log4j2/README.md)
log4j2的集成与使用
# [mybatis](Mybatis/README.md)
mybatis的集成与使用
# [liquibase](Liquibase/README.md)
liquibase的集成与使用
# [cache](Cache/README.md)
业务层缓存的集成与使用
# [Async](Async/README.md)
异步调用的集成与使用
# [Schedule](Schedule/README.md)
定时任务的集成与使用
# [view](View/README.MD)
springboot Thymeleaf模板渲染的集成与使用
# [@Configuration配置作用](Configuration/README.md)
@Configuration用于定义配置类,定义的配置类可以替换xml文件。两者的区别!
# [Conditional系列注解](Conditional/README.md)
@Conditional系列注解的用法与意义
# [spring-boot-starter-xxx](Starter/README.MD)
用到的starter的具体作用
# SpringShiro session获取方式
获取Session的方法定义在SessionManager接口中细节见如下代码
```java
public interface SessionManager {
/**
* Starts a new session based on the specified contextual initialization data, which can be used by the underlying
* implementation to determine how exactly to create the internal Session instance.
*
* This method is mainly used in framework development, as the implementation will often relay the argument
* to an underlying {@link SessionFactory} which could use the context to construct the internal Session
* instance in a specific manner. This allows pluggable {@link org.apache.shiro.session.Session Session} creation
* logic by simply injecting a {@code SessionFactory} into the {@code SessionManager} instance.
*
* @param context the contextual initialization data that can be used by the implementation or underlying
* {@link SessionFactory} when instantiating the internal {@code Session} instance.
* @return the newly created session.
* @see SessionFactory#createSession(SessionContext)
* @since 1.0
*/
Session start(SessionContext context);
/**
* Retrieves the session corresponding to the specified contextual data (such as a session ID if applicable), or
* {@code null} if no Session could be found. If a session is found but invalid (stopped or expired), a
* {@link SessionException} will be thrown.
*
* @param key the Session key to use to look-up the Session
* @return the {@code Session} instance corresponding to the given lookup key or {@code null} if no session
* could be acquired.
* @throws SessionException if a session was found but it was invalid (stopped/expired).
* @since 1.0
*/
Session getSession(SessionKey key) throws SessionException;
}
默认SessionManager
public class ServletContainerSessionManager implements WebSessionManager
```

从左下红框处可以看出shiro获取session的完整流程,如果完成了登录,可以在左下解看到session的属性里有org.apache.shiro.subject.support.DefaultSubjectContext_AUTHENTICATED_SESSION_KEY的值为true这代表该用户已经通过了鉴权操作。
获取session的方法在下例代码的```context = resolveSession(context)```方法中,从方法注释里也能看出来其基本做用。
```java
public class DefaultSecurityManager extends SessionsSecurityManager {
public Subject createSubject(SubjectContext subjectContext) {
//create a copy so we don't modify the argument's backing map:
SubjectContext context = copy(subjectContext);
//ensure that the context has a SecurityManager instance, and if not, add one:
context = ensureSecurityManager(context);
//Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
//sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the
//process is often environment specific - better to shield the SF from these details:
context = resolveSession(context);
//Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
//if possible before handing off to the SubjectFactory:
context = resolvePrincipals(context);
Subject subject = doCreateSubject(context);
//save this subject for future reference if necessary:
//(this is needed here in case rememberMe principals were resolved and they need to be stored in the
//session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
//Added in 1.2:
save(subject);
return subject;
}
}
```
然后会去调用resolveContextSession(context)方法-->getSession(SessionKey key),而在getSession方法中会转去sessionManger获取Session,默认的shiro SessionManager是ServletContainerSessionManager
```java
protected SubjectContext resolveSession(SubjectContext context) {
if (context.resolveSession() != null) {
log.debug("Context already contains a session. Returning.");
return context;
}
try {
//Context couldn't resolve it directly, let's see if we can since we have direct access to
//the session manager:
Session session = resolveContextSession(context);
if (session != null) {
context.setSession(session);
}
} catch (InvalidSessionException e) {
log.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " +
"(session-less) Subject instance.", e);
}
return context;
}
protected Session resolveContextSession(SubjectContext context) throws InvalidSessionException {
SessionKey key = getSessionKey(context);
if (key != null) {
return getSession(key);
}
return null;
}
public Session getSession(SessionKey key) throws SessionException {
return this.sessionManager.getSession(key);
}
```
ServletContainerSessionManager.getSession-->HttpSession httpSession = request.getSession(false);从而拿到Web session信息。
```java
public Session getSession(SessionKey key) throws SessionException {
if (!WebUtils.isHttp(key)) {
String msg = "SessionKey must be an HTTP compatible implementation.";
throw new IllegalArgumentException(msg);
}
HttpServletRequest request = WebUtils.getHttpRequest(key);
Session session = null;
HttpSession httpSession = request.getSession(false);
if (httpSession != null) {
session = createSession(httpSession, request.getRemoteHost());
}
return session;
}
```
以上流程都是默认流程,
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware
DefaultWebSessionManager获取Session的基本流程如下(一般情况下在给DefaultWebSessionManager传入DAO层对像的时候会默认做applyCacheManagerToSessionDAO操作,即给DAO加缓存控制。工作流程见下面基本流程部分,相关代码见如下:
```java
public void setSessionDAO(SessionDAO sessionDAO) {
this.sessionDAO = sessionDAO;
applyCacheManagerToSessionDAO();
}
```
获最Session的调用过程
```java
AbstractNativeSessionManager.getSession(SessionKey key)-->AbstractNativeSessionManager. lookupSession(SessionKey key) --> AbstractValidatingSessionManager.doGetSession(final SessionKey key)-->DefaultSessionManager.retrieveSession(SessionKey sessionKey)-->DefaultSessionManager.retrieveSessionFromDataSource(Serializable sessionId)
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
return sessionDAO.readSession(sessionId);
}
基本流是:getCachedSession获取实现了CacheManager接口的对像--->从 Cache getCache(String name)方法中拿到缓存-->从缓存中获取session数据-->如果拿到直接反回否则从super.readSession方法,从SessionDAO中拿数据。
CachingSessionDAO{
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = getCachedSession(sessionId);
if (s == null) {
s = super.readSession(sessionId);
}
return s;
}
protected Session getCachedSession(Serializable sessionId) {
Session cached = null;
if (sessionId != null) {
Cache cache = getActiveSessionsCacheLazy();
if (cache != null) {
cached = getCachedSession(sessionId, cache);
}
}
return cached;
}
private Cache getActiveSessionsCacheLazy() {
if (this.activeSessions == null) {
this.activeSessions = createActiveSessionsCache();
}
return activeSessions;
}
protected Cache createActiveSessionsCache() {
Cache cache = null;
CacheManager mgr = getCacheManager();
if (mgr != null) {
String name = getActiveSessionsCacheName();
cache = mgr.getCache(name);
}
return cache;
}
}
getCacheManager返回的是实现了CacheManager接口的对像,比如自己实现的基于Redis的等
public interface CacheManager {
Cache getCache(String var1) throws CacheException;
}
============================以下是调用super.readSession(sessionId)的调用关系======================================
public abstract class AbstractSessionDAO implements SessionDAO{
{
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = doReadSession(sessionId);
if (s == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
}
return s;
}
}
#由实际实现子类完成功能
protected abstract Session doReadSession(Serializable sessionId);
}
```
从上我们可以看出来,我们一般会通过继承CachingSessionDAO来实现自己的DAO对象,
SessionsSecurityManager
# Subject对像绑定到线程
ApplicationFilterChain.internalDoFilter()-->OncePerRequestFilter.doFileter()-->AbstractShiroFilter.doFilterInternal()-->DelegatingSubject.execute()-->SubjectCallable.call()-->threadState.bind()--》ThreadContext.bind(this.subject)
```java
AbstractShiroFilter{
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
//创建DelegatingSubject对像
final Subject subject = createSubject(request, response);
//noinspection unchecked subject.execute方法会先去当前线程ThreadLocal下绑定Subject对像
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response);
executeChain(request, response, chain);
return null;
}
});
}
#DelegatingSubject.execute方法详情
public V execute(Callable callable) throws ExecutionException {
//包装成SubjectCallable
Callable associated = associateWith(callable);
try {
return associated.call();
} catch (Throwable t) {
throw new ExecutionException(t);
}
}
public Callable associateWith(Callable callable) {
return new SubjectCallable(this, callable);
}
//associated.call()
public V call() throws Exception {
try {
threadState.bind();
return doCall(this.callable);
} finally {
threadState.restore();
}
}
//threadState.bind();
public void bind() {
SecurityManager securityManager = this.securityManager;
if ( securityManager == null ) {
//try just in case the constructor didn't find one at the time:
securityManager = ThreadContext.getSecurityManager();
}
this.originalResources = ThreadContext.getResources();
ThreadContext.remove();
ThreadContext.bind(this.subject);
if (securityManager != null) {
ThreadContext.bind(securityManager);
}
}
//ThreadContext.bind(this.subject);
public static void bind(Subject subject) {
if (subject != null) {
put(SUBJECT_KEY, subject);
}
}
//put(SUBJECT_KEY, subject);
private static final ThreadLocal