一、引子

最近项目需要添加权限拦截,经讨论决定采用spring security4.2.2!废话少说直接上干货!

spring security 4.2.2文档:http://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/#el-access-web

spring security 3 中文2文档:http://www.mossle.com/docs/auth/html/index.html

二、pom.xml配置

需要在pom.xml里配置spring security的依赖,可以通过http://mvnrepository.com/search?q=spring+securitye查询不同版本需要的spring的版本支持。

   <!--spring security  -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>

三、web.xml配置

需要在web.xml中配置Spring Security控制权限过滤器,

   <filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

四、spring-security.xml配置

下面最主要的就是spring-security.xml的配置了

 <?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd" > <!-- 打印调试信息,仅在开发环境中使用 -->
<!-- <debug/> --> <!-- 不需要被拦截的请求 -->
<http pattern="/loginPage" security="none"/>
<http pattern="/scripts/**" security="none"/> <!--
登录页面可以使用第一种:
<http pattern="/login.jsp" security="none"></http>
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" ......
这种方式直接访问.jsp
也可以使用Controller来控制,两种方式登录页面login.jsp的位置不一样!!!
下面是第二种方式:
<http pattern="/login" security="none"></http>
<form-login login-page="/login" login-processing-url="/login" ......
第一个配置告诉spring security,类似于/login的url请求不做过滤处理,而第二个配置信息又告诉spring security url为/login的post请求登录请求处理。正是这种冲突导致了405错误的发生。
既然知道了错误原因,那么只要避免冲突就能解决这个问题:使登录页的请求和登录处理的请求不一致,然后只配置登录页的请求不做拦截处理.
我采用的方法是使用默认的登录URL /login,修改登录页面跳转url为/loginPage。请自行修改代码和配置信息
这样是不是就大工告成了呢?很遗憾,当你启动程序时,输出用户名和密码,不管正确与否,都会有404 Not Found等着你,这又是为什么呢?
登录请求404错误
首先,建议你看一下spring security自动生成的登录页源码,你会发现有如下代码
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
对于什么是csrf,请自行参考网上的资料。
spring security默认情况下csrf protection是开启的,由于我们的登录页没有配置csrf的相关信息,因此spring security内置的过滤器将此链接置为无效链接
解决办法就是配置csrf protection为不可用状态,在配置文件中增加
<csrf disabled="true"/>
--> <!--
1、<http auto-config="true">,他可以自动配置login form,BSIC 认证和logout URL 和logout services,如果没有特殊表明,这个的默认值是false。想要自己配置则设置为"true"
2、Spring Security采用的是一种就近原则,就是说当用户访问的url资源满足多个intercepter-url时,系统将使用第一个符合条件的intercept-url进行权限控制
-->
<http auto-config="true" use-expressions="true">
<!--
另一种权限表达式:
<http use-expressions="false">
<intercept-url pattern='/**' access='ROLE_USER' />
-->
<!-- 禁用CSRF保护,默认是启用 -->
<csrf disabled="true"/> <anonymous enabled="false"/> <!-- 基于角色认证(必须拥有ROLE_XXX角色才能访问所有/**/XXX/**资源) -->
<!-- 确保对功能URL访问都需要权限 -->
<intercept-url pattern="/**/add/**" access="hasRole('ADD')"/>
<intercept-url pattern="/**/update/**" access="hasRole('UPDATE')"/>
<intercept-url pattern="/**/delete/**" access="hasRole('DELETE')"/>
<intercept-url pattern="/**/download/**" access="hasRole('DOWNLOAD')"/>
<intercept-url pattern="/**/access/**" access="hasRole('ADMIN')"/> <!-- Ensures that any request to our application requires the user to be authenticated -->
<intercept-url pattern="/**" access="authenticated"/> <!--
实现免登陆验证,默认有效时间是两周,启用rememberMe之后的两周内,用户都可以直接跳过系统,直接进入系统。
实际上,Spring Security中的rememberMe是依赖cookie实现的,当用户在登录时选择使用rememberMe,系统就会在登录成功后将为用户生成一个唯一标识,并将这个标识保存进cookie中
Spring Security生成的cookie名称是SPRING_SECURITY_REMEMBER_ME_COOKIE,它的内容是一串加密的字符串,
当用户再次访问系统时,Spring Security将从这个cookie读取用户信息,并加以验证。如果可以证实cookie有效,就会自动将用户登录到系统中,并为用户授予对应的权限。
--> <!--
<remember-me remember-me-parameter="remember-me"
data-source-ref="dataSource"/>
spring security还提供了remember me的另一种相对更安全的实现机制 :在客户端的cookie中,仅保存一个无意义的加密串(与用户名、密码等敏感数据无关),然后在db中保存该加密串-用户信息的对应关系,自动登录时,用cookie中的加密串,到db中验证,如果通过,自动登录才算通过。会自动在你的数据库里创建一个表:PERSISTENT_LOGINS。
如果不加data-source-ref="dataSource",会将上述信息放在内存中!
作者的项目有些特殊要求,所有采用了下面的方式:登录后要在myAuthenticationSuccessHandler做特殊的处理!
-->
<remember-me authentication-success-handler-ref="myAuthenticationSuccessHandler"/> <!--
login-page : 表示用户登陆时显示我们自定义的登录页面
authentication-failure-url : 登录认证失败转向的url,当用户输入的登录名和密码不正确时,系统将再次跳转到登录页面,并添加一个error=true参数作为登陆失败的标示,这个标识是我们自定义的。
default-target-url : 登录认证成功转向的地址
-->
<form-login
login-page="/loginPage"
authentication-failure-url="/loginPage?error=true"
authentication-success-handler-ref="myAuthenticationSuccessHandler"
/> <!-- 登出后,返回到登陆页面 -->
<!-- <logout logout-success-url="/loginPage" logout-url="/logout"/>
delete-cookies="JSESSIONID":退出删除JSESSIONID
-->
<logout />
<!--
控制同步session的过滤器
如果concurrency-control标签配置了error-if-maximum-exceeded="true",max-sessions="1",那么第二次登录时,是登录不了的。
如果error-if-maximum-exceeded="false",那么第二次是能够登录到系统的,
但是第一个登录的账号再次发起请求时,会跳转到expired-url配置的url中,
如果没有配置expired-url,则显示:
This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).
翻译过来意思就是说:这个会话已经过期(可能由于多个并发登录尝试相同的用户)
--> <session-management invalid-session-url="/loginPage" >
<concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/loginPage"/>
</session-management> <!-- 指定自己的权限验证过滤器,首先走自己的的过滤器 myFilter,如果被拦截就报没有权限;
如果通过会走spring security自带的拦截器,即上面配置的权限配置!
-->
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter"/>
</http> <!-- 权限认证Spring日志监听器 -->
<beans:bean class="org.springframework.security.authentication.event.LoggerListener"/>
<beans:bean class="org.springframework.security.access.event.LoggerListener"/> <!--
一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
我们的所有控制将在这三个类中实现,解释详见具体配置。
-->
<beans:bean id="myFilter" class="com.tcbd.common.interceptor.MyFilterSecurityInterceptor" >
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
</beans:bean> <!-- 验证配置,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
<authentication-manager alias="authenticationManager" >
<authentication-provider ref="daoAuthenticationProvider" >
</authentication-provider>
</authentication-manager> <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="myUserDetailService" />
<beans:property name="passwordEncoder" ref="passwordEncoder" />
</beans:bean>
<!-- spring推荐的单向加密算法 -->
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> <!-- 在这个类中,读入用户的密码,角色信息,是否锁定,账号是否过期等属性信息 -->
<beans:bean id="myUserDetailService" class="com.tcbd.common.interceptor.MyUserDetailService" /> <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="myAccessDecisionManagerBean" class="com.tcbd.common.interceptor.MyAccessDecisionManager" ></beans:bean> <!-- 资源源数据定义,即定义某一资源可以被哪些角色访问 -->
<beans:bean id="securityMetadataSource" class="com.tcbd.common.interceptor.MyFilterSecurityMetadataSource" /> <beans:bean id="myAuthenticationSuccessHandler" class="com.tcbd.common.interceptor.MyAuthenticationSuccessHandler"/> </beans:beans>

spring security为我们提供了三种通配符。
通配符:?
示例:/admin/g?t.jsp
匹配任意一个字符,/admin/g?t.jsp可以匹配/admin/get.jsp和/admin/got.jsp或是/admin/gxt.do。不能匹配/admin/xxx.jsp。
通配符:*
示例:/admin/*.jsp
匹配任意多个字符,但不能跨越目录。/*/index.jsp可以匹配/admin/index.jsp和/user/index.jsp,但是不能匹配/index.jsp和/user/test/index.jsp。
通配符:**
示例:/**/index.jsp
可以匹配任意多个字符,可以跨越目录,可以匹配/index.jsp,/admin/index.jsp,/user/admin/index.jsp和/a/b/c/d/index.jsp

五、自己的拦截器

1,读入用户的密码,角色信息,是否锁定,账号是否过期等属性信息 :MyUserDetailService

 import java.util.ArrayList;
import java.util.Collection; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; /**
* 从数据库中读入用户的密码,角色信息,是否锁定,账号是否过期等
*
*/
public class MyUserDetailService implements UserDetailsService {
@Resource
private UserService userService; /**
* 数据库交互获取用户拥有的权限角色,并设置权限
*/
@Override
public UserDetails loadUserByUsername(String userCode) throws UsernameNotFoundException, DataAccessException {
// 根据登录用户名获取用户信息
Users user = new Users();
user.setUserCode(username);
user = userService.selectByModel(user);
if (null != user) {
// 存放权限
Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
String action = user.getRole();
if (StringUtils.isNotBlank(action)) {
String[] roleaCtion = action.split(",");
for (int i = 0; i < roleaCtion.length; i++) {
SimpleGrantedAuthority auth = new SimpleGrantedAuthority(roleaCtion[i]);
auths.add(auth);
}
}
//spring security自带的User对象
User userDetails = new User(username, user.getPassword(), true, true, true, true, auths);
return userDetails;
}
return null;
}
}

2,定义某一资源可以被哪些角色访问:MyFilterSecurityMetadataSource

 import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static Logger logger = Logger.getLogger(MyFilterSecurityMetadataSource.class); public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
HttpServletRequest request = fi.getRequest();
String requestUrl = fi.getRequest().getRequestURI();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
// 所有URL对应的角色,应用启动就存放到静态资源里,得到的结果是:不同的URL下,包含的多个角色
Map<String, String> resRoles = Constant.URL_ROLES; for (Map.Entry<String, String> ent : resRoles.entrySet()) {
String url = ent.getKey();
String roles = ent.getValue();
//根据业务写自己的匹配逻辑
if(requestUrl.startsWith(url)){
attributes.addAll(SecurityConfig.createListFromCommaDelimitedString(roles));
}
}
logger.debug("【"+request.getRequestURI()+"】 roles: "+attributes);
return attributes;
} public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
} public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}

