spring中aop全注解时配置类怎么写

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring中aop全注解时配置类怎么写相关的知识,希望对你有一定的参考价值。

参考技术A

先说注解,使用注解配置Spring AOP总体分为两步,第一步是在xml文件中声明激活自动扫描组件功能,同时激活自动代理功能(同时在xml中添加一个UserService的普通服务层组件,来测试AOP的注解功能):

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
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-3.0.xsd">

<!-- 激活组件扫描功能,在包cn.ysh.studio.spring.aop及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="cn.ysh.studio.spring.aop"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 用户服务对象 -->
<bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService" />

</beans>

第二步是为Aspect切面类添加注解:

package cn.ysh.studio.spring.aop.aspect;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.springframework.stereotype.Component;

/**
* 系统服务组件Aspect切面Bean
* @author Shenghany
* @date 2013-5-28
*/
//声明这是一个组件
@Component
//声明这是一个切面Bean
@Aspect
public class ServiceAspect

private final static Log log = LogFactory.getLog(ServiceAspect.class);

//配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
@Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
public void aspect()

/*
* 配置前置通知,使用在方法aspect()上注册的切入点
* 同时接受JoinPoint切入点对象,可以没有该参数
*/
@Before("aspect()")
public void before(JoinPoint joinPoint)
if(log.isInfoEnabled())
log.info("before " + joinPoint);



//配置后置通知,使用在方法aspect()上注册的切入点
@After("aspect()")
public void after(JoinPoint joinPoint)
if(log.isInfoEnabled())
log.info("after " + joinPoint);



//配置环绕通知,使用在方法aspect()上注册的切入点
@Around("aspect()")
public void around(JoinPoint joinPoint)
long start = System.currentTimeMillis();
try
((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
if(log.isInfoEnabled())
log.info("around " + joinPoint + "\\tUse time : " + (end - start) + " ms!");

catch (Throwable e)
long end = System.currentTimeMillis();
if(log.isInfoEnabled())
log.info("around " + joinPoint + "\\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());




//配置后置返回通知,使用在方法aspect()上注册的切入点
@AfterReturning("aspect()")
public void afterReturn(JoinPoint joinPoint)
if(log.isInfoEnabled())
log.info("afterReturn " + joinPoint);



//配置抛出异常后通知,使用在方法aspect()上注册的切入点
@AfterThrowing(pointcut="aspect()", throwing="ex")
public void afterThrow(JoinPoint joinPoint, Exception ex)
if(log.isInfoEnabled())
log.info("afterThrow " + joinPoint + "\\t" + ex.getMessage());




测试代码:

package cn.ysh.studio.spring.aop;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.ysh.studio.spring.aop.service.UserService;
import cn.ysh.studio.spring.mvc.bean.User;

/**
* Spring AOP测试
* @author Shenghany
* @date 2013-5-28
*/
public class Tester

private final static Log log = LogFactory.getLog(Tester.class);

public static void main(String[] args)
//启动Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取service组件
UserService service = (UserService) context.getBean("userService");
//以普通的方式调用UserService对象的三个方法
User user = service.get(1L);
service.save(user);
try
service.delete(1L);
catch (Exception e)
if(log.isWarnEnabled())
log.warn("Delete user : " + e.getMessage());




控制台输出如下:

INFO [spring.aop.aspect.ServiceAspect:40] before execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.service.UserService:19] getUser method . . .
INFO [spring.aop.aspect.ServiceAspect:60] around execution(User cn.ysh.studio.spring.aop.service.UserService.get(long)) Use time : 42 ms!
INFO [spring.aop.aspect.ServiceAspect:48] after execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(User cn.ysh.studio.spring.aop.service.UserService.get(long))
INFO [spring.aop.aspect.ServiceAspect:40] before execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.service.UserService:26] saveUser method . . .
INFO [spring.aop.aspect.ServiceAspect:60] around execution(void cn.ysh.studio.spring.aop.service.UserService.save(User)) Use time : 2 ms!
INFO [spring.aop.aspect.ServiceAspect:48] after execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(void cn.ysh.studio.spring.aop.service.UserService.save(User))
INFO [spring.aop.aspect.ServiceAspect:40] before execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
INFO [spring.aop.service.UserService:32] delete method . . .
INFO [spring.aop.aspect.ServiceAspect:65] around execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long)) Use time : 5 ms with exception : spring aop ThrowAdvice演示
INFO [spring.aop.aspect.ServiceAspect:48] after execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
INFO [spring.aop.aspect.ServiceAspect:74] afterReturn execution(boolean cn.ysh.studio.spring.aop.service.UserService.delete(long))
WARN [studio.spring.aop.Tester:32] Delete user : Null return value from advice does not match primitive return type for: public boolean cn.ysh.studio.spring.aop.service.UserService.delete(long) throws java.lang.Exception

