AOP是Aspect-Oriented Programming的缩写,翻译为面向切面编程。我个人理解切面就是一个方面。
例子,一个接口里面有增删改查四个方法:
package com.stuspring.aop.impl;
/** * Created by bee on 17/5/15. */
public interface ArithmeticCalculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
实现类:
package com.stuspring.aop.impl;
import org.springframework.stereotype.Component;
/** * Created by bee on 17/5/15. */
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@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;
}
}
如果想给这四个方法分别加上前置日志功能,以其中一个为例,add方法变这样:
@Override
public int add(int i, int j) {
System.out.println("The method add begins with " +i+","+j);
int result=i+j;
return result;
}
手动给每个方法都加上固然可行,但是维护起来过于麻烦,面向切面编程就是为了解决这一问题。AOP的好处就是每个逻辑位于一个位置,代码不分散便于维护和升级,业务模块更简洁。
加一个日志切面:
package com.stuspring.aop.impl;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/** * Created by bee on 17/5/15. */
@Aspect
@Component
public class LoggingAspect {
/** * 前置通知 方法开始之前执行 * @param joinPoint */
@Before("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> agrs = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + agrs);
}
/** * 后置通知,方法执行完之后执行,不论方法是否出现异常 * 后置通知中不能访问目标方法的执行结果 */
@After("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " ends with " + args);
}
}
新建spring配置文件beans-aspect.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:aop="http://www.springframework.org/schema/aop" xmlns:content="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">
<content:component-scan base-package="com.stuspring.aop.impl"/>
<aop:aspectj-autoproxy/>
</beans>
附maven依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
@Before前置通知:方法开始之前执行。
/** * 前置通知 方法开始之前执行 * @param joinPoint */
@Before("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> agrs = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + agrs);
}
@After后置通知:方法开始之后执行,不论方法是否出现异常。
/** * 后置通知,方法执行完之后执行,不论方法是否出现异常 * 后置通知中不能访问目标方法的执行结果 */
@After("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " ends with " + args);
}
@AfterRunning:返回通知,在方法返回结果之后执行。
/** * 返回通知,在方法正常结束之后执行的代码 * 返回通知可以访问方法的返回值 */
@AfterReturning(value="execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" ends with "+result);
}
@AfterThrowing:异常通知,在方法抛出异常之后。
/** * 异常通知:在方法抛出异常之后执行 * @param joinPoint * @param e */
@AfterThrowing(value="execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e){
String methodName=joinPoint.getSignature().getName();
System.out.println("The method "+methodName+" occurs execution: "+e);
}
@Around:环绕通知,围绕方法执行。
/** * 环绕通知需要携带ProceedingJoinPoint类型的参数 * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法 * 环绕通知必须有返回值,返回值即为目标方法的返回值 * * @param pjd */
@Around("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd) {
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("--->The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//后置通知
System.out.println("--->The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method "+methodName+" occurs exception : "+e);
}
//后置通知
System.out.println("The Method "+methodName+" ends!");
return result;
}
切面的优先级可以用@Order注解指定,传入的整数值越小,优先级越高。
/** * 定义一个方法用于声明切入点表达式。一般地,该方法不需要再写其它代码。 */
@Pointcut("execution(public int com.stuspring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")
public void declareJoinPointExpression(){
}
/** * 前置通知 方法开始之前执行 * @param joinPoint */
@Before("declareJoinPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> agrs = Arrays.asList(joinPoint.getArgs());
System.out.println("The method " + methodName + " begins with " + agrs);
}
外部类引用可以使用报名加方法名的方法。
接口:
package com.stuspring.aop.fileimpl;
/** * Created by bee on 17/5/16. */
public interface ArithmeticCalculator {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
实现类:
package com.stuspring.aop.fileimpl;
/** * Created by bee on 17/5/16. */
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@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;
}
}
日志切面:
package com.stuspring.aop.fileimpl;
import org.aspectj.lang.JoinPoint;
/** * Created by bee on 17/5/16. */
public class LoggingAspect {
public void beforeMethod(JoinPoint joinPoint){
String methodName=joinPoint.getSignature().getName();
System.out.println("The method begins with "+methodName);
}
}
参数验证切面:
package com.stuspring.aop.fileimpl;
import org.aspectj.lang.JoinPoint;
/** * Created by bee on 17/5/16. */
public class ValidationAspect {
public void validateArgs(JoinPoint joinPoint){
System.out.println("validationMethod......");
}
}
Spring配置文件:
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!--配置bean-->
<bean id="arithmeticCalculator" class="com.stuspring.aop.fileimpl.ArithmeticCalculatorImpl"/>
<bean id="loggingAspect" class="com.stuspring.aop.fileimpl.LoggingAspect"/>
<bean id="validationAspect" class="com.stuspring.aop.fileimpl.ValidationAspect"/>
<aop:config>
<!--配置切面表达式-->
<aop:pointcut id="pointcut" expression="execution(public int com.stuspring.aop.fileimpl.ArithmeticCalculatorImpl.*(int,int))"/>
<!--配置切面通知-->
<aop:aspect ref="loggingAspect" order="2">
<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
</aop:aspect>
<aop:aspect ref="validationAspect" order="1">
<aop:before method="validateArgs" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
Main测试方法:
package com.stuspring.aop.fileimpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/** * Created by bee on 17/5/16. */
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("beans-aspectfile.xml");
ArithmeticCalculator calculator= (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
System.out.println(calculator.add(2,4));
}
}