您现在的位置:首页 >> 前端 >> 内容

html开发教程之springaop解析

时间:2017/11/17 11:11:48 点击:

  核心提示:Spring中AOP实现。1.什么是SpringAOP什么是aop:Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的统一维护的一种技术主要...

Spring中AOP实现。

1.什么是SpringAOP

什么是aop:Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的

统一维护的一种技术

主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等

2.SpringAOP框架的用途

提供了声明的企业服务,特别是EJB的替代服务的声明

允许用户控制自己的方面,以完成OOP和AOP的互补使用

OOP:模拟真实的世界,一切皆是对象

3.AOP的实现方式

下边这两种Spring都是支持的

3.1预编译

-AspectJ 完整的面向切面编程解决方案--》spring不是完整的解决方案,不过spring提供比较好的实现方式,当然spring是同时也是支持这种方式的,这也是一种常用的方式

3.2运行期间动态代理(JDK动态代理,CGLib动态代理)

-SpringAop,JbossAop

Spring的AOP使用纯java实现,无需特殊的编译过程,不需要控制类的加载器层次,目前只支持方法的执行的连接点(通知Spring Bean某个方法执行)

不是为了提供完整AOP实现;而是侧重于一种AOP于IOC容器之间的整合,SpringAOP不会AspectJ(完整的AOP解决方案)竞争

Spring没有使用AspectJ的时候,也可以通过如下方式实现AOP

Spring AOP 默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者集合)都可以被代理

Spring AOP 中也可以使用CGLIB代理(如果一个业务对象没有实现一个接口)

有接口的:使用JDK的动态里

无接口的:使用CGLIB代理

4.SpringAOP中的一些概念

在实际使用SpringAOP之前,了解他的概念是必不可少的一个过程,

SpringAOP主要围绕以下概念展开:

切面(Aspect)一个关注点的模块化,这个关注点可能会横切多个对象

连接点(Joinpoint)程序执行过程中某个特定的连接点

通知(Advice) 在切面的某个特的连接点上执行的动作

切入点(Pointcut)匹配连接的断言,在Aop中通知和一个切入点表达式关联

引入(Intruduction) 在不修改类代码的前提下,为类添加新的方法和属性

目标对象(Target Object) 被一个或者多个切面所通知的对象

Aop代理(AOP Proxy) AOP框架创建的对象,用来实现切面契约(aspect contract)(包括方法执行等)

织入(Weaving)把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,氛围:编译时织入,类加载时织入,执行时织入

通知类型Advice:

前置通知(before advice) 在某个连接点(jion point)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)

返回后通知(after returning advice)在某个连接点(jion point)正常执行完后执行通知

抛出异常通知(after throwing advice) 在方法异常退出时执行的通知

后通知(after(finally) advice)在方法抛出异常退出时候的执行通知(不管正常返回还是异常退出)

环绕通知(around advice) 包围一个连接点(jion point)的通知

切入点Pointcut:SpringAOP占时仅仅支持方法的连接点

execution(public * *(..)) 切入点为执行所有的public方式时

execution(* set*(..)) 切入点执行所有的set开始的方法时

execution(* com.xyz.service.Account.*(..)) 切入点执行Account类的所有方法时

execution(* com.xyz.service..(..))切入点执行com.xyz.service包下的所有方法时

execution(* com.xyz.service...(..)) 切入点执行com.xyz.service包以及其子包的所有的方法时

上边这种方式aspectj和springaop通用的,其他方式可以自己查找资料

5.SpringAOP实现

上边的一些概念,看过后可能还是不懂,可以自行查阅资料,下边我也会说明

我们在Java项目开发中,AOP常见的配置有如下3种:

a.基于SpringAOP容器的实现,使用AspectJ(Spring封装了AspectJ的使用),这是一种比较常用的方式,可以很容易看清代码结构

b.运行期间的动态代理实现,这是一种比较老的实现方式,比较繁琐

c.注解实现,这种方式开发起来简单,但是不利于后期维护,比如说很难找出你所有使用了SpringAOP注解的地方

这里我主要介绍第一种,和第二种的使用

5.1SpringAOP中的容器实现

5.1.1 前置通知before

