为什么
首先创建一个CalculatorService类和一个Test类。
CalculatorService:
package com.jd.calculator.service;
import org.springframework.stereotype.Service;
@Service
public class CalculatorService implements ICalculatorService {
@Override
public int mul(int a, int b) {
System.out.println(this.getClass().getName()+":The mul method begins.");
System.out.println(this.getClass().getName()+":Parameters of the mul method: ["+a+","+b+"]");
int result = a*b;
System.out.println(this.getClass().getName()+":Result of the mul method:"+result);
System.out.println(this.getClass().getName()+":The mul method ends.");
return result;
}
@Override
public int div(int a, int b) {
System.out.println(this.getClass().getName()+":The div method begins.");
System.out.println(this.getClass().getName()+":Parameters of the div method: ["+a+","+b+"]");
int result = a/b;
System.out.println(this.getClass().getName()+":Result of the div method:"+result);
System.out.println(this.getClass().getName()+":The div method ends.");
return result;
}
}
Test:
import com.jd.calculator.service.ICalculatorService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
ICalculatorService calculateService = applicationContext.getBean(ICalculatorService.class);
int result = calculateService.add(1,1);
System.out.println(result);
applicationContext.close();
}
}
可以注意到CalculatorService类中有很多冗余代码,如果可以简化代码将会好很多。因此我们尝试将日志处理变成一个新的方法。
怎么做
AOP(Aspect Oriented Programming 面向切面编程)是一种指在程序运行期间动态的将某段代码切入到指定方法的指定位置进行运行的编程方式,这种编程方式实现了代码复用,是对传统OOP(Object Oriented Programming,面向对象编程 )的补充。目前,Aspect是Java社区里最完整最流行的AOP框架,在Spring 2.0以上版本中可以通过Aspect注解或基于XML配置AOP。
首先需要添加maven依赖。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
创建一个CalculatorAspect类并修改CalculatorService类和Test类代码:
CalculatorAspect:
package com.jd.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CalculatorAspect {
@Before("execution(public int com.jd.calculator.service.CalculatorService.*(..))")//切入点表达式
public void before(JoinPoint jp){
Object [] args = jp.getArgs();
String name = jp.getSignature().getName();
System.out.println("The "+name+" method begins");
System.out.println("The parameters of the "+name+" method are "+args[0]+","+args[1]);
}
@After("execution(public int com.jd.calculator.service.CalculatorService.*(..))")
public void after(JoinPoint jp){
String name = jp.getSignature().getName();
System.out.println("The "+name+" method ends");
}
@AfterReturning(value = "execution(public int com.jd.calculator.service.CalculatorService.*(..))",returning = "obj")
public void afterReturning(JoinPoint jp,Object obj){
String name = jp.getSignature().getName();
System.out.println("The "+name+" method result:"+obj);
}
@AfterThrowing(value = "execution(public int com.jd.calculator.service.CalculatorService.*(..))", throwing = "e")
public void afterThrowing(JoinPoint jp,Throwable e){
System.out.println("#############"+e.getMessage());
e.printStackTrace();
}
}
CalculatorService:
package com.jd.calculator.service;
import org.springframework.stereotype.Service;
@Service
public class CalculatorService implements ICalculatorService{
@Override
public int add(int a, int b) {
int result = a+b;
return result;
}
@Override
public int mul(int a, int b) {
int result = a*b;
return result;
}
@Override
public int div(int a, int b) {
int result = a/b;
return result;
}
}
Test:
import com.jd.calculator.service.ICalculatorService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
ICalculatorService calculatorService = applicationContext.getBean(ICalculatorService.class);
System.out.println(calculatorService.getClass().getSuperclass().getName());
System.out.println(calculatorService.getClass().getName());
int result = calculatorService.div(1,1);
System.out.println(result);
}
}
在Spring配置文件中添加配置,该配置作用:如果创建目标对象的目标类中的方法与Aspect切面中切入点表达式匹配,则自动为该目标对象生成动态代理对象,该代理对象默认使用JDK动态代理。
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
Test类中调用div方法时,先生成CalculatorService类的代理类,包含CalculatorService类中的所有方法所有参数,调用的是代理类中的div方法,而非CalculatorService类中的div方法。
当配置为添加proxy-target-class="true"时,则使用CGLib动态代理。
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
Test类中调用div方法时,先生成CalculatorService类的子类,调用时直接调用子类中继承的div方法。
可以注意到,有@Before,@After,@AfterReturning,@AfterThrowing四种注解,执行过程如下:
try {
try {
doBefore();// @Before注解所修饰的方法
method.invoke();// 执行目标对象内的方法
} finally {
doAfter();// @After注解所修饰的方法
}
doAfterReturning();// @AfterReturning注解所修饰的方法
} catch (Exception e) {
doAfterThrowing();// @AfterThrowing注解所修饰的方法
}
首先,在执行被调用的方法之前,执行@Before注解的方法,然后执行被调用的方法。不论两方法是否报错,一定会随后执行@After方法,再执行@AfterReturning方法,倘若三个方法都没有错误,则不执行@AfterThrowing方法,反之执行;倘若@Before或被调用方法出现错误,由于没有catch,不执行@AfterReturning方法,但执行@After和@AfterThrowing方法。
在CalculatorAspect类中可以注意到切入点表达式有所重复,因此可以借助@Pointcut实现简化。
package com.jd.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CalculatorAspect {
@Pointcut("execution(public int com.jd.calculator.service.CalculatorService.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void before(JoinPoint jp){
Object [] args = jp.getArgs();
String name = jp.getSignature().getName();
System.out.println("The "+name+" method begins");
System.out.println("The parameters of the "+name+" method are "+args[0]+","+args[1]);
}
@After("pointcut()")
public void after(JoinPoint jp){
String name = jp.getSignature().getName();
System.out.println("The "+name+" method ends");
}
@AfterReturning(value = "pointcut()",returning = "obj")
public void afterReturning(JoinPoint jp,Object obj){
String name = jp.getSignature().getName();
System.out.println("The "+name+" method result:"+obj);
}
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint jp,Throwable e){
System.out.println("#############"+e.getMessage());
e.printStackTrace();
}
}
@Around注解是所有注解的结合体。
@Around("pointcut()")
public Object around(ProceedingJoinPoint jp){
try{
String name = jp.getSignature().getName();
Object result = null;
try {
Object [] args = jp.getArgs();
System.out.println("The "+name+" method begins");
System.out.println("The parameters of the "+name+" method are "+args[0]+","+args[1]);
result = jp.proceed();
}finally {
System.out.println("The "+name+" method ends");
}
System.out.println("The "+name+" method result:"+result);
return result;
}catch (Throwable e){
System.out.println("#############"+e.getMessage());
}
return -1;
}
可以看到@Around注解可以实现上述@Before,@After,@AfterReturning,@AfterThrowing四种注解的功能。