在Spring根底 - Spring简略例子引入Spring的外围中向你展现了AOP的根底含意,同时以此发散了一些AOP相干知识点; 本节将在此基础上进一步解读AOP的含意以及AOP的应用形式。@pdai
Spring框架系列(4) - 深入浅出Spring外围之面向切面编程(AOP)
- 引入
如何了解AOP
- AOP是什么
- AOP术语
- Spring AOP和AspectJ是什么关系
AOP的配置形式
- XML Schema配置形式
AspectJ注解形式
- 接口应用JDK代理
- 非接口应用Cglib代理
AOP应用问题小结
- 切入点(pointcut)的申明规定?
- 多种加强告诉的程序?
- Spring AOP 和 AspectJ 之间的要害区别?
- Spring AOP还是齐全用AspectJ?
- 参考文章
- 更多文章
引入
咱们在Spring根底 - Spring简略例子引入Spring的外围中向你展现了AOP的根底含意,同时以此发散了一些AOP相干知识点。
- Spring 框架通过定义切面, 通过拦挡切点实现了不同业务模块的解耦,这个就叫面向切面编程 - Aspect Oriented Programming (AOP)
- 为什么@Aspect注解应用的是aspectj的jar包呢?这就引出了Aspect4J和Spring AOP的历史渊源,只有了解了Aspect4J和Spring的渊源能力了解有些注解上的兼容设计
- 如何反对更多拦挡形式来实现解耦, 以满足更多场景需要呢? 这就是@Around, @Pointcut... 等的设计
- 那么Spring框架又是如何实现AOP的呢? 这就引入代理技术,分动态代理和动静代理,动静代理又蕴含JDK代理和CGLIB代理等
本节将在此基础上进一步解读AOP的含意以及AOP的应用形式;后续的文章还将深刻AOP的实现原理:
- Spring进阶 - Spring AOP实现原理详解之切面实现
- Spring进阶 - Spring AOP实现原理详解之AOP代理
如何了解AOP
AOP的实质也是为理解耦,它是一种设计思维; 在了解时也应该简化了解。
AOP是什么
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
AOP最早是AOP联盟的组织提出的,指定的一套标准,spring将AOP的思维引入框架之中,通过预编译形式和运行期间动静代理实现程序的对立保护的一种技术,
- 先来看一个例子, 如何给如下UserServiceImpl中所有办法增加进入办法的日志,
/** * @author pdai */public class UserServiceImpl implements IUserService { /** * find user list. * * @return user list */ @Override public List<User> findUserList() { System.out.println("execute method: findUserList"); return Collections.singletonList(new User("pdai", 18)); } /** * add user */ @Override public void addUser() { System.out.println("execute method: addUser"); // do something }}
咱们将记录日志性能解耦为日志切面,它的指标是解耦。进而引出AOP的理念:就是将扩散在各个业务逻辑代码中雷同的代码通过横向切割的形式抽取到一个独立的模块中!
OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行形象封装,以取得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以取得逻辑过程的中各局部之间低耦合的隔离成果。这两种设计思维在指标上有着实质的差别。
AOP术语
首先让咱们从一些重要的AOP概念和术语开始。这些术语不是Spring特有的。
- 连接点(Jointpoint):示意须要在程序中插入横切关注点的扩大点,连接点可能是类初始化、办法执行、办法调用、字段调用或解决异样等等,Spring只反对办法执行连接点,在AOP中示意为在哪里干;
- 切入点(Pointcut): 抉择一组相干连接点的模式,即能够认为连接点的汇合,Spring反对perl5正则表达式和AspectJ切入点模式,Spring默认应用AspectJ语法,在AOP中示意为在哪里干的汇合;
- 告诉(Advice):在连接点上执行的行为,告诉提供了在AOP中须要在切入点所抉择的连接点处进行扩大现有行为的伎俩;包含前置告诉(before advice)、后置告诉(after advice)、盘绕告诉(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以盘绕连接点的拦截器链织入告诉;在AOP中示意为干什么;
- 方面/切面(Aspect):横切关注点的模块化,比方上边提到的日志组件。能够认为是告诉、引入和切入点的组合;在Spring中能够应用Schema和@AspectJ形式进行组织实现;在AOP中示意为在哪干和干什么汇合;
- 引入(inter-type declaration):也称为外部类型申明,为已有的类增加额定新的字段或办法,Spring容许引入新的接口(必须对应一个实现)到所有被代理对象(指标对象), 在AOP中示意为干什么(引入什么);
- 指标对象(Target Object):须要被织入横切关注点的对象,即该对象是切入点抉择的对象,须要被告诉的对象,从而也可称为被告诉对象;因为Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中示意为对谁干;
- 织入(Weaving):把切面连贯到其它的应用程序类型或者对象上,并创立一个被告诉的对象。这些能够在编译时(例如应用AspectJ编译器),类加载时和运行时实现。Spring和其余纯Java AOP框架一样,在运行时实现织入。在AOP中示意为怎么实现的;
- AOP代理(AOP Proxy):AOP框架应用代理模式创立的对象,从而实现在连接点处插入告诉(即利用切面),就是通过代理来对指标对象利用切面。在Spring中,AOP代理能够用JDK动静代理或CGLIB代理实现,而通过拦截器模型利用切面。在AOP中示意为怎么实现的一种典型形式;
告诉类型:
- 前置告诉(Before advice):在某连接点之前执行的告诉,但这个告诉不能阻止连接点之前的执行流程(除非它抛出一个异样)。
- 后置告诉(After returning advice):在某连接点失常实现后执行的告诉:例如,一个办法没有抛出任何异样,失常返回。
- 异样告诉(After throwing advice):在办法抛出异样退出时执行的告诉。
- 最终告诉(After (finally) advice):当某连接点退出的时候执行的告诉(不论是失常返回还是异样退出)。
- 盘绕告诉(Around Advice):突围一个连接点的告诉,如办法调用。这是最弱小的一种告诉类型。盘绕告诉能够在办法调用前后实现自定义的行为。它也会抉择是否继续执行连接点或间接返回它本人的返回值或抛出异样来完结执行。
盘绕告诉是最罕用的告诉类型。和AspectJ一样,Spring提供所有类型的告诉,咱们举荐你应用尽可能简略的告诉类型来实现须要的性能。例如,如果你只是须要一个办法的返回值来更新缓存,最好应用后置告诉而不是盘绕告诉,只管盘绕告诉也能实现同样的事件。用最合适的告诉类型能够使得编程模型变得简略,并且可能防止很多潜在的谬误。比方,你不须要在JoinPoint上调用用于盘绕告诉的proceed()办法,就不会有调用的问题。
咱们把这些术语串联到一起,不便了解
Spring AOP和AspectJ是什么关系
- 首先AspectJ是什么?
AspectJ是一个java实现的AOP框架,它可能对java代码进行AOP编译(个别在编译期进行),让java代码具备AspectJ的AOP性能(当然须要非凡的编译器)
能够这样说AspectJ是目前实现AOP框架中最成熟,性能最丰盛的语言,更侥幸的是,AspectJ与java程序齐全兼容,简直是无缝关联,因而对于有java编程根底的工程师,上手和应用都非常容易。
- 其次,为什么须要理分明Spring AOP和AspectJ的关系?
咱们看下@Aspect以及加强的几个注解,为什么不是Spring包,而是来源于aspectJ呢?
- Spring AOP和AspectJ是什么关系?
- AspectJ是更强的AOP框架,是实际意义的AOP规范;
- Spring为何不写相似AspectJ的框架? Spring AOP应用纯Java实现, 它不须要专门的编译过程, 它一个重要的准则就是无侵入性(non-invasiveness); Spring 小组齐全有能力写相似的框架,只是Spring AOP素来没有打算通过提供一种全面的AOP解决方案来与AspectJ竞争。Spring的开发小组置信无论是基于代理(proxy-based)的框架如Spring AOP或者是成熟的框架如AspectJ都是很有价值的,他们之间应该是互补而不是竞争的关系。
- Spring小组喜爱@AspectJ注解格调更胜于Spring XML配置; 所以在Spring 2.0应用了和AspectJ 5一样的注解,并应用AspectJ来做切入点解析和匹配。然而,AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器(weaver)。
- Spring 2.5对AspectJ的反对:在一些环境下,减少了对AspectJ的装载时编织反对,同时提供了一个新的bean切入点。
- 更多对于AspectJ?
理解AspectJ利用到java代码的过程(这个过程称为织入),对于织入这个概念,能够简略了解为aspect(切面)利用到指标函数(类)的过程。
对于这个过程,个别分为动静织入和动态织入:
- 动静织入的形式是在运行时动静将要加强的代码织入到指标类中,这样往往是通过动静代理技术实现的,如Java JDK的动静代理(Proxy,底层通过反射实现)或者CGLIB的动静代理(底层通过继承实现),Spring AOP采纳的就是基于运行时加强的代理技术
- ApectJ采纳的就是动态织入的形式。ApectJ次要采纳的是编译期织入,在这个期间应用AspectJ的acj编译器(相似javac)把aspect类编译成class字节码后,在java指标类编译时织入,即先编译aspect类再编译指标类。
AOP的配置形式
Spring AOP 反对对XML模式和基于@AspectJ注解的两种配置形式。
XML Schema配置形式
Spring提供了应用"aop"命名空间来定义一个切面,咱们来看个例子(例子代码):
- 定义指标类
package tech.pdai.springframework.service;/** * @author pdai */public class AopDemoServiceImpl { public void doMethod1() { System.out.println("AopDemoServiceImpl.doMethod1()"); } public String doMethod2() { System.out.println("AopDemoServiceImpl.doMethod2()"); return "hello world"; } public String doMethod3() throws Exception { System.out.println("AopDemoServiceImpl.doMethod3()"); throw new Exception("some exception"); }}
- 定义切面类
package tech.pdai.springframework.aspect;import org.aspectj.lang.ProceedingJoinPoint;/** * @author pdai */public class LogAspect { /** * 盘绕告诉. * * @param pjp pjp * @return obj * @throws Throwable exception */ public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("-----------------------"); System.out.println("盘绕告诉: 进入办法"); Object o = pjp.proceed(); System.out.println("盘绕告诉: 退出办法"); return o; } /** * 前置告诉. */ public void doBefore() { System.out.println("前置告诉"); } /** * 后置告诉. * * @param result return val */ public void doAfterReturning(String result) { System.out.println("后置告诉, 返回值: " + result); } /** * 异样告诉. * * @param e exception */ public void doAfterThrowing(Exception e) { System.out.println("异样告诉, 异样: " + e.getMessage()); } /** * 最终告诉. */ public void doAfter() { System.out.println("最终告诉"); }}
- XML配置AOP
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="tech.pdai.springframework" /> <aop:aspectj-autoproxy/> <!-- 指标类 --> <bean id="demoService" class="tech.pdai.springframework.service.AopDemoServiceImpl"> <!-- configure properties of bean here as normal --> </bean> <!-- 切面 --> <bean id="logAspect" class="tech.pdai.springframework.aspect.LogAspect"> <!-- configure properties of aspect here as normal --> </bean> <aop:config> <!-- 配置切面 --> <aop:aspect ref="logAspect"> <!-- 配置切入点 --> <aop:pointcut id="pointCutMethod" expression="execution(* tech.pdai.springframework.service.*.*(..))"/> <!-- 盘绕告诉 --> <aop:around method="doAround" pointcut-ref="pointCutMethod"/> <!-- 前置告诉 --> <aop:before method="doBefore" pointcut-ref="pointCutMethod"/> <!-- 后置告诉;returning属性:用于设置后置告诉的第二个参数的名称,类型是Object --> <aop:after-returning method="doAfterReturning" pointcut-ref="pointCutMethod" returning="result"/> <!-- 异样告诉:如果没有异样,将不会执行加强;throwing属性:用于设置告诉第二个参数的的名称、类型--> <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointCutMethod" throwing="e"/> <!-- 最终告诉 --> <aop:after method="doAfter" pointcut-ref="pointCutMethod"/> </aop:aspect> </aop:config> <!-- more bean definitions for data access objects go here --></beans>
- 测试类
/** * main interfaces. * * @param args args */public static void main(String[] args) { // create and configure beans ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml"); // retrieve configured instance AopDemoServiceImpl service = context.getBean("demoService", AopDemoServiceImpl.class); // use configured instance service.doMethod1(); service.doMethod2(); try { service.doMethod3(); } catch (Exception e) { // e.printStackTrace(); }}
- 输入后果
-----------------------盘绕告诉: 进入办法前置告诉AopDemoServiceImpl.doMethod1()盘绕告诉: 退出办法最终告诉-----------------------盘绕告诉: 进入办法前置告诉AopDemoServiceImpl.doMethod2()盘绕告诉: 退出办法最终告诉后置告诉, 返回值: hello world-----------------------盘绕告诉: 进入办法前置告诉AopDemoServiceImpl.doMethod3()最终告诉异样告诉, 异样: some exception
AspectJ注解形式
基于XML的申明式AspectJ存在一些有余,须要在Spring配置文件配置大量的代码信息,为了解决这个问题,Spring 应用了@AspectJ框架为AOP的实现提供了一套注解。
注解名称 | 解释 | |
---|---|---|
@Aspect | 用来定义一个切面。 | |
@pointcut | 用于定义切入点表达式。在应用时还须要定义一个蕴含名字和任意参数的办法签名来示意切入点名称,这个办法签名就是一个返回值为void,且办法体为空的一般办法。 | |
@Before | 用于定义前置告诉,相当于BeforeAdvice。在应用时,通常须要指定一个value属性值,该属性值用于指定一个切入点表达式(能够是已有的切入点,也能够间接定义切入点表达式)。 | |
@AfterReturning | 用于定义后置告诉,相当于AfterReturningAdvice。在应用时能够指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。 | returning属性值用于示意Advice办法中定义与此同名的形参,该形参可用于拜访指标办法的返回值。 |
@Around | 用于定义盘绕告诉,相当于MethodInterceptor。在应用时须要指定一个value属性,该属性用于指定该告诉被植入的切入点。 | |
@After-Throwing | 用于定义异样告诉来处理程序中未解决的异样,相当于ThrowAdvice。在应用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来示意Advice办法中可定义与此同名的形参,该形参可用于拜访指标办法抛出的异样。 | |
@After | 用于定义最终final 告诉,不论是否异样,该告诉都会执行。应用时须要指定一个value属性,该属性用于指定该告诉被植入的切入点。 | |
@DeclareParents | 用于定义引介告诉,相当于IntroductionInterceptor (不要求把握)。 |
Spring AOP的实现形式是动静织入,动静织入的形式是在运行时动静将要加强的代码织入到指标类中,这样往往是通过动静代理技术实现的;如Java JDK的动静代理(Proxy,底层通过反射实现)或者CGLIB的动静代理(底层通过继承实现),Spring AOP采纳的就是基于运行时加强的代理技术。所以咱们看下如下的两个例子(例子代码 中05模块):
- 基于JDK代理例子
- 基于Cglib代理例子
接口应用JDK代理
- 定义接口
/** * Jdk Proxy Service. * * @author pdai */public interface IJdkProxyService { void doMethod1(); String doMethod2(); String doMethod3() throws Exception;}
- 实现类
/** * @author pdai */@Servicepublic class JdkProxyDemoServiceImpl implements IJdkProxyService { @Override public void doMethod1() { System.out.println("JdkProxyServiceImpl.doMethod1()"); } @Override public String doMethod2() { System.out.println("JdkProxyServiceImpl.doMethod2()"); return "hello world"; } @Override public String doMethod3() throws Exception { System.out.println("JdkProxyServiceImpl.doMethod3()"); throw new Exception("some exception"); }}
- 定义切面
package tech.pdai.springframework.aspect;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.context.annotation.EnableAspectJAutoProxy;import org.springframework.stereotype.Component;/** * @author pdai */@EnableAspectJAutoProxy@Component@Aspectpublic class LogAspect { /** * define point cut. */ @Pointcut("execution(* tech.pdai.springframework.service.*.*(..))") private void pointCutMethod() { } /** * 盘绕告诉. * * @param pjp pjp * @return obj * @throws Throwable exception */ @Around("pointCutMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { System.out.println("-----------------------"); System.out.println("盘绕告诉: 进入办法"); Object o = pjp.proceed(); System.out.println("盘绕告诉: 退出办法"); return o; } /** * 前置告诉. */ @Before("pointCutMethod()") public void doBefore() { System.out.println("前置告诉"); } /** * 后置告诉. * * @param result return val */ @AfterReturning(pointcut = "pointCutMethod()", returning = "result") public void doAfterReturning(String result) { System.out.println("后置告诉, 返回值: " + result); } /** * 异样告诉. * * @param e exception */ @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e") public void doAfterThrowing(Exception e) { System.out.println("异样告诉, 异样: " + e.getMessage()); } /** * 最终告诉. */ @After("pointCutMethod()") public void doAfter() { System.out.println("最终告诉"); }}
- 输入
-----------------------盘绕告诉: 进入办法前置告诉JdkProxyServiceImpl.doMethod1()最终告诉盘绕告诉: 退出办法-----------------------盘绕告诉: 进入办法前置告诉JdkProxyServiceImpl.doMethod2()后置告诉, 返回值: hello world最终告诉盘绕告诉: 退出办法-----------------------盘绕告诉: 进入办法前置告诉JdkProxyServiceImpl.doMethod3()异样告诉, 异样: some exception最终告诉
非接口应用Cglib代理
- 类定义
/** * Cglib proxy. * * @author pdai */@Servicepublic class CglibProxyDemoServiceImpl { public void doMethod1() { System.out.println("CglibProxyDemoServiceImpl.doMethod1()"); } public String doMethod2() { System.out.println("CglibProxyDemoServiceImpl.doMethod2()"); return "hello world"; } public String doMethod3() throws Exception { System.out.println("CglibProxyDemoServiceImpl.doMethod3()"); throw new Exception("some exception"); }}
- 切面定义
和下面雷同
- 输入
-----------------------盘绕告诉: 进入办法前置告诉CglibProxyDemoServiceImpl.doMethod1()最终告诉盘绕告诉: 退出办法-----------------------盘绕告诉: 进入办法前置告诉CglibProxyDemoServiceImpl.doMethod2()后置告诉, 返回值: hello world最终告诉盘绕告诉: 退出办法-----------------------盘绕告诉: 进入办法前置告诉CglibProxyDemoServiceImpl.doMethod3()异样告诉, 异样: some exception最终告诉
AOP应用问题小结
这里总结下理论开发中会遇到的一些问题:
切入点(pointcut)的申明规定?
Spring AOP 用户可能会常常应用 execution切入点批示符。执行表达式的格局如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- ret-type-pattern 返回类型模式, name-pattern名字模式和param-pattern参数模式是必选的, 其它局部都是可选的。返回类型模式决定了办法的返回类型必须顺次匹配一个连接点。 你会应用的最频繁的返回类型模式是
*
,它代表了匹配任意的返回类型。 - declaring-type-pattern, 一个全限定的类型名将只会匹配返回给定类型的办法。
- name-pattern 名字模式匹配的是办法名。 你能够应用
*
通配符作为所有或者局部命名模式。 - param-pattern 参数模式略微有点简单:()匹配了一个不承受任何参数的办法, 而(..)匹配了一个承受任意数量参数的办法(零或者更多)。 模式()匹配了一个承受一个任何类型的参数的办法。 模式(,String)匹配了一个承受两个参数的办法,第一个能够是任意类型, 第二个则必须是String类型。
对应到咱们下面的例子:
上面给出一些通用切入点表达式的例子。
// 任意公共办法的执行:execution(public * *(..))// 任何一个名字以“set”开始的办法的执行:execution(* set*(..))// AccountService接口定义的任意办法的执行:execution(* com.xyz.service.AccountService.*(..))// 在service包中定义的任意办法的执行:execution(* com.xyz.service.*.*(..))// 在service包或其子包中定义的任意办法的执行:execution(* com.xyz.service..*.*(..))// 在service包中的任意连接点(在Spring AOP中只是办法执行):within(com.xyz.service.*)// 在service包或其子包中的任意连接点(在Spring AOP中只是办法执行):within(com.xyz.service..*)// 实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是办法执行):this(com.xyz.service.AccountService)// 'this'在绑定表单中更加罕用// 实现AccountService接口的指标对象的任意连接点 (在Spring AOP中只是办法执行):target(com.xyz.service.AccountService) // 'target'在绑定表单中更加罕用// 任何一个只承受一个参数,并且运行时所传入的参数是Serializable 接口的连接点(在Spring AOP中只是办法执行)args(java.io.Serializable) // 'args'在绑定表单中更加罕用; 请留神在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args版本只有在动静运行时候传入参数是Serializable时才匹配,而execution版本在办法签名中申明只有一个 Serializable类型的参数时候匹配。// 指标对象中有一个 @Transactional 注解的任意连接点 (在Spring AOP中只是办法执行)@target(org.springframework.transaction.annotation.Transactional)// '@target'在绑定表单中更加罕用// 任何一个指标对象申明的类型有一个 @Transactional 注解的连接点 (在Spring AOP中只是办法执行):@within(org.springframework.transaction.annotation.Transactional) // '@within'在绑定表单中更加罕用// 任何一个执行的办法有一个 @Transactional 注解的连接点 (在Spring AOP中只是办法执行)@annotation(org.springframework.transaction.annotation.Transactional) // '@annotation'在绑定表单中更加罕用// 任何一个只承受一个参数,并且运行时所传入的参数类型具备@Classified 注解的连接点(在Spring AOP中只是办法执行)@args(com.xyz.security.Classified) // '@args'在绑定表单中更加罕用// 任何一个在名为'tradeService'的Spring bean之上的连接点 (在Spring AOP中只是办法执行)bean(tradeService)// 任何一个在名字匹配通配符表达式'*Service'的Spring bean之上的连接点 (在Spring AOP中只是办法执行)bean(*Service)
此外Spring 反对如下三个逻辑运算符来组合切入点表达式
&&:要求连接点同时匹配两个切入点表达式||:要求连接点匹配任意个切入点表达式!::要求连接点不匹配指定的切入点表达式
多种加强告诉的程序?
如果有多个告诉想要在同一连接点运行会产生什么?Spring AOP遵循跟AspectJ一样的优先规定来确定告诉执行的程序。 在“进入”连接点的状况下,最高优先级的告诉会先执行(所以给定的两个前置告诉中,优先级高的那个会先执行)。 在“退出”连接点的状况下,最高优先级的告诉会最初执行。(所以给定的两个后置告诉中, 优先级高的那个会第二个执行)。
当定义在不同的切面里的两个告诉都须要在一个雷同的连接点中运行, 那么除非你指定,否则执行的程序是未知的。你能够通过指定优先级来管制执行程序。 在规范的Spring办法中能够在切面类中实现org.springframework.core.Ordered 接口或者用Order注解做到这一点。在两个切面中, Ordered.getValue()办法返回值(或者注解值)较低的那个有更高的优先级。
当定义在雷同的切面里的两个告诉都须要在一个雷同的连接点中运行, 执行的程序是未知的(因为这里没有办法通过反射javac编译的类来获取申明程序)。 思考在每个切面类中按连接点压缩这些告诉办法到一个告诉办法,或者重构告诉的片段到各自的切面类中 - 它能在切面级别进行排序。
Spring AOP 和 AspectJ 之间的要害区别?
AspectJ能够做Spring AOP干不了的事件,它是AOP编程的齐全解决方案,Spring AOP则致力于解决企业级开发中最广泛的AOP(办法织入)。
下表总结了 Spring AOP 和 AspectJ 之间的要害区别:
Spring AOP | AspectJ |
---|---|
在纯 Java 中实现 | 应用 Java 编程语言的扩大实现 |
不须要独自的编译过程 | 除非设置 LTW,否则须要 AspectJ 编译器 (ajc) |
只能应用运行时织入 | 运行时织入不可用。反对编译时、编译后和加载时织入 |
性能不强-仅反对办法级编织 | 更弱小 - 能够编织字段、办法、构造函数、动态初始值设定项、最终类/办法等......。 |
只能在由 Spring 容器治理的 bean 上实现 | 能够在所有域对象上实现 |
仅反对办法执行切入点 | 反对所有切入点 |
代理是由指标对象创立的, 并且切面利用在这些代理上 | 在执行应用程序之前 (在运行时) 前, 各方面间接在代码中进行织入 |
比 AspectJ 慢多了 | 更好的性能 |
易于学习和利用 | 绝对于 Spring AOP 来说更简单 |
Spring AOP还是齐全用AspectJ?
以下Spring官网的答复:(总结来说就是 Spring AOP更易用,AspectJ更弱小)。
- Spring AOP比齐全应用AspectJ更加简略, 因为它不须要引入AspectJ的编译器/织入器到你开发和构建过程中。 如果你仅仅须要在Spring bean上告诉执行操作,那么Spring AOP是适合的抉择。
- 如果你须要告诉domain对象或其它没有在Spring容器中治理的任意对象,那么你须要应用AspectJ。
- 如果你想告诉除了简略的办法执行之外的连接点(如:调用连接点、字段get或set的连接点等等), 也须要应用AspectJ。
当应用AspectJ时,你能够抉择应用AspectJ语言(也称为“代码格调”)或@AspectJ注解格调。 如果切面在你的设计中表演一个很大的角色,并且你能在Eclipse等IDE中应用AspectJ Development Tools (AJDT), 那么首选AspectJ语言 :- 因为该语言专门被设计用来编写切面,所以会更清晰、更简略。如果你没有应用 Eclipse等IDE,或者在你的利用中只有很少的切面并没有作为一个次要的角色,你或者应该思考应用@AspectJ格调 并在你的IDE中附加一个一般的Java编辑器,并且在你的构建脚本中减少切面织入(链接)的段落。
参考文章
http://shouce.jb51.net/spring...
https://www.cnblogs.com/linhp...
https://www.cnblogs.com/bj-xi...
更多文章
首先, 从Spring框架的整体架构和组成对整体框架有个认知。
Spring根底 - Spring和Spring框架组成
- Spring是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑Spring和Spring Framework的整体认知。
其次,通过案例引出Spring的外围(IoC和AOP),同时对IoC和AOP进行案例应用剖析。
Spring根底 - Spring简略例子引入Spring的外围
- 上文中咱们简略介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文次要承接上文,向你展现Spring Framework组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出Spring的外围要点,比方IOC和AOP等;在此基础上还引入了不同的配置形式, 如XML,Java配置和注解形式的差别。
Spring根底 - Spring外围之管制反转(IOC)
- 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了IoC的根底含意,同时以此发散了一些IoC相干知识点; 本节将在此基础上进一步解读IOC的含意以及IOC的应用形式
Spring根底 - Spring外围之面向切面编程(AOP)
- 在Spring根底 - Spring简略例子引入Spring的外围中向你展现了AOP的根底含意,同时以此发散了一些AOP相干知识点; 本节将在此基础上进一步解读AOP的含意以及AOP的应用形式。
基于Spring框架和IOC,AOP的根底,为构建下层web利用,须要进一步学习SpringMVC。
Spring根底 - SpringMVC申请流程和案例
- 前文咱们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那咱们如何更好的构建下层的利用呢(比方web 利用),这便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技术根底上,遵循上述Web MVC的标准推出的web开发框架,目标是为了简化Java栈的web开发。 本文次要介绍SpringMVC的申请流程和根底案例的编写和运行。
Spring进阶 - IoC,AOP以及SpringMVC的源码剖析
Spring进阶 - Spring IOC实现原理详解之IOC体系结构设计
- 在对IoC有了初步的认知后,咱们开始对IOC的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看IOC最顶层的结构设计
Spring进阶 - Spring IOC实现原理详解之IOC初始化流程
- 上文,咱们看了IOC设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的
Spring进阶 - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
- 上文,咱们看了IOC设计要点和设计构造;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中寄存的是Bean的定义即BeanDefinition放到beanDefinitionMap中,实质上是一个
ConcurrentHashMap<String, Object>
;并且BeanDefinition接口中蕴含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢,这是本文次要钻研的内容?
- 上文,咱们看了IOC设计要点和设计构造;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中寄存的是Bean的定义即BeanDefinition放到beanDefinitionMap中,实质上是一个
Spring进阶 - Spring AOP实现原理详解之切面实现
- 前文,咱们剖析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。本文次要介绍Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理加强实现做筹备的过程)。
Spring进阶 - Spring AOP实现原理详解之AOP代理
- 上文咱们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上持续介绍,代理(cglib代理和JDK代理)的实现过程。
Spring进阶 - Spring AOP实现原理详解之Cglib代理实现
- 咱们在前文中曾经介绍了SpringAOP的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍Cglib动静代理的案例和SpringAOP实现的原理。
Spring进阶 - Spring AOP实现原理详解之JDK代理实现
- 上文咱们学习了SpringAOP Cglib动静代理的实现,本文次要是SpringAOP JDK动静代理的案例和实现局部。
Spring进阶 - SpringMVC实现原理之DispatcherServlet初始化的过程
- 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第一篇:DispatcherServlet的初始化过程的源码解析。
Spring进阶 - SpringMVC实现原理之DispatcherServlet解决申请的过程
- 前文咱们有了IOC的源码根底以及SpringMVC的根底,咱们便能够进一步深刻了解SpringMVC次要实现原理,蕴含DispatcherServlet的初始化过程和DispatcherServlet解决申请的过程的源码解析。本文是第二篇:DispatcherServlet解决申请的过程的源码解析。