Spring -- aop, 用Aspectj进行AOP开发
1. 概要
AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知,在方法执行之前执行
@After: 后置通知,在方法执行之后执行
@AfterRunning: 返回通知,在方法返回结果之后执行
@AfterThrowing: 异常通知,在方法抛出异常之后
@Around: 环绕通知,围绕着方法执行
前置通知:@Before
@Aspect
public class AudienceAdvice {
@Before("execution(* WelcomeService.*(..))")
public void takeSeats(){..}
@Before("execution(* WelcomeService.*(..))")
public void turnOffCellphone(JoinPoint jp){..}
JoinPoint参数可访问连接点细节,入方法名和参数等.
jp.getTarget()//目标对象
jp.getThis()//当前的代理对象
jp.getArgs();//方法调用参数
jp.getSignature().getName()//方法签名
后置通知:@After
@After("execution(* *..WelcomeService.*(..))")
public void applaud(){..}
后置通知在目标方法执行完成之后执行.一个切面aspect包含很多通知.
@After
后置通知表明目标方法执行完之后,不论是否抛异常,都会织入该通知.
@AfterReturning
方法返回后通知只在目标方法返回以后执行,若抛异常不执行.
@AfterReturning(pointcut="",returning="res")
public void xxx(Joinput jp,Object res)
在后置通知中可接收到返回值.res即是用来接收返回值的对象.
环绕通知:@Around
@Around("execution(* *..WelcomeService.*(..))")
public void around(ProceedingPointCut jp){..}
注意:可以控制目标方法是否调用,以及返回完全不同的对象,要慎用.
指定优先级:
@Aspect
@Order(0)
public class xxx{...}
加上@Order注解可以指定加入切面的优先级(先后顺序,值越小,优先级越高)
引入通知:
@Aspect
public class MyAspectjIntroduction {
@DeclareParents(value="*..*Service*",
defaultImpl=ModifyDateImpl.class)
private ModifyDate md ;
}
value:指定哪些类可以应用该属性
defaultImpl:指定接口的实现类
典型Aspectj切入点表达式定义:
execution(* cn.itcast.WelcomeServiceImpl.*(..))
execution(public * *..WelcomeServiceImpl.*(..))
execution(public void *..WelcomeServiceImpl.*(..))
execution(public void *..*Service.*(double,double))..
切入点表达式运算(&& || !)
@Pointcut("execution(..) || execution(..)")
2. 示例代码:
Performer.java 演员接口
public interface Performer { public void show(); }
Singer.java 接口实现
public class Singer implements Performer { public void show() { System.out.println("其实我是个演员!"); // String str = null ; // str.toString(); } }
Audience.java 观众类, 即通知类
package cn.itcast.spring.aop.aspectj; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.DeclareParents; import org.aspectj.lang.annotation.Pointcut; /** * 观众, 需要添加 @Aspect */ @Aspect public class Audience { /** * 引入通知, */ @DeclareParents(value="cn.itcast.spring.aop.aspectj.Singer",defaultImpl=ModifyDateImpl.class) private ModifyDate md ; /** * 定义在切入点, 切入点表达式 */ //任意返回值 Performer中 任意方法 任意参数 @Pointcut("execution(* cn.itcast.spring.aop.aspectj.Performer.*(..))") public void perform(){ } /** * 坐好 */ @Before(value="perform()") public void takeSeat(){ System.out.println("takeSeat"); } /** * 关机 */ @Before(value="perform()") public void turnOffCellphone(JoinPoint jp){ System.out.println(jp.getSignature().getName()); System.out.println(jp.getArgs()); System.out.println(jp.getTarget()); System.out.println(jp.getThis()); System.out.println("turnOffCellphone"); } /** * returning:指定哪个参数接受方法的返回值 */ @AfterReturning(pointcut="perform()",returning="ret") public void applaud(Object ret){ System.out.println("applaud"); System.out.println("ret = " + ret); } /** * 退票 * throwing:指定哪个参数接受异常信息 */ @AfterThrowing(pointcut="perform()",throwing="e") public void demandMoney(Exception e){ System.out.println("demandMoney"); System.out.println("出事了 " + e.getMessage()); } @After("perform()") public void goHome(){ System.out.println("goHome"); } /* * 环绕通知 */ @Around(value="perform()") public Object watch(ProceedingJoinPoint pjp){ try { System.out.println("takeSeat"); System.out.println("turnOffCellphone"); Object o = pjp.proceed(); System.out.println("applaud"); return o; } catch (Throwable e) { System.out.println("demandMoney"); } finally{ System.out.println("goHome"); } return null ; } }
ModifyDate.java 引入通知接口
/** * 修改日期 */ public interface ModifyDate { public void setModifyDate(Date date); public Date getModifyDate(); }
ModifyDateImpl.java 引入通知实现
public class ModifyDateImpl implements ModifyDate { private Date date ; public Date getModifyDate() { return date; } public void setModifyDate(Date date) { this.date = date ; } }
aspectj.xml 配置文件
<?xml version="1.0"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- 观众通知 --> <bean id="audience" class="cn.itcast.spring.aop.aspectj.Audience" /> <!-- 歌手 --> <bean id="singer" class="cn.itcast.spring.aop.aspectj.Singer" /> <!-- 使用aspectj自动产生代理 --> <aop:aspectj-autoproxy /> </beans>
App.java 测试代码
public class App { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext( "cn/itcast/spring/aop/aspectj/aspectj.xml"); Performer p = (Performer) ac.getBean("singer"); p.show(); //测试引入通知 ((ModifyDate)p).setModifyDate(new Date()); System.out.println(((ModifyDate)p).getModifyDate()); } }
3. 使用pojo+xml开发aop
基于注解的aspectj声明优先于xml配置.基于xml的配置是spring专有的.aspectj得到越来越多的支持,具备更好的重用性.
其他bean 和 通知类 都不会改变, 只会不再需要用注解, 改用xml文件
pojo.xml 引入通知, 前置 后置 异常通知
<?xml version="1.0"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- 观众 --> <bean id="audience" class="cn.itcast.spring.aop.pojo.Audience" /> <!-- 歌手 --> <bean id="singer" class="cn.itcast.spring.aop.pojo.Singer" /> <!-- aop配置 --> <aop:config> <aop:aspect ref="audience"> <!-- 引入通知 --> <aop:declare-parents types-matching="cn.itcast.spring.aop.pojo.Singer" implement-interface="cn.itcast.spring.aop.pojo.ModifyDate" default-impl="cn.itcast.spring.aop.pojo.ModifyDateImpl"/> <!-- 单独定义切入点表达式 --> <aop:pointcut id="audiencePointcut" expression="execution(* cn.itcast.spring.aop.pojo.Performer.*(..))"/> <!-- 前置通知 --> <aop:before method="takeSeat" pointcut-ref="audiencePointcut" /> <aop:before method="turnOffCellphone" pointcut-ref="audiencePointcut"/> <!-- 后置通知,ret指定哪个参数接受返回值 --> <aop:after-returning method="applaud" pointcut-ref="audiencePointcut" returning="ret"/> <!-- throwing:指定哪个参数接受异常信息 --> <aop:after-throwing method="demandMoney" pointcut-ref="audiencePointcut" throwing="ex"/> <!-- --> <aop:after method="goHome" pointcut-ref="audiencePointcut"/> </aop:aspect> </aop:config> </beans>
pojoAround.xml 环绕通知
<?xml version="1.0"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd "> <!-- 观众 --> <bean id="audience" class="cn.itcast.spring.aop.pojo.AudienceAround" /> <!-- 歌手 --> <bean id="singer" class="cn.itcast.spring.aop.pojo.Singer" /> <!-- aop配置 --> <aop:config> <aop:aspect ref="audience"> <aop:around method="watch" pointcut="execution(* cn.itcast.spring.aop.pojo.Performer.*(..))"/> </aop:aspect> </aop:config> </beans>