面向切面-AOP1.AOP简介AOP是OOP的延续,是AspectOrientedProgramming的缩写,意思是面向方面(切面)编程。它将分布在各个类中具有相同功能的代码片段整合到一起,由单独的功能模块完成,不仅减少了代码的重复量,降低了耦合,也提高了代码的可维护性。AOP的作用就是在可以顺序执行的程序中,插入某些特殊的逻辑来实现一些特殊的功能,例如日志、事务、安全等都可以很方便的使用AOP来实现。不要认为AOP会取代OOP,它只是OOP的补充。但就像当年的OOP一样,它很可能引发一场软件产业的革命。1.AOP简介AOP把软件系统分成两部分:核心关注点和横切关注点。所谓核心关注点,是业务处理的主要流程,也就是说这个解决方案要做的事。所谓横切关注点,是与核心关注点无关的部分,常常发生在核心关注点的多处,而各处基本相似,如日志、权限等。AOP的核心思想是将应用程序中的商业逻辑和对其进行支持的通用服务进行分离。目前,宣称能够支持AOP的项目已达近百种,Java语言的实现也有20多种,其中最为完善的是AspectJ。2.8JavaWeb技术——Spring采用AOP之前采用AOP之后2.AOP术语切面(Aspect):从对象中抽取出来的横切性功能模块。类似与OOP中的一个类。由通知和切入点两部分组成。通知(Adivice):切面的具体实现,例如具体的日志操作代码,一般是是切面中的某个方法。连接点(Joinpoint):目标对象中插入通知的地方。即advice的应用位置。spring中只支持是方法切入点(Pointcut):切面的一部分,对应一个表达式,定义了advice应该被插入到什么样的Joinpoint点上,即advice的应用范围2.AOP术语目标对象(TargetObject):被通知的对象。代理(AOPProxy):由AOP框架创建的目标对象的代理对象。是被插入了advice的TargetObject。织入(Weaving):将通知与目标对象结合在一起,生成新的对象的过程。新的对象就是AOPProxy。Spring是在运行是完成织入工作的。引入(Introduction):为已经存在的类添加新方法和属性,从而达到修改对象内部结构的目的。3.AOP实现技术Spring使用两种机制实现AOP技术,一是使用java的动态代理,即java.lang.reflect.Proxy类创建代理(注解方式和配置方式两种实现手段)。二是使用CGLIB库自动生成目标对象的子类,同时织入通知。动态代理要求目标对象必须要实现接口(只有这样才能创建代理),而CGLIB则没有这种限制。目前AOP还没有完全的统一标准,因此实现出来的AOP框架也是多种多样的,为此人们成立了一个AOP联盟,用于统一这种混乱局面。3.1.1注解方式实现AOP关键步骤切面操作加入aspect注解编写ponintcut方法并添加注解编写advice方法并添加注解修改spring配置文件:启用aspectJ对anotation的支持配置切面类和目标对象类3.1.1注解方式实现AOP切面LogAspect.java@AspectpublicclassLogAspect{@Pointcut(execution(*save*(..))||execution(*delete*(..)))publicvoidallSaveMethod(){}@Before(allSaveMethod())publicvoidlog(){System.out.println(------------writelogintologfile--------------);}}3.1.1注解方式实现AOP修改spring配置文件:aop:aspectj-autoproxy/beanid=userManagerclass=cn.bsw.spring.annotation.UserManagerImpl“/beanid=logAspectclass=cn.bsw.spring.annotation.LogAspect/3.1.1注解方式实现AOPPointcut表达式任意公共方法的执行:execution(public**(..))任何一个以“set”开始的方法的执行:execution(*set*(..))AccountService接口的任意方法的执行:execution(*com.xyz.service.AccountService.*(..))定义在service包里的任意方法的执行:execution(*com.xyz.service.*.*(..))定义在service包或者子包里的任意方法的执行:execution(*com.xyz.service..*.*(..))3.1.1注解方式实现AOPAdvice类型:前置通知(Beforeadvice)一个切面里使用@Before注解声明前置通知:后置通知(After(finally)advice)不论一个方法是如何结束的,在它结束后(finally)后通知(After(finally)advice)都会运行。使用@After注解来声明。返回后通知(Afterreturningadvice)返回后通知通常在一个匹配的方法返回时执行。使用@AfterReturning注解来声明:抛出后通知(Afterthrowingadvice)抛出后通知在一个方法抛出异常后执行。使用@AfterThrowing注解来声明:环绕通知(AroundAdvice)环绕通知环绕通知在一个方法执行之前和之后执行,使用@Around注解来声明。3.1.2配置文件实现AOP关键步骤编写aspect类,只需advice即可修改spring配置文件:配置aop:config配置aop:aspect指定切面类配置aop:pointcut指定哪些对象的哪些方法订阅切面配置advice如:aop:beforeaop:after等。3.1.2配置文件实现AOP切面LogAspect.javapublicclassLogAspect{publicvoidlog(JoinPointjoinPoint){Objectargs[]=joinPoint.getArgs();for(inti=0;iargs.length;i++)System.out.println(args[i]);System.out.println(joinPoint.getSignature().getName());System.out.println(joinPoint.getSignature().getDeclaringTypeName());System.out.println(------------writelogintologfile--------------);}}可为Advice添加一个JoinPoint参数,这个值会由spring自动传入,从中可以取得连接点的参数值、方法名等信息。3.1.2配置文件实现AOP修改spring配置文件:beanid=userManagerImplclass=cn.bsw.aop.xmlconf.UserManagerImpl/beanid=logAspectclass=cn.bsw.aop.xmlconf.LogAspect/aop:configaop:aspectid=logref=logAspectaop:pointcutid=allSaveMethodexpression=execution(*save*(..))||execution(*delete*(..))/aop:aftermethod=logpointcut-ref=allSaveMethod//aop:aspect/aop:config注意和annotation方式对比,会发现配置内容一致,只是位置和格式不同而已3.2CGLIB实现AOPspring对AOP的支持如果目标对象实现了接口,默认情况下采用JDK动态代理实现AOP如果目标对象实现了接口,可以强制使用CGLIB实现AOP如果目标对象没有实现接口,自动采用CGLIB库实现AOP如何强制使用CGLIB实现AOP?在spring配置文件中加入aop:aspectj-autoproxyproxy-target-class=true/JDK动态代理和CGLIB字节码生成的区别?JDK动态代理只能对实现了接口的类生成代理,而不能针对类CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法补充代理技术1Java静态代理代理模式的作用是:为其他对象提供一种代理以控制对目标对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式一般涉及到的角色有:抽象角色:声明真实对象和代理对象的共同接口;真实角色(目标对象):代理角色所代表的真实对象,是我们最终要引用的对象。代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。1Java静态代理抽象角色UserManagr.javapublicinterfaceUserManager{publicvoidsave(Stringname,Stringpwd);publicvoidupdate(Stringname,Stringpwd,Stringrepwd);publicvoiddelete(intid);publicStringselect(intid);}1Java静态代理真实角色:UserManagerImpl.javapublicclassUserManagerImplimplementsUserManager{publicvoiddelete(intid){System.out.println(------------UserManagerImpl.delete(id)------------);}publicvoidsave(Stringname,Stringpwd){System.out.println(------------UserManagerImpl.save(name,pwd)------------);}publicStringselect(intid){System.out.println(------------UserManagerImpl.select(id)------------);returnnull;}publicvoidupdate(Stringname,Stringpwd,Stringrepwd){System.out.println(-------UserManagerImpl.update(name,pwd,repwd)-----);}}1Java静态代理代理角色:UserManagerSPorxy.javapublicclassUserManagerSPorxyimplementsUserManager{UserManageruserManager;publicUserManagerSPorxy(UserManageruserManager){this.userManager=userManager;}publicvoiddelete(intid){log();userManager.delete(id);}publicvoidsave(Stringname,Stringpwd){log();userManager.save(n