消息通知通常有5种:
- 前置通知
- 返回通知
- 异常通知
- 后置通知
- 环绕通知
1.采用注解的方式
这里使用简单计算器的例子作为示范:
声明一个Calculator的接口:
public interface Calculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
这里只做简单的加减乘除运算。
实现该接口:
@Component
public class CalculatorImpl implements Calculator{
@Override
public int add(int i, int j) {
int result=i+j;
return result;
}
@Override
public int sub(int i, int j) {
int result=i-j;
return result;
}
@Override
public int mul(int i, int j) {
int result=i*j;
return result;
}
@Override
public int div(int i, int j) {
int result=i/j;
return result;
}
}
开始有一个@Component用来实例化一个bean对象。
然后我们想在每个方法执行前输出执行方法的名称和参数就需要配置一个切面对象了。
//把这个类声明为一个切面:需要把该类放入到IOC容器中,再声明为一个切面
@Order //指定切面的优先级,值越小级别越高
@Aspect
@Component
public class LoggingAspect {
//声明该方法是一个前置通知
@Before("execution(public int com.spring.aop.impl.Calculator.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
List<Object> args=Arrays.asList(joinPoint.getArgs());
System.out.println("The method "+methodName+" begins with"+args);
}
}
@Order是用来指定切面的优先级的,如果声明了多个切面,则可以根据@Order指定的值来判断执行的顺序,默认值越小的优先级越高。
@Aspect就是表面该类是一个切面对象。
此时只要是Calculator中的方法被执行前就会输出相应的参数信息了。
当然,如果我们不需要所有的方法被执行前都输出该信息,我们同样可以指定Calculator中特定的方法,只需要把@Before中的参数修改下:
@Before("execution(public int com.spring.aop.impl.Calculator.add(int, int))")这样就只在执行add方法时才会有提示信息。
同样,我们也可以实现后置,返回,异常,环绕等通知。
后置通知:
//执行完方法之后掉用,无论是否存在异常
@After("execution(public int com.spring.aop.impl.Calculator.*(int, int))")
public void afterMethod(JoinPoint joinPoint) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" end ");
}
返回通知:
@AfterReturning(value="execution(public int com.spring.aop.impl.Calculator.*(int, int))",
returning="result")
public void afterReturning(JoinPoint joinPoint,Object result) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" result: "+result);
}
异常通知:
@AfterThrowing(value="execution(public int com.spring.aop.impl.Calculator.*(int, int))",
throwing="ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" Exception: "+ex);
}
环绕通知:
//类似动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
//必须有返回值,返回值即为目标方法的返回值
@Around("execution(public int com.spring.aop.impl.Calculator.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint p) {
System.out.println("aroundMtethod");
Object result=null;
try {
//前置
result=p.proceed();
//后置
} catch (Throwable e) {
// TODO Auto-generated catch block
//异常
e.printStackTrace();
}
//返回
return result ;
}
环绕通知是功能最强大的通知,但也是最复杂的通知,因为它也包含了其它的四种通知,如果所需要的通知比较简单基础就使用其余四种通知就已经足够了。
配置bean
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="com.spring.aop.impl"></context:component-scan>
<!-- 使AspjectJ注解起作用:自动为匹配的类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
记住需要选中bean的namespace中的aop和context 。
测试:
public static void main(String[] args) {
//1.创建IOC容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
Calculator calculator=ctx.getBean(Calculator.class);
int result=calculator.add(1, 2);
System.out.println(result);
result=calculator.div(4, 2);
System.out.println(result);
}
输出结果:
aroundMtethod
The method add begins with[1, 2]
The method add end
The method add result: 3
3
aroundMtethod
The method div begins with[4, 2]
The method div end
The method div result: 2
2
2.使用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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="t" class="com.spring.aop.xml.CalculatorImpl"></bean>
<!-- 配置切面的bean -->
<bean id="loggingAspect" class="com.spring.aop.xml.LoggingAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<aop:pointcut expression="execution(* com.spring.aop.xml.Calculator.*(int,int))" id="pointcut"/>
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
默认都是基于接口的代理,也就是aop:pointcut的expression中设置的往往都是关于接口的,当然也可以设置基于类的代理,只需要将<aop:config>的proxy-target-class属性设置为true就行了,默认是false。