Spring之AOP基本概念及配置AOP
为什么使用AOP
传统方法
AOP前前奏
首先考虑一个问题,假设我们要设计一个计算器,有如下两个需求:-在程序运行期间追踪正在放生的活动-希望计算器只能处理正数的运算通常我们会用如下代码进行实现:定义一个接口:
publicinterfaceArithmeticCalculator{
intadd(inti,intj);
intsub(inti,intj);
intmul(inti,intj);
intdiv(inti,intj);
}
实现类(在实现类中加入具体方法的实现,即正数的操作和日志功能,通过System.out.println输出实现):
publicclassArithmeticCalculatorLoggingImplimplementsArithmeticCalculator{
@Override
publicintadd(inti,intj){
System.out.println("Themethodaddbeginswith["+i+","+j+"]");
intresult=i+j;
System.out.println("Themethodaddendswith"+result);
returnresult;
}
@Override
publicintsub(inti,intj){
System.out.println("Themethodsubbeginswith["+i+","+j+"]");
intresult=i-j;
System.out.println("Themethodsubendswith"+result);
returnresult;
}
@Override
publicintmul(inti,intj){
System.out.println("Themethodmulbeginswith["+i+","+j+"]");
intresult=ij;
System.out.println("Themethodmulendswith"+result);
returnresult;
}
@Override
publicintdiv(inti,intj){
System.out.println("Themethoddivbeginswith["+i+","+j+"]");
intresult=i/j;
System.out.println("Themethoddivendswith"+result);
returnresult;
}
传统方法存在的问题
-代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀.每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.-代码分散:以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码.如果日志需求发生变化,必须修改所有模块.
使用动态代理解决上述问题
代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象.任何对原始对象的调用都要通过代理.代理对象决定是否以及何时将方法调用转到原始对象上.
/
动态代理
@authorMegustas
/
publicclassArithmeticCalculatorLoggingProxy{
//要代理的对象
privateArithmeticCalculatortarget;
publicArithmeticCalculatorLoggingProxy(ArithmeticCalculatortarget){
super();
this.target=target;
}
//返回代理对象
publicArithmeticCalculatorgetLoggingProxy(){
ArithmeticCalculatorproxy=null;
//代理对象由哪一个类加载器加载
ClassLoaderloader=target.getClass().getClassLoader();
//代理对象的类型,即其中有哪些方法
Class[]interfaces=newClass[]{ArithmeticCalculator.class};
//当调用代理对象其中的方法时,该执行的代码
InvocationHandlerh=newInvocationHandler(){
/
proxy:正在返回的那个代理对象,一般情况下,在invoke方法中不使用该对象
method:正在被调用的方法
args:调用方法传入的参数
/
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
throwsThrowable{
StringmethodName=method.getName();
//打印日志,此种方式对日志进行维护和更改就十分简洁
System.out.println("[before]Themethod"+methodName+"beginswith"+Arrays.asList(args));
//调用目标方法
Objectresult=null;
try{
//前置通知
//invoke:通过函数名反射相应的函数
result=method.invoke(target,args);
//返回通知,可以访问到方法的返回值
}catch(NullPointerExceptione){
e.printStackTrace();
//异常通知,可以访问到方法出现的异常
}
//后置通知.因为方法可以能会出异常,所以访问不到方法的返回值
//打印日志
System.out.println("[after]Themethodendswith"+result);
returnresult;
}
};
/
loader:代理对象使用的类加载器。
interfaces:指定代理对象的类型.即代理代理对象中可以有哪些方法.
h:当具体调用代理对象的方法时,应该如何进行响应,实际上就是调用InvocationHandler的invoke方法
以下是代理对象,不同于基本对象通过new生成
/
proxy=(ArithmeticCalculator)Proxy.newProxyInstance(loader,interfaces,h);
returnproxy;
}
但是写动态代理,难度却不小,不是很容易掌握。
AOP简介
AOP(Aspect-OrientedProgramming,面向切面编程):是一种新的方法论,是对传统OOP(Object-OrientedProgramming,面向对象编程)的补充.
AOP的主要编程对象是切面(aspect),而切面模块化横切关注点(即对象里放入的是一个个横切关注点的方法).
在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类.这样一来横切关注点就被模块化到特殊的对象(切面)里.
AOP的好处:每个事物逻辑位于一个位置,代码不分散,便于维护和升级业务模块更简洁,只包含核心业务代码.
这里写图片描述
这里的一个个需求,例如验证参数、日志功能等都是横切关注点,我们将横切关注点抽取出来
通过切面和业务逻辑的结合实现目标功能,即为面向切片编程。
AOP术语
切面(Aspect):横切关注点(一个个具体需求)(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice):切面必须要完成的工作(比如说切面需要完成验证,即切面中的每一个方法)
目标(Target):被通知的对象(即业务逻辑)
代理(Proxy):向目标对象应用通知之后创建的对象(将切面和目标混合)
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如ArithmethicCalculator#add()方法执行前的连接点,执行点为ArithmethicCalculator#add();方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。
AOP配置
用注解方式声明切面
首先介绍一个AOP框架,AspectJ,是Java社区里最完整最流行的AOP框架,在Spring2.0以上版本中,可以使用基于AspectJ注解来配置AOP。
要在spring应用中使用AspectJ注解,必须在classpath下包含AspectJ类库:aopalliance.jar、aspectj.weaver.jar和spring-aspects.jar,即导入包:
com.springsource.NET.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
将aopSchema添加到根元素中.
要在SpringIOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的XML元素
当SpringIOC容器侦测到Bean配置文件中的元素时,会自动为与AspectJ切面匹配的Bean创建代理.
要在Spring中声明AspectJ切面,只需要在IOC容器中将切面声明为Bean实例.当在SpringIOC容器中初始化AspectJ切面之后,SpringIOC容器就会为那些与AspectJ切面相匹配的Bean创建代理.在AspectJ注解中,切面只是一个带有@Aspect注解的Java类.通知是标注有某种注解的简单的Java方法.
AspectJ支持的5中类型的通知注解
@Before:前置通知,在方法执行之前执行
@After:后置通知,在方法执行之后执行
@AfterRunning:返回通知,在方法返回结果之后执行
@AfterThrowing:异常通知,在方法抛出异常之后
@Around:环绕通知,围绕着方法执行
/
日志切面
@authorMegustas
/
//把这个类声明为一个切面:首先需要把该类放入到IOC容器中,通过注解@Component、再声明为一个切面,通过注解@Aspect,并且在配置文件中加入配置
//通过Order注解来指定切面的优先级,优先级数字越小代表优先级越高,越先执行
@Order(2)
@Aspect
@Component
publicclassLoggingAspect{
//这个方法在哪些类的哪些方法前执行,通过注解来规定
//声明该方法是一个前置通知,在目标方法开始之前执行,".add"方法说明在add方法之前执行,"."则表示在包下所有方法之前执行
@Before("execution(publicintcom.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int,int))")
publicvoidbeforeMethwww.baiyuewang.netod(JoinPointjoinPoint){
StringmethodName=joinPoint.getSignature().getName();
List |
|