在普通AOP开发中,有几个通知,就要建几个类,很麻烦,如果能将这些通知类全浓缩到一个类中,会方便许多,因此可以使用AspectJ进行Aop开发。
AspectJ介绍
AspectJ是一个基于Java语言的AOP框架,Spring2.0 开始引入对AspectJ的支持,AspectJ扩展了Java语言,提供了专门的编译器,在编译时提供了横向代码的注入。
@AspectJ是AspectJ1.5新增功能,通过JDK1.5注解技术,允许直接在Bean中定义切面
新版本的Spring中,建议使用AspectJ方式开发AOP
AspectJ开发的两种方式:
- 基于xml配置文件方式开发
- 基于注解方式开发
开发前的准备:
在进行开发前需要导入jar包
1. 基于xml配置文件方式开发
创建一个目标类Man.java
public class Man {
public void eat() {
System.out.println("喝酒");
}
}
创建一个切面类,切面类中有各种通知,其方法名可以随意定义,只要在配置文件中对应即可;
此处要注意,环绕通知的方法需要传入一个ProceedingJoinPoint对象,这个对象的作用是执行目标对象的方法的执行;
异常通知的方法需要传入一个Throwable对象,用于接收目标对象执行时抛出的异常,从而执行这个通知的功能;切面类的创建如下
public class MyAdvice {
/*
* 前置通知
*/
public void beforeAdvice() {
System.out.println("抽烟");
}
/**
* 后置通知
*/
public void afterAdvice() {
System.out.println("烫头");
}
/**
* 环绕通知
* @param jp
* @throws Throwable
*/
public void arroundAdvice(ProceedingJoinPoint jp) throws Throwable {
System.out.println("抽烟");
jp.proceed(); // 执行目标方法
System.out.println("烫头");
}
/**
* 异常通知
* @param tw
*/
public void exceptionAdvice(Throwable tw) {
System.out.println("====发生了异常=====");
}
}
切面类创建好了,接下来需要配置applicationContext.xml文件,先要声明目标对象和切面对象,声明后在aop:config标签内写切入点标签,以及各个通知的标签;需要注意的是:
- 与普通AOP开发不同的是,AspectJ开发中,切入点标签和各个通知的标签是写在aop:config标签内的 aop:aspect内的;
- aop:aspect标签的ref属性的值指向切面对象,与切面对象的id的值一致
- 通知标签中, aop:before表示前置通知、 aop:after表示后置通知、aop:around表示环绕通知、aop:after-throwing表示异常通知
- 异常通知标签aop:after-throwing中必须添加throwing属性,其值必须与切面类中的异常通知方法传入的参数名一致;
配置方法如下
<bean id="man" class="com.sxt.bean.Man"></bean>
<bean id="myAdvice" class="com.sxt.bean.MyAdvice"></bean>
<aop:config>
<aop:aspect ref="myAdvice">
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.sxt.bean.Man.*())" id="pc"/>
<!-- 配置切面方法 -->
<aop:before method="beforeAdvice" pointcut-ref="pc"/>
<aop:after method="afterAdvice" pointcut-ref="pc"/>
<aop:around method="arroundAdvice" pointcut-ref="pc"/>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="pc" throwing="tw"/>
</aop:aspect>
</aop:config>
测试:
public class Test {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Man bean = app.getBean(Man.class);
bean.eat();
}
}
2. 基于注解方式开发
创建一个目标类Man.java,每一个注解中的都要声明切入点,例如 @Before(“execution(* com.sxt.bean.Man.*())”),异常注解中的throwing的值必须与参数名称一致
public class Man {
public void eat() {
System.out.println("喝酒");
}
}
在切面类上加入注解
@Component // 让Spring的IOC容器创建对象
@Aspect // 表示是一个切面类
public class MyAdvice {
/*
* 前置通知
*/
@Before("execution(* com.sxt.bean.Man.*())")
public void beforeAdvice() {
System.out.println("抽烟");
}
/**
* 后置通知
*/
@After("execution(* com.sxt.bean.Man.*())")
public void afterAdvice() {
System.out.println("烫头");
}
/**
* 环绕通知
* @param jp
* @throws Throwable
*/
@Around("execution(* com.sxt.bean.Man.*())")
public void arroundAdvice(ProceedingJoinPoint jp) throws Throwable {
System.out.println("抽烟");
jp.proceed();
System.out.println("烫头");
}
/**
* 异常通知
* @param tw
*/
@AfterThrowing(value="execution(* com.sxt.bean.Man.*())",throwing="tw")
public void exceptionAdvice(Throwable tw) {
System.out.println("====发生了异常=====");
}
}
配置xml文件的思路
有了切面类,切面类上有@componet注解,在applicationContext.xml文件中可以不声明切面类对象,但是要声明目标类对象;
因为使用了注解,所以在xml文件中需要扫描注解;
扫描到注解后需要解析,所以还需要开启用于解析的标签;
按照这个思路,我们可以写出正确的xml文件
<!--声明目标对象-->
<bean id="man" class="com.sxt.bean.Man"></bean>
<!--开启AOP的自动代理,Spring框架会解析切面注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!---开启注解扫描-->
<context:component-scan base-package="com.sxt.bean"></context:component-scan>
由切面类的代码可以发现,每个通知的切入点都一样,要写很多重复的代码,那么可不可以用一个什么东西来代替这个切入点呢?
答案是肯定的,如果在切面类中声明切入点,就可以达到想要的效果,具体代码实现如下
@Component
@Aspect
public class MyAdvice {
/**
* 声明切入点
*/
@Pointcut("execution(* com.sxt.bean.Man.*())")
public void p() {
}
/*
* 前置通知
*/
@Before(value="p()") // 使用切入点注解注释的方法名代替冗长的切入点
public void beforeAdvice() {
System.out.println("抽烟");
}
/**
* 后置通知
*/
@After(value="p()")
public void afterAdvice() {
System.out.println("烫头");
}
/**
* 环绕通知
* @param jp
* @throws Throwable
*/
@Around(value="p()")
public void arroundAdvice(ProceedingJoinPoint jp) throws Throwable {
System.out.println("抽烟");
jp.proceed();
System.out.println("烫头");
}
/**
* 异常通知
* @param tw
*/
@AfterThrowing(value="p()",throwing="tw")
public void exceptionAdvice(Throwable tw) {
System.out.println("====发生了异常=====");
}
}
除此之外,还有更骚的操作,有一种注解,叫做@EnableAspectJAutoProxy,它的意思是开启切面的注解配置。
简单来说,只要它注释在切面类上,xml文件中就不需要写开启AOP自动代理的标签了,也就是下面这个标签。
<!--开启AOP的自动代理,Spring框架会解析切面注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>