某个需要切入的方法之前执行切面中的方法 Spring所有的切面通知都必须放在一个内(可以配置多个元素),每一个可以包含pointcut,adviso

和aspect元素(注意这些元素的出现是由顺序的)

配置文件:spring-aop-schema-advice.xml

声明了切面类,当切入点中匹配到了类名包含BIZ字符串的类时,选取面类中的一个方法,在选取某种Apice通知,切入该方法的执行过程 其中:aop:aspect 中id的值任意(表意性强) ref的值为切面类的id值 aop:pointcut 中expression写切入点表达式,说明那个方法可能需要做切入点 aop:before 中method的值为切面中的方法名说明切入那个方法,pointcut-ref的值为切入点的id值 [html] view plain copy

xsi:schemaLocation="https://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttps://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

切面类:

切面类中的某个方法,一般是用于切入业务类中的某个方法在某种状态时切入 [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

}

业务类: [java] view plain copy

//业务类publicclassAspectBiz{

publicvoidbiz(){

System.out.println("Aspectbiz");}

}

测试: [java] view plain copy

@TestpublicvoidaspectBefore(){

ClassPathXmlApplicationContextac=newClassPathXmlApplicationContext("com/briup/spring/chap4/spring-aop-schema-advice.xml");AspectBizaspectBiz=(AspectBiz)ac.getBean("aspectBiz");

aspectBiz.biz();} 结果: [plain] view plain copy

aspectbeforeAspectbiz

通过结果可以看出,前置通知在业务类AspectBiz的方法biz执行之前执行了before方法

5.1.2后置通知after-returning

某个需要切入的方法执行完成之后执行切面中指定的方法 配置文件:spring-aop-schema-advice.xml

[html] view plain copy

切面类: 添加afterReturning方法 [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

publicvoidafterReturning(){

System.out.println("aspectafterReturning");}

} 结果: [plain] view plain copy

aspectbeforeAspectbiz

aspectafterReturning 结果可以看出,后置通知会在业务方法的执行之后

5.1.3异常通知after-throwing

在方法抛出异常后的通知 配置文件:spring-aop-schema-advice.xml [html] view plain copy

切面类: [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

publicvoidafterReturning(){

System.out.println("aspectafterReturning");}

publicvoidafteThrowing(){

System.out.println("aspectafteThrowing");}

}

业务类: 修改为如下: [java] view plain copy

publicvoidbiz(){System.out.println("Aspectbiz");

thrownewRuntimeException();//出现异常的时候afteThrowing才会执行}

测试和结果: [plain] view plain copy

aspectbeforeAspectbiz

aspectafteThrowing上边的结果由于抛出异常后,不会正常的返回所有没有aspectafterReturning输出

5.1.4最终通知after(finally) advice

不管方法是否会抛出异常都会通知,就像try...catch..finallly 配置文件:spring-aop-schema-advice.xml [html] view plain copy

切面类: [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

publicvoidafterReturning(){

System.out.println("aspectafterReturning");}

publicvoidafteThrowing(){

System.out.println("aspectafteThrowing");}

publicvoidafter(){

System.out.println("aspectafter(finally)");}

}

业务类: [html] view plain copy

//业务类publicclassAspectBiz{

publicvoidbiz(){

System.out.println("Aspectbiz");thrownewRuntimeException();

}} 测试结果: [plain] view plain copy

aspectbeforeAspectbiz

aspectafteThrowingaspectafter(finally) 从结果中可以看出,抛出异常后,切面类中的after方法还是可以执行

[java] view plain copy

如果业务类如下:没有异常,publicclassAspectBiz{

publicvoidbiz(){System.out.println("Aspectbiz");

}

} 结果: [plain] view plain copy

aspectbeforeAspectbiz

aspectafter(finally)

由以上结果可以看到,不管怎么样after方法还是会执行,很像try...catch..finally

5.1.5环绕通知around

在方法的执行前后执行通知 配置文件:spring-aop-schema-advice.xml [html] view plain copy