可以看到,正如我们预期的那样,虽然我们并没有对UserSerivce类包括其调用方式做任何改变,但是Spring仍然拦截到了其中方法的调用,或许这正是AOP的魔力所在。

再简单说一下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"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
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-3.0.xsd">


<!-- 系统服务组件的切面Bean -->
<bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
<!-- AOP配置 -->
<aop:config>
<!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
<aop:aspect id="simpleAspect" ref="serviceAspect">
<!-- 配置一个切入点,相当于@Pointcut -->
<aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
<!-- 配置通知,相当于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
<aop:before pointcut-ref="simplePointcut" method="before"/>
<aop:after pointcut-ref="simplePointcut" method="after"/>
<aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
<aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
</aop:aspect>
</aop:config>

</beans>

个人觉得不如注解灵活和强大,你可以不同意这个观点,但是不知道如下的代码会不会让你的想法有所改善:

//配置前置通知,拦截返回值为cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(cn.ysh.studio.spring.mvc.bean.User cn.ysh.studio.spring.aop.service..*(..))")
public void beforeReturnUser(JoinPoint joinPoint)
if(log.isInfoEnabled())
log.info("beforeReturnUser " + joinPoint);



//配置前置通知,拦截参数为cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(* cn.ysh.studio.spring.aop.service..*(cn.ysh.studio.spring.mvc.bean.User))")
public void beforeArgUser(JoinPoint joinPoint)
if(log.isInfoEnabled())
log.info("beforeArgUser " + joinPoint);



//配置前置通知,拦截含有long类型参数的方法,并将参数值注入到当前方法的形参id中
@Before("aspect()&&args(id)")
public void beforeArgId(JoinPoint joinPoint, long id)
if(log.isInfoEnabled())
log.info("beforeArgId " + joinPoint + "\\tID:" + id);


附上UserService的代码(其实很简单):

package cn.ysh.studio.spring.aop.service;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.ysh.studio.spring.mvc.bean.User;

/**
* 用户服务模型
* @author Shenghany
* @date 2013-5-28
*/
public class UserService

private final static Log log = LogFactory.getLog(UserService.class);

public User get(long id)
if(log.isInfoEnabled())
log.info("getUser method . . .");

return new User();


public void save(User user)
if(log.isInfoEnabled())
log.info("saveUser method . . .");



public boolean delete(long id) throws Exception
if(log.isInfoEnabled())
log.info("delete method . . .");
throw new Exception("spring aop ThrowAdvice演示");

return false;



应该说学习Spring AOP有两个难点,第一点在于理解AOP的理念和相关概念,第二点在于灵活掌握和使用切入点表达式。概念的理解通常不在一朝一夕,慢慢浸泡的时间长了,自然就明白了,下面我们简单地介绍一下切入点表达式的配置规则吧。

通常情况下,表达式中使用”execution“就可以满足大部分的要求。表达式格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

modifiers-pattern:方法的操作权限

ret-type-pattern:返回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:参数名

throws-pattern:异常

其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。

最后说一下通知参数

可以通过args来绑定参数,这样就可以在通知(Advice)中访问具体参数了。例如,<aop:aspect>配置如下:

<aop:config>
<aop:aspect id="TestAspect" ref="aspectBean">
<aop:pointcut id="businessService"
expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
<aop:after pointcut-ref="businessService" method="doAfter"/>
</aop:aspect>
</aop:config>上面的代码args(msg,..)是指将切入点方法上的第一个String类型参数添加到参数名为msg的通知的入参上,这样就可以直接使用该参数啦。



本回答被提问者采纳

以上是关于spring中aop全注解时配置类怎么写的主要内容,如果未能解决你的问题,请参考以下文章

spring全注解事务管理中怎么手动回滚事物

mapperscannerconfigurer 怎么注解

Spring基础:AOP注解和XML方式实现

spring学习5:基于注解实现spring的aop

Spring IoC 容器配置(全注解方式 )

阶段3 2.Spring_08.面向切面编程 AOP_9 spring基于注解的AOP配置