我定义了controller层的aop,以及service等的aop,但是service层的不生效?我用Test类直接getBean,调用userService.xx(),service层aop生效,但是在web项目中就是不生效。我初步猜测是配置或者说加载顺序问题?
新的补充:
反复测试发现,当quartz与shiro同时使用时,service层的aop就会失效。目前不是太过确定原因,但是一种猜测是shiro自带的quartz与quartz.jar冲突导致的。但是问题来了,为什么他们冲突导致service层的aop失效呢?而controller层不失效?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描web相关的controller-->
<context:component-scan base-package="com.yingjun.ssm.web"/>
<!-- 激活组件扫描功能,扫描aop的相关组件组件 -->
<context:component-scan base-package="com.yingjun.ssm.aop"/>
<!--启动对@AspectJ注解的支持 , proxy-target-class设置为true,表示通知spring使用cglib而不是jdk的来生成代理方法,
这样AOP可以拦截到Controller -->
<!--写在spring-mvc.xml中-->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!--简化配置:
1、自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
2、提供一系列:数据绑定,数字和日期的format,@NumberFormat,@DataTimeFormat,xml,json默认读写支持
-->
<mvc:annotation-driven/>
<!--静态资源默认servlet配置
1、加入对静态资源的处理:js,css,gif,png
2、允许使用"/"做整体映射
-->
<!-- 当在web.xml 中 DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
<mvc:default-servlet-handler/>
<!-- 静态资源映射 -->
<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>
<!--配置JSP 显示ViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--全局异常捕捉 -->
<bean class="com.yingjun.ssm.exception.GlobalExceptionResolver" />
<bean class="com.yingjun.ssm.spring.SpringUtils"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--扫描service包(包含子包)下所有使用注解的类型-->
<context:component-scan base-package="com.yingjun.ssm.service"/>
<!--配置事务管理器(mybatis采用的是JDBC的事务管理器)-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置基于注解的声明式事务,默认使用注解来管理事务行为-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
package com.yingjun.ssm.aop;
import com.yingjun.ssm.dto.BaseResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
/**
*
* 采用AOP的方式处理@Valid 参数验证。
*/
@Component
@Aspect
public class BindingResultAop {
private static final Logger LOG = LoggerFactory.getLogger(BindingResultAop.class);
@Pointcut("execution(* com.yingjun.ssm.web.*.*(..))")
public void bindingResultPointcut(){
//该方法的内容不重要,该方法的本身只是个标识,供@Pointcut注解依附
}
/**
* Aspect = Advice + Pointcut
* Advice: @Around
* Pointcut: execution(* com.yingjun.ssm.web.*.*(..))
*/
@Around("bindingResultPointcut()")
public Object aroundAdvice(ProceedingJoinPoint jp) throws Throwable{
System.out.println("--->BindingResultAop start...");
String className = jp.getTarget().getClass().getName();
String methodName = jp.getSignature().getName();
LOG.info("before " + className + "." + methodName + "() invoking!");
// 遍历参数,找到BindingResult,判断是否hasError
BindingResult bindingResult = null;
for(Object arg: jp.getArgs()){
if(arg instanceof BindingResult){
bindingResult = (BindingResult) arg;
}
}
if(bindingResult != null){
if(bindingResult.hasErrors()){
LOG.info("--->bindingResult hasError!");
String errorInfo="["+bindingResult.getFieldError().getField()+"]"+bindingResult.getFieldError().getDefaultMessage();
return new BaseResult<Object>(false, errorInfo);
}
}
// 执行目标方法
return jp.proceed();
}
}
package com.yingjun.ssm.aop;
import com.yingjun.ssm.cache.RedisCache;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
* 采用AOP的方式处理: XXService关于数据更新(增删改)时,缓存的清理。
*/
@Component
@Aspect
public class ClearCacheAop {
private static final Logger LOG = LoggerFactory.getLogger(ClearCacheAop.class);
private static final String[] UPDATE_USER_MOTHOD = new String[]{"createUser", "updateUser", "deleteUser", "changePassword"};
private static final String[] UPDATE_ROLE_MOTHOD = new String[]{"createRole", "updateRole", "deleteRole"};
private static final String[] UPDATE_RESOURCE_MOTHOD = new String[]{"createResource", "updateResource", "deleteResource"};
@Autowired
private RedisCache cache;
// 声明切入点
@Pointcut("execution(* com.yingjun.ssm.service..*.*(..))")
public void clearCachePointcut(){}
@Before("clearCachePointcut()")
public void beforeAdvice(JoinPoint jp) {
System.out.println("--->clearCachePointcut start...");
String className = jp.getTarget().getClass().getName();
String methodName = jp.getSignature().getName();
LOG.info("before " + className + "." + methodName + "() invoking!");
if(StringUtils.contains(className, "UserService") && ArrayUtils.contains(UPDATE_USER_MOTHOD, methodName)){
// 此时缓存中的数据不是最新的,需要对缓存进行清理(具体的缓存策略还是要根据具体需求制定)
String cache_key = RedisCache.CAHCENAME + "|UserService|*";
cache.deleteCacheWithPattern(cache_key);
LOG.info("aop: delete cache with key: " + cache_key);
} else if(StringUtils.contains(className, "RoleService") && ArrayUtils.contains(UPDATE_ROLE_MOTHOD, methodName)){
String cache_key = RedisCache.CAHCENAME + "|RoleService|*";
cache.deleteCacheWithPattern(cache_key);
LOG.info("aop: delete cache with key: " + cache_key);
} else if(StringUtils.contains(className, "ResourceService") && ArrayUtils.contains(UPDATE_RESOURCE_MOTHOD, methodName)){
String cache_key = RedisCache.CAHCENAME + "|ResourceService|*";
cache.deleteCacheWithPattern(cache_key);
LOG.info("aop: delete cache with key: " + cache_key);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 缓存管理器 -->
<bean id="cacheManager" class="com.yingjun.ssm.spring.SpringCacheManagerWrapper">
<property name="cacheManager" ref="springCacheManager"/>
</bean>
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="com.yingjun.ssm.credentials.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- Realm实现 -->
<bean id="userRealm" class="com.yingjun.ssm.realm.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
<!--<property name="authenticationCachingEnabled" value="true"/>-->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!--<property name="authorizationCachingEnabled" value="true"/>-->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
</bean>
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="180000"/>
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<!-- 会话DAO -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!-- step1: 配置securityManager,并set给SecurityUtils -->
<!-- 安全管理器 (上面的都是为此处铺垫) -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!-- step2: 配置shiroFilter(securityManager+url拦截器) -->
<!-- shiroFilter: shiro启动的核心。web.xml中的DelegatingFilterProxy会寻找Spring容器中的shiroFilter,把所有请求交给他过滤-->
<!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
<property name="loginUrl" value="/login"/>
</bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/static/** = anon <!-- 静态资源不拦截 -->
/welcome = anon <!-- 匿名拦截器,定义不拦截的url -->
/login = authc <!-- 直接访问/login,如果之前是'记住我'登录的不算,需要重新登录 -->
/register = anon
/logout = logout <!-- 注销拦截器 -->
/authenticated = authc
/** = user <!-- 所有请求必须通过user拦截器,否则跳转loginUrl(即使用'subject.login'或者'记住我'登录的用户通过) -->
</value>
</property>
</bean>
<!-- step3: 其他配置-->
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 定义aop切面,用于代理如@RequiresPermissions注解的控制器,进行权限控制 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd ">
<!--开启这个配置,spring才能识别@Scheduled注解-->
<!--但是和shiro一起使用,service层的aop将失效-->
<!--<task:annotation-driven />-->
<!--<context:annotation-config />-->
<!--<context:component-scan base-package="com.yingjun.ssm.quartz"/>-->
</beans>
巴扎黑2017-04-18 10:27:42
It should be a problem with the scanning scope of the spring container
<!-- Activate the component scanning function and scan the related components of aop -->
<context:component-scan base-package="com.yingjun.ssm.aop"/>
Try adding this sentence in service.xml
Supplementary content: Remove the corresponding content from another configuration file
PHP中文网2017-04-18 10:27:42
Repeated testing found that when quartz and shiro are used at the same time, the aop of the service layer will fail. The reason is not yet certain, but one guess is that it is caused by the conflict between quartz that comes with Shiro and quartz.jar. But the question arises, why do their conflicts cause the aop of the service layer to fail? And the controller layer does not fail?