3,访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源:MyAccessDecisionManager

 import java.util.Collection;
import java.util.Iterator; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; /**
* 在这种方法中,需要与configAttributes比较验证
* 1、一个对象是一个URL,一个过滤器被这个URL找到权限配置,并通过这里
* 2、如果没有匹配相应的认证,AccessDeniedException
*
*/
public class MyAccessDecisionManager implements AccessDecisionManager { private static final Log logger = LogFactory.getLog(MyAccessDecisionManager.class); /**
* 在这个类中,最重要的是decide方法,如果不存在对该资源的定义,直接放行; 否则,如果找到正确的角色,即认为拥有权限,并放行,否则throw
* new AccessDeniedException("no right");这样,就会进入上面提到的/accessDenied.jsp页面。
* @param authentication :当前用户所且有的角色
* @param object :当前请求的URL
* @param configAttributes :当前URL所且有的角色
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
// 资源所需的角色列表,如果角色列表为空,则放行!继续下一个拦截器。
if (configAttributes == null) {
return;
}
// 即将访问的资源URL,如 : /admin.jsp
logger.info("URL :"+object);
// 遍历所需的角色集合
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
ConfigAttribute ca = ite.next();
// 该资源所需要的角色
String needRole = ((SecurityConfig) ca).getAttribute();
// authentication.getAuthorities()获取用户所拥有的角色列表,如:OLE_DEFULT
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
// 将资源所需要的角色与用户拥有的角色比较
if (needRole.equals(grantedAuthority.getAuthority())) {
// grantedAuthority is user's role.
// 角色相同,直接放行
return;
}
}
}
// 否则,提示没有权限访问该资源
throw new AccessDeniedException("no right");
} @Override
public boolean supports(ConfigAttribute attribute) {
return true;
} @Override
public boolean supports(Class<?> clazz) {
return true;
} }

4,拦截器核心,MyFilterSecurityInterceptor

 import java.io.IOException;

 import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; /**
*
*/
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
implements Filter { private FilterInvocationSecurityMetadataSource securityMetadataSource; /* get、set方法 */
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
} public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
} @Override
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
} @Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
} @Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
} public void invoke(FilterInvocation fi) throws IOException,
ServletException {
/**
* 最核心的代码就是@link InterceptorStatusToken token = super.beforeInvocation(fi);
* 它会调用我们定义的MyInvocationSecurityMetadataSource.getAttributes方法和MyAccessDecisionManager.decide方法
* 这一句,即在执行doFilter之前,进行权限的检查,而具体的实现已经交给@link MyAccessDecisionManager 了
*/
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//继续走下一个拦截器,也就是org.springframework.security.web.access.intercept.FilterSecurityInterceptor
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
} @Override
public void destroy() { } @Override
public void init(FilterConfig arg0) throws ServletException { } }

5,登录页面(需要自己改一些东西)

 <%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix='fmt' uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/favicon.ico"> <title><fmt:message key="application.title"></fmt:message></title> <!-- Bootstrap core CSS -->
<link href="/scripts/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- Custom styles for this template --> <link href="/scripts/apps/css/signin.css" rel="stylesheet"> <style type="text/css">
.tip {
font-size: 10px;
color: red;
text-align: center;
} </style>
</head> <body>
<div class="container">
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" id="login_form" class="form-signin" method="post">
<div class="form-signin-heading text-center">
<h2 ><fmt:message key="application.title"></fmt:message></h2>
</div>
<div id="tip" class="tip"></div>
<c:if test="${param.error != null}">
<span style="color:red">用户名或密码有误</span>
</c:if> <label for="inputEmail" class="sr-only">登录帐号</label>
<input type="text" id="inputUserCode" name="username" class="form-control" placeholder="登录帐号" autofocus>
<label for="inputPassword" class="sr-only">登录密码</label>
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="登录密码">
<div class="input-group input-sm">
<div class="checkbox">
<label><input type="checkbox" id="rememberme" name="remember-me" value="true"> 下次自动登录</label>
</div>
</div>
<button id="login_button" class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form> </div> <script src="/scripts/plugins/jQuery/jquery-2.2.3.min.js"></script>
</body>
</html>

在JSP中相应的操作按钮上加上如下标签,控制显示:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> 该标签一定要加上!!!
<sec:authorize access="hasRole('ADD')">
<a href="/XXX/add">增加</a>
</sec:authorize>

数据库设计:user表里有roleId,role表,resource-role表,resource表

spring security4.2.2的maven配置+spring-security配置详解+java源码+数据库设计的更多相关文章

  1. Spring Boot启动命令参数详解及源码分析

    使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring ...

  2. Spring IOC容器启动流程源码解析(一)——容器概念详解及源码初探

    目录 1. 前言 1.1 IOC容器到底是什么 1.2 BeanFactory和ApplicationContext的联系以及区别 1.3 解读IOC容器启动流程的意义 1.4 如何有效的阅读源码 2 ...

  3. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

  4. Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍

    Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍 spring集成 mybatis Spring4.x零配置框架搭建 两年前一直在做后 ...

  5. Spring Security Filter详解

    Spring Security Filter详解 汇总 Filter 作用 DelegatingFilterProxy Spring Security基于这个Filter建立拦截机制 Abstract ...

  6. 整合Spring时Service层为什么不做全局包扫描详解

    合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两个 ...

  7. spring security之 默认登录页源码跟踪

    spring security之 默认登录页源码跟踪 ​ 2021年的最后2个月,立个flag,要把Spring Security和Spring Security OAuth2的应用及主流程源码研究透 ...

  8. spring security 授权方式(自定义)及源码跟踪

    spring security 授权方式(自定义)及源码跟踪 ​ 这节我们来看看spring security的几种授权方式,及简要的源码跟踪.在初步接触spring security时,为了实现它的 ...

  9. spring盒springMVC整合父子容器问题:整合Spring时Service层为什么不做全局包扫描详解

    整合Spring时Service层为什么不做全局包扫描详解 一.Spring和SpringMVC的父子容器关系 1.讲问题之前要先明白一个关系 一般来说,我们在整合Spring和SpringMVC这两 ...

随机推荐

  1. 飞思卡尔IMX6处理器的GPIO配置方式

    在linux或android系统中,假如我们要配置飞思卡尔IMX6处理器的GPIO管脚,比如是GPIO_19这个管脚,那么要像这样: [cpp] view plaincopy #define  MX6 ...

  2. TCP连接建立系列 — TCP选项解析

    本文主要分析:在收到客户端的SYN包时,服务器端是如何解析它所携带的TCP选项,并结合本端情况决定是否予以支持. 内核版本:3.6 Author:zhangskd @ csdn blog 概述 收到客 ...

  3. 能量最小化初探,graphcuts能量最小化调用

    1.相对于能量函数来说,能量最小化的办法都有哪些? 梯度下降 模拟退火 图割 2.这个 跟最优化问题的求解,有什么联系跟区别呢? 基本上差不多,其实就是求出来了函数的一个最小值,我们看问题的时候不妨把 ...

  4. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  5. python MultiProcessing标准库使用Queue通信的注意要点

    今天原本想研究下MultiProcessing标准库下的进程间通信,根据 MultiProcessing官网 给的提示,有两种方法能够来实现进程间的通信,分别是pipe和queue.因为看queue顺 ...

  6. C#基础随手笔记之基础操作优化

    对数据的查询,删除等基本操作是任何编程语言都会涉及到的基础,因此,研究了一下C#中比较常用的数据操作类型,并顺手做个笔记. List查询时,若是处理比较大的数据则使用HashSet<T>类 ...

  7. MongoDB学习笔记(四)

    第四章 Mongodb聚合函数 插入 测试数据 for(var j=1;j<3;j++){ for(var i=1;i<3;i++){ var person={ Name:"ja ...

  8. Python_PyMySQL数据库操作

    连接数据库: conn=pymysql.connect(host=,user=',charset='utf8') 建立游标: cur = conn.cursor() 创建一个名字叫 lj 的数据库: ...

  9. javascript学习(二)javascript常见问题总结

    在js使用过程中,经常会碰到一些问题,本人利用闲暇时间整理了一些常见问题的解决方法,贴出来和大家分享,有需要的朋友可以参考下 1.JS中方法和变量都是区分大小写的  2.单引号.双引号在JS中没有特殊 ...

  10. Python新手入门学习常见错误

    当初学 Python 时,想要弄懂 Python 的错误信息的含义可能有点复杂.这里列出了常见的的一些让你程序 crash 的运行时错误. 1)忘记在 if , elif , else , for , ...