最近在做一个项目,项目地址: 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命名空间和挪到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标注,这样才能被注册到容器中