最近在做一个项目,项目地址: https://github.com/wangyuanjun008/wyj-parent.git
写日志注解完,进行测试时,发现日志注解没有生效,代码如下:
定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String action() default "";//动作
}
切面:
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("@annotation(com.wyj.annotation.SysLog)")
public void pointCut(){}
@Around("pointCut()")
public Object aroud(ProceedingJoinPoint joinPoint) throws Throwable{
// 开始时间
long beginTime = System.currentTimeMillis();
//执行目标方法
Object result = joinPoint.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(joinPoint, time);
return result;
}
}
applicationContext.xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
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-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<context:annotation-config />
<context:component-scan base-package="com.wyj" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
controller代码:
@Controller
@RequestMapping(value = "/user")
public class UserController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserService userService;
@SysLog(action="新增/编辑用户")
@ResponseBody
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Retval save(User user) {
Retval retval = Retval.newInstance();
try {
if (user.getUserId() == null) {
userService.saveUser(user);
} else {
userService.updateUser(user);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return retval;
}
}
和别人讨论了下,大致是Spring上下文的问题
我的AOP配置是这样的:AOP命名空间和这两个都配在了ApplicationContext.xml里面了,而不是SpringMVC框架自己约定的配置文件中(我这个项目里面,这个配置文件叫做springmvc-servlet.xml),正因为这样,出问题了。后来,我把AOP命名空间和挪到SpringMVC自己的配置文件里面,AOP就生效了。
代码如下:
springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
default-lazy-init="true">
<mvc:annotation-driven />
<mvc:annotation-driven
content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prettyPrint" value="true" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="application/json" />
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
<context:component-scan base-package="com.wyj"></context:component-scan>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:resources location="/resources/**" mapping="/resources/**" />
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
另外以上所述的是对controller进行切面时的配置,如果是对service进行切面,那么
这两个注释就要用在ApplicationContext.xml里面了,注意此时不要开启aop的cglib代理模式。
解释:
1.SpringMVC这个框架简化了很多的配置,但是请注意@Controller和@Service都是SpringMVC框架包里面的,也就是说,这些类的实例化以及注入也是由SpringMVC这个框架完成的(确切的来说是这个框架自己有的上下文的IoC容器完成的)。
2.而对AOP和事务的支持是Spring框架本身完成的,是Spring框架的应用上下文所扫描并处理的。
从1.2可以得出一个结论,如果SpringMVC和Spring本身用的是一个应用上下文,一个Ioc容器,那随便你的和命名空间配置在哪里,无论是Spring的ApplicationContext.xml还是SpringMVC的springmvc-servlet.xml里面,反正都是一个容器,怎么扫描,怎么处理都能找到。
但关键的是以上假设不成立,总的来说SpringMVC的应用上下文的 “ 父 ” 上下文才是Spring的应用上下文。那么这个也就是说Spring的应用上下文初始化完成的时候,它开始扫描到底哪些Bean用了AspectJ的注解,哪些用了Transactional的注解,但是利用SpringMVC注解配置的这些Bean它是找不到的,因为用了这些注解的Bean还没有被实例化甚至是还没有被装载,为什么呢?因为管理这些bean的SpringMVC的上下文可能还没有完成初始化。OK,既然Spring的上下文找不到到底哪些Bean应用了注解,那他自然也没有办法给这些Bean提供声明式AOP和事务的支持了。
至于为什么SpringMVC的应用上下文的 “ 父 ” 上下文才是Spring的应用上下文,这里有大牛为我们详解:http://blog.csdn.net/c289054531/article/details/9196149
另外,Spring中的切面类固然要用@Aspect标注,但也不要忘了用@Componet标注,这样才能被注册到容器中