切面类: 可以看出,环绕方法的第一参数必须四ProceedingJoinPoint类型 [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

publicvoidafterReturning(){

System.out.println("aspectafterReturning");}

publicvoidafteThrowing(){

System.out.println("aspectafteThrowing");}

publicvoidafter(){

System.out.println("aspectafter(finally)");}

publicvoidaround(ProceedingJoinPointjoinPoint){

Objectobject=null;try{

System.out.println("aspectaround1");//方法执行前object=joinPoint.proceed();//代表业务方法的执行

System.out.println("aspectaround1");//方法执行后}catch(Throwablee){

e.printStackTrace();}

}}

测试结果: [plain] view plain copy

aspectaround1Aspectbiz

aspectaround1

从结果可以看出,joinPoint.proceed其实就是执行业务方法,我们可以在其之前做和之后做一些操作

5.1.6通知中的参数传递 这里我们列举环绕通知中的参数参数传递,其他通知也是同样的方式。 配置文件:spring-aop-schema-advice.xml [html] view plain copy

xsi:schemaLocation="https://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttps://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

其中,通知中也可以单独指定的pointcut,切入点中,我们单独指定了init方法,且指定了方法的参数类型,和方法参数的名字,其中方法参数名字,可以更实际的方法参数名字不同,比如说init(String biz,int t) 都是可以匹配到的,不过为了维护方便,最好还是都一样。

切面类: [java] view plain copy

/**声明一个切面类

**/publicclassMyAspect{

publicvoidbefore(){

System.out.println("aspectbefore");}

publicvoidafterReturning(){

System.out.println("aspectafterReturning");}

publicvoidafteThrowing(){

System.out.println("aspectafteThrowing");}

publicvoidafter(){

System.out.println("aspectafter(finally)");}

publicvoidaround(ProceedingJoinPointjoinPoint){

Objectobject=null;try{

System.out.println("aspectaround1");//方法执行前object=joinPoint.proceed();//代表业务方法的执行

System.out.println("aspectaround2");//方法执行后}catch(Throwablee){

e.printStackTrace();}

}

//AOP中参数的传递publicvoidaroundInit(ProceedingJoinPointjoinPoint,StringbizName,inttimes){

System.out.println(bizName+"--"+times);Objectobject=null;

try{System.out.println("aspectaround1");//方法执行前

object=joinPoint.proceed();//代表业务方法的执行System.out.println("aspectaround1");//方法执行后

}catch(Throwablee){e.printStackTrace();

}}

}

业务类: [java] view plain copy

publicclassAspectBiz{

publicvoidbiz(){System.out.println("Aspectbiz");

}

publicvoidinit(StringbizName,inttimes){System.out.println("aspectBizinit:"+bizName+""+times);

}

}

测试: [java] view plain copy

@TestpublicvoidaspectAround(){

ClassPathXmlApplicationContextac=newClassPathXmlApplicationContext("com/briup/spring/chap4/spring-aop-schema-advice.xml");AspectBizaspectBiz=(AspectBiz)ac.getBean("aspectBiz");

aspectBiz.init("init",3);} 测试结果: [plain] view plain copy

aspectbeforeinit--3

aspectaround1aspectBizinit:init3

aspectaround1aspectafter(finally)

aspectafterReturningAfterClass标注的方法会最后执行 可以看到,参数比顺利的传送过去

6.Advisor

Advisor就像一个小的自包含,只有一个advice切面通过一个bean标识,并且必须实现一个advice接口,同时advisor也可以很好的利用aspectJ的切入点表达式Spring通过配置文件中 元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用,用于事务控制 配置文件使用案例: [html] view plain copy

xmlns:aop="https://www.springframework.org/schema/aop"xsi:schemaLocation="https://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans-3.2.xsd

https://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

classpath:hibernate.cfg.xml

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

关于spring的事务控制将会在另外一篇文章中介绍。

7.SpringAOP代理实现

这种实现方式,主要使用动态代理技术。有接口的:使用JDK的动态里,无接口的:使用CGLIB代理 这种方式不是很常用

7.1.1ProxyFactoryBean

1.使用SpringAOP代理最关键的是使用org.springframework.aop.framework.ProxyFactoryBean,可以完全控制切入点和通知advice以及他们的顺序 2.使用ProxyFacotryBean或者其他的IOC相关类来创建AOP代理的最重要的好处就是通知切入点可以由IOC来管理 3.被代理的类没有实现任何接口,使用CGLIB代理,否者使用JDK代理 4.通过设置ProxyTargetClass为true可以强制使用CGLIB,(无论是否实现接口) 5.如果目标类实现了一个或者多个接口,那么创建代理的类型将依赖于ProxyFactoryBean的配置 6.如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理被创建 7.如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现类一个或多个接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理

7.1.2使用代理实现环绕通知around

配置文件:

[html] view plain copy

advice

注意: 上边的可以不用写,Spring的ProxyFactoryBean将会自动检测到 如果被代理的对象target实现了多个接口可以按照如下配置: [html] view plain copy

com.xxx.spring.aop.bean.UserDao

value中可以写多个接口

同样:通知interceptorNames如果只用一个通知可以写成

如果有多个通知可以写成: [html] view plain copy

advice

但是 通知是不可以省略的,同时推荐结合的写法

上边的委托类即使普通的实现类:即ProxyFactoryBean被代理的对象

上边的通知配置

ProxyFactoryBean的配置 需要配置被代理的目标对象,通知,目标类实现的接口(可以省略,被ProxyFactoryBean自动识别)

切面通知实现类 实现MethodInterceptor就可以实现环绕通知,我们可以在方法的目标对象类的方法执行前后加入处理逻辑 [java] view plain copy

importjava.util.Date;importorg.aopalliance.intercept.MethodInterceptor;

importorg.aopalliance.intercept.MethodInvocation;

publicclassAdviceTestimplementsMethodInterceptor{

@OverridepublicObjectinvoke(MethodInvocationmethod)throwsThrowable{

System.out.println("方法开始执行"+newDate());method.proceed();

System.out.println("方法执行完毕"+newDate());returnnull;

}}

目标类: [java] view plain copy

//委托类publicclassUserDaoImplimplementsUserDao{

@Override

publicvoidsaveUser(){System.out.println("保存用户");

}

@OverridepublicvoiddeleteUser(){

System.out.println("删除用户");}

@Override

publicvoidupdateUser(){System.out.println("更新用户");

}

} 接口: [java] view plain copy

publicinterfaceUserDao{publicabstractvoidsaveUser();

publicabstractvoiddeleteUser();publicabstractvoidupdateUser();

}

测试: [java] view plain copy

@Testpublicvoidadvice(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/advice.xml");UserDaouserDao=factory.getBean("proxy",UserDao.class);

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();} 测试结果: [plain] view plain copy

方法开始执行SunSep1121:02:12CST2016保存用户

方法执行完毕SunSep1121:02:12CST2016方法开始执行SunSep1121:02:12CST2016

删除用户方法执行完毕SunSep1121:02:12CST2016

方法开始执行SunSep1121:02:12CST2016更新用户

方法执行完毕SunSep1121:02:12CST2016

7.1.3使用动态代理实现前置通知before

配置文件: [html] view plain copy

com.xxx.spring.aop.bean.UserDao

before

切面前置通知实现类: 实现MethodBeforeAdvice [java] view plain copy

importjava.lang.reflect.Method;importorg.springframework.aop.MethodBeforeAdvice;

publicclassBeforeTestimplementsMethodBeforeAdvice{

@Overridepublicvoidbefore(Methodmethod,Object[]obj,Objectobject)

throwsThrowable{System.out.println("version1.0authortom"+method.getName()+"isexecute");

}

}

测试: [java] view plain copy

@Testpublicvoidbefore(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/before.xml");UserDaouserDao=factory.getBean("proxy",UserDao.class);

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();}

结果: [plain] view plain copy

version1.0authortomsaveUserisexecute保存用户

version1.0authortomdeleteUserisexecute删除用户

version1.0authortomupdateUserisexecute更新用户

7.1.4后置通知afterReturning

配置文件: [html] view plain copy

com.xxx.spring.aop.bean.UserDao

after

切面后置通知: 实现 AfterReturningAdivce接口 [java] view plain copy

importjava.lang.reflect.Method;importorg.springframework.aop.AfterReturningAdvice;

publicclassAfterTestimplementsAfterReturningAdvice{

@OverridepublicvoidafterReturning(Objectarg0,Methodarg1,Object[]arg2,

Objectarg3)throwsThrowable{System.out.println(arg1.getName()+"isover!");

}} 测试: [java] view plain copy

@Testpublicvoidafter(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/after.xml");UserDaouserDao=factory.getBean("proxy",UserDao.class);

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();} 测试结果: [plain] view plain copy

保存用户saveUserisover!

删除用户deleteUserisover!

更新用户updateUserisover!

7.1.5异常通知throw

配置文件: [html] view plain copy

com.briup.spring.aop.bean.UserDao

throws

异常通知切面: 参数中必须有Throwable的子类,前边的参数afterThrowing([Method,args,target],subclassOfThrowable)[]号中的参数可选 [java] view plain copy

importjava.lang.reflect.Method;

importorg.springframework.aop.ThrowsAdvice;

publicclassThrowsApiceTestimplementsThrowsAdvice{

publicvoidafterThrowing(Methodmethod,Object[]args,Objecttarget,Exceptionex)throwsThrowable{//ThrowablesubclassSystem.out.println("afterThrowing2...."+method.getName()+""+target.getClass().getName());

}

}

实现类: [java] view plain copy

@OverridepublicvoidsaveUser()throwsRuntimeException{

System.out.println("保存用户");thrownewRuntimeException();

}

测试: [java] view plain copy

@TestpublicvoidthrowTest(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/throw.xml");UserDaouserDao=factory.getBean("proxy",UserDao.class);

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();} 结果: [plain] view plain copy

保存用户afterThrowing2....saveUsercom.xxx.spring.aop.bean.UserDaoImpl

7.1.6切入点配置pointcut

使用代理方式也可以配置切入点 NameMatchMethodPointcut,根据方法的名字进行匹配 mappedNames匹配的方法名集合

配置文件: [html] view plain copy

sa*

com.briup.spring.aop.bean.UserDao

defaultAdvisorthrows

如上边切入点配置: [html] view plain copy

sa*

切面: [html] view plain copy

7.1.7使用匿名的代理对象

使用匿名的代理对象可以将bean的配置到代理里边,这样就不用为target目标对象配置单独的对象,这样可以直接避免目标对象 配置文件: [html] view plain copy

sa*

com.xxx.spring.aop.bean.UserDao

defaultAdvisorthrows

7.1.8IntroductionInterceptor

Introduction是个特别的Advice,可以在不修改代码的基础上添加一些方法,可以参见 https://www.iteedu.com/webtech/j2ee/springdiary/35.php https://go12345.iteye.com/blog/352745

8.自动代理实现

8.1.BeanNameAutoProxyCreator

Spring允许使用自动代理的bean定义,他可以自动代理选定bean,这样我么就不用为代理对象声明接口,或者没有实现接口的时候,使用CGLIB代理

配置文件: [html] view plain copy

targ*

before

通过,自动代理,proxy会自动帮我们代理所有以targ开头的目标委托类

实现类: [java] view plain copy

//委托类publicclassUserDaoImplimplementsUserDao{

@OverridepublicvoidsaveUser(){

System.out.println("保存用户");}

@Override

publicvoiddeleteUser(){System.out.println("删除用户");

}

@OverridepublicvoidupdateUser(){

System.out.println("更新用户");}

}

测试: [java] view plain copy

@TestpublicvoidautoAdvisor(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/autoAdvisor.xml");UserDaouserDao=factory.getBean("target",UserDao.class);//autoAdvisor只能通过委托类的名字来拿

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();}

结果: [plain] view plain copy

version1.0authortomsaveUserisexecute保存用户

version1.0authortomdeleteUserisexecute删除用户

version1.0authortomupdateUserisexecute更新用户

8.2DefaultAdvisorAutoProxyCreator

使用DefaultAdvisorAutoProxyCreator我们可以不用显示的指定advisor的bean定义

[html] view plain copy

.*deleteUser

测试: [java] view plain copy

@TestpublicvoidautoProxyByName(){

BeanFactoryfactory=newClassPathXmlApplicationContext("com/xxx/spring/chap2/autoProxyByName.xml");UserDaouserDao=factory.getBean("target",UserDao.class);

userDao.saveUser();userDao.deleteUser();

userDao.updateUser();}

结果: [java] view plain copy

保存用户version1.0authortomdeleteUserisexecute

删除用户更新用户

Tags:HT TM ML L开 
作者:网络 来源:u013231550