Spring AOP的实现
1、AOP的定义
AOP(Aspect Oriented Programming 面向切面编程)是一种通过运行期动态代理实现代码复用的机制,是对传统OOP(Object Oriented Programming,面向对象编程 )的补充。
2、AOP的配置方法
目前,Aspectj是Java社区里最完整最流行的AOP框架,在Spring 2.0以上版本中可以通过Aspectj注解或基于XML配置AOP。
3、AOP的实现
1、新建一个Java工程;
2、添加jar类库,如下图所示:
3、按下图结构创建工程目录
4、根据上图的框架写出如下代码:
ICalculatorService.java
package com.jd.calculator;
public interface ICalculatorService {
int mul(int a,int b);
int div(int a,int b);
}
CalculatorService.java
package com.jd.calculator;
import org.springframework.stereotype.Service;
@Service
public class CalculatorService implements ICalculatorService {
@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;
}
}
CalculatorAspect.java
package com.jd.calculator;
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;
@Aspect
@Component/*将CalculatorAspect类创建对象并保存到Spring容器*/
public class CalculatorAspect {
//前置增强:在目标方法执行之前执行
@Before("execution(int mul(int, int))")
public void before(JoinPoint jp) {
Object object = jp.getTarget();
Object [] args = jp.getArgs();
String name = jp.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method begins.");
System.out.println(object.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
}
//后置增强:在目标方法执行后执行,无论目标方法运行期间是否出现异常。注意:后置增强无法获取目标方法执行结果,可以在返回增强中获取
@After("execution(int mul(int, int))")
public void after(JoinPoint jp) {
Object object = jp.getTarget();
String name = jp.getSignature().getName();
System.out.println(object.getClass().getName()+":The "+name+" method ends.");
}
}
application.xml
<context:component-scan base-package="com.jd"></context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
测试类:Test
package com.jd.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jd.calculator.CalculatorService;
import com.jd.calculator.ICalculatorService;
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().getName());
int result = calculatorService.div(1, 1);
System.out.println("——>"+result);
}
}
Spring AOP的执行过程
1、根据Tset类中的main方法开始执行;
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
2、第一行的执行分析:
(1)创建Spring IOC容器,加载application.xml中的配置;
(2)application.xml中的配置分析com.jd”开头的包,并为其中加有@Component、@Service等注解的类创建对象
aop:aspectj-autoproxy
该配置作用:
a.如果创建目标对象的目标类中的方法与AspectJ切面中切入点表达式匹配,则自动为该目标对象生成动态代理对象,该代理对象默认使用JDK动态代理;
"true"/>时,则使用CGLib动态代理
执行过程:
@Aspect注解的类(CalculatorAspect);
②接着寻找该类中的所有方法;
③从而获取到该类中所有方法的注解;
表达式(execution(int mul(int , int ));
⑤然后再此检查Spring能扫描到的所有类,找到与表达式能够匹配的方法所对应的类;
⑥最后为匹配到的类创建动态对象。
(3)举例:
@Aspect注解;
before和after方法;
@Before和@After的注解
表达式(execution(int mul(int , int ));
CalculatorService;
CalculatorService类创建动态对象。
(4)注意④中的execution表达式中的参数不能具体化,要么写成execution(int mul(int , int )或者execution(int mul(..)
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
3、第二行的执行分析
获取代理对象
System.out.println(calculatorService.getClass().getName());
4、第三行的执行分析:通过反射获得动态代理对象的名字
int result = calculatorService.div(1, 1);
5、第四行的执行分析:
先执行CalculatorAspect类中带有@Before注解的前置增强,再通过动态代理对象执行目标方法(详情移至),最后执行CalculatorAspect类中带有@After注解的后置增强,将返回的值赋给result。
System.out.println("——>"+result);
6、第五行的执行分析:最后输出result结果
补充:
(1)为什么没有引asm和CGLib的jar包,还可以用CGLib动态代理?
原因:
①从Spring 3.2开始spring-core-xxx.jar包已集成CGLib和ASM 相关jar包,所以Spring工程不需再额外引入这些jar包,如下:
②即使proxy-target-class设置为false,如果目标类没有声明接口,则Spring将自动使用CGLib生成代理对象。
(2)当aop:aspectj-autoproxy标签内的proxy-target-class标签属性为"false"时,
applicationContext.getBean(ICalculatorService.class)为什么不能写作
applicationContext.getBean(CalculatorService.class)?
原因:
①proxy-target-class标签属性为"false"表示使用JDK生成代理对象,此时创建代理对象的代理类与创建目标对象的类之间没有继承关系,所以不能写成applicationContext.getBean(CalculatorService.class),但是由于此时创建代理对象的代理类实现了ICalculatorService接口,所以可以写成applicationContext.getBean(ICalculatorService.class);
②当proxy-target-class标签属性为"true"表示使用CGLib生成代理对象,此时创建代理对象的代理类继承创建目标对象的类,所以可以写作applicationContext.getBean(CalculatorService.class)