Spring之AOP基本概念及配置AOP.docx

上传人:b****1 文档编号:13496069 上传时间:2023-06-14 格式:DOCX 页数:16 大小:139.59KB
下载 相关 举报
Spring之AOP基本概念及配置AOP.docx_第1页
第1页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第2页
第2页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第3页
第3页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第4页
第4页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第5页
第5页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第6页
第6页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第7页
第7页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第8页
第8页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第9页
第9页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第10页
第10页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第11页
第11页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第12页
第12页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第13页
第13页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第14页
第14页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第15页
第15页 / 共16页
Spring之AOP基本概念及配置AOP.docx_第16页
第16页 / 共16页
亲,该文档总共16页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Spring之AOP基本概念及配置AOP.docx

《Spring之AOP基本概念及配置AOP.docx》由会员分享,可在线阅读,更多相关《Spring之AOP基本概念及配置AOP.docx(16页珍藏版)》请在冰点文库上搜索。

Spring之AOP基本概念及配置AOP.docx

Spring之AOP基本概念及配置AOP

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=i*j;

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元素

aspectj-autoproxy>

当SpringIOC容器侦测到Bean配置文件中的

aspectj-autoproxy>元素时,会自动为与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))")

publicvoidbeforeMethod(JoinPointjoinPoint){

StringmethodName=joinPoint.getSignature().getName();

Listargs=Arrays.asList(joinPoint.getArgs());

System.out.println("Themethod"+methodName+"beginswith"+args);

}

//后置通知:

在目标方法执行后(无论是否发生异常),执行的通知

//在后置通知中还不能访问目标目标方法执行的结果,执行结果在返回通知中进行访问

@After("execution(*com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.*(int,int))")

publicvoidafterMethod(JoinPointjoinPoint){

StringmethodName=joinPoint.getSignature().getName();

System.out.println("Themethod"+methodName+"end");

}

/*

*返回通知:

在方法正常结束后执行的代码,返回通知是可以访问到方法的返回值的

*/

@AfterReturning(value="execution(publicintcom.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int,int))",

returning="result")

publicvoidafterReturning(JoinPointjoinPoint,Objectresult){

StringmethodName=joinPoint.getSignature().getName();

System.out.println("Themethod"+methodName+"endswith"+result);

}

/**

*在目标方法出现异常时会执行的代码

*可以访问到异常对象,且可以指定在出现特定异常时再执行通知代码

*例如Exceptionex,NullPointerExceptionex,可以指定不同种类的异常,当是指定的异常种类时执行

*@paramjoinPoint

*@paramex

*/

@AfterThrowing(value="execution(publicintcom.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.div(int,int))",

Twing="ex")

publicvoidafterThrowing(JoinPointjoinPoint,Exceptionex){

StringmethodName=joinPoint.getSignature().getName();

System.out.println("Themethod"+methodName+"occursexcetion"+ex);

}

/**

*环绕通知需要携带ProceedingJoinPoint类型的参数

*环绕通知类似于动态代理的全过程:

ProceedingJoinPoint类型的参数可以决定是否执行目标方法

*并且环绕通知必须有返回值,返回值即为目标方法的返回值(类似于动态代理)

*最强的,前置、后置、返回与异常通知都可以,但是并不代表是最常用的

*@parampjd

*/

@Around("execution(*com.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int,int))")

publicObjectaroundMethod(ProceedingJoinPointpjd){

Objectresult=null;

StringmethodName=pjd.getSignature().getName();

//执行目标方法

try{

//前置通知

System.out.println("Themethod"+methodName+"beginswith"+Arrays.asList(pjd.getArgs()));

result=pjd.proceed();

//后置通知

System.out.println("Themethod"+methodName+"endswith"+result);

}catch(Throwablee){

//异常通知

System.out.println("Themethod"+methodName+"occursexcetion"+e);

}

//后置通知

System.out.println("Themethod"+methodName+"ends");

returnresult;

}

}

Bean的配置:

--自动扫描的包-->

component-scanbase-package="com.atguigu.spring.aop">

component-scan>

--使AspectJ的注解起作用-->

aspectj-autoproxy>

aspectj-autoproxy>

验证方法:

publicclassMain{

publicstaticvoidmain(String[]args){

//1.创建IOC容器

ClassPathXmlApplicationContextctx=newClassPathXmlApplicationContext("applicationContext.xml");

//2.从IOC容器中获取bean实例

ArithmeticCalculatorarithmeticCalculator=(ArithmeticCalculator)ctx.getBean(ArithmeticCalculator.class);

//3.调用bean的方法

intresult1=arithmeticCalculator.add(3,6);

System.out.println("result1:

"+result1);

//intresult2=arithmeticCalculator.div(1000,0);//通过异常通知显示

//System.out.println("result2:

"+result2);

//4.关闭容器

ctx.close();

}

}

指定切面的优先级及重用切入点

指定切面的优先级

实例可以参照上诉代码的@Order

在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的.

切面的优先级可以通过实现Ordered接口或利用@Order注解指定.

实现Ordered接口,getOrder()方法的返回值越小,优先级越高.

若使用@Order注解,序号出现在注解中

@Aspect

@Order(0)

publicclassCalculaotorValidationAspect{}

@Aspect

@Order

(1)

publicclassCalculaotorLoggingAspect{}

重用切入点

在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式.但同一个切点表达式可能会在多个通知中重复出现.

在AspectJ切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法.切入点的方法体通常是空的,因为将切入点定义与应用程序逻辑混在一起是不合理的.

切入点方法的访问控制符同时也控制着这个切入点的可见性.如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中.在这种情况下,它们必须被声明为public.在引入这个切入点时,必须将类名也包括在内.如果类没有与这个切面放在同一个包中,还必须包含包名.

其他通知可以通过方法名称引入该切入点.

(总结就是一句话:

将切入点的表达式“封装”为一个方法,通过方法调用来实现)

同类中使用:

/**

*实现重用切面表达式

*定义一个方法,用于声明切入点表达式。

一般该方法中不需要再填入其他的代码

*使用@Pointcut来声明切入点表达式

*/

@Pointcut("execution(publicintcom.atguigu.spring.aop.impl.ArithmeticCalculatorImpl.add(int,int))")

publicvoiddeclareJointPointExpression(){

}

 

@Before("declareJointPointExpression()")

publicvoidbeforeMethod1(JoinPointjoinPoint){

StringmethodName=joinPoint.getSignature().getName();

Lis

展开阅读全文
相关搜索
资源标签

当前位置:首页 > PPT模板 > 商务科技

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2