为什么当项目中同时出现shiro+quartz时,service层的aop不生效?
Repeated tests found that when the quartz and Shiro used simultaneously, the service layer aop will fail. The current is not too sure why, but a guess is shiro carrying quartz and quartz.jar conflict caused. But the problem is, why do they conflict with the service layer aop failure? The controller layer is not invalid?
Specifically, I note spring-quartz.xml, then the ClearCacheAop Aspect will take effect.
spring-web.xml
<?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"/>
<!--shiro mvc注解部分-->
<!-- 定义aop切面,用于代理如@RequiresPermissions注解的控制器,进行权限控制 -->
<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!--简化配置:
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" />
</beans>
spring-shiro.xml
<?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"/>
</beans>
spring-service.xml
<?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"/>
</bean>
<!--配置基于注解的声明式事务,默认使用注解来管理事务行为-->
<!--不清楚为什么,没卵用-->
<!--<tx:annotation-driven transaction-manager="transactionManager"/>-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--<tx:method name="*" propagation="REQUIRED"/>-->
<!--
REQUIRED:始终保持方法在一个新的事务或者重复的事务中执行
SUPPORTS:和不加@Transaction有一定区别(区别很小,它包含一个事务作用域)
-->
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config expose-proxy="true" proxy-target-class="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* com.yingjun.ssm.service..*+.*(..))"/>
<aop:advisor id="txAdvisor" advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- spring aop -->
<bean id="logInterceptor" class="com.yingjun.ssm.aop.LogInterceptor"></bean>
<aop:config expose-proxy="true" proxy-target-class="true">
<aop:pointcut expression="execution(public * com.yingjun.ssm.service..*.*(..))" id="servicePointcut"/>
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut-ref="servicePointcut" />
</aop:aspect>
</aop:config>
<!-- 一些业务层需要调用但未扫描的bean -->
<bean class="com.yingjun.ssm.spring.SpringUtils"/>
</beans>
spring-quartz.xml
<?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:component-scan base-package="com.yingjun.ssm.quartz"/>
</beans>
ClearCacheAop.java
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","createUsers","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(className.endsWith("UserServiceImpl") && 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(className.endsWith("RoleServiceImpl") && 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(className.endsWith("ResourceServiceImpl") && 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);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论