Spring容器蕴含两个重要的个性:面向切面编程(AOP)和管制反转(IOC)。面向切面编程是面向对象(OOP)的一种补充,在面向对象编程的过程中编程针对的指标是一个个对象,而面向切面编程中编程针对的指标是一个个切面。切面反对跨类型跨对象(如事务的切面能够加在任何中央)进行模块化。
<!--more-->
前言
AOP是Spring的要害个性之一,尽管Spring的IOC个性并不依赖于AOP(意味着你能够只应用Spring的IOC个性而不应用AOP个性),然而二者联合起来能够灵便的实现很多中间件解决方案。比方咱们常常应用的事务(@Transaction),就是通过AOP计划实现的。本文重点介绍AOP编程中的一些术语,这些术语不仅仅局限于Spring,它实用于所有的AOP编程。
- 切面(Aspect):面向切面编程能够跨类跨对象进行切面编程,一个切面就是对一类横切关注点的模块化。
- 切入点(JoinPoint):程序执行过程中的一个点,如办法调用、字段拜访和异样抛出等。
- 加强(Advice):用于对切面加强,蕴含前加强、后加强和盘绕加强。大多数AOP框架会对切入点进行拦挡,并在切入点保护一个拦截器链。
- 指标对象(TargetObject):蕴含一个或者多个切面的对象。
- AOP代理(AOPProxy):通过Java动静代理或者CGLib加强失去的代理对象。
- 织入(Weaving):将切面整合到残缺的流执行流程。
Spring的AOP的性能和指标
Spring的AOP应用纯Java语言实现(如AspectJ就不是Java语言),不须要任何额定的编译流程,不须要批改类加载器,实用于任何Servlet容器和应用服务。Spring的AOP只反对办法拦挡,不反对字段拦挡,如果用户须要应用字段拦挡,能够思考引入AspectJ等相似的框架。
Spring的AOP框架和其它的框架有些不同,Spring的Aop框架不仅仅是为了提供一个AOP性能,它更重要的性能是和Spring的IOC容器联合,提供一些企业应用服务的解决方案(如事务等),咱们能够和定义一个一般Bean一样的形式去定以一个切面。Spring的AOP不反对十分细粒度的AOP,只反对对容器中的Bean进行AOP,如果须要更细粒度的AOP,能够思考应用AspectJ。Spring容器的一个优良的个性就是非侵入性,所以你能够灵便的抉择本人的AOP计划,不肯定非要应用Spring的计划。
代理形式
Spring对实现接口的办法默认应用Java动静代理实现AOP拦挡,对于非接口办法则会应用CGLIB字节码工具实现代理。
@AspectJ的反对
@AspectJ注解能够把一个一般的Java类申明为切面。@AspectJ注解是AspectJ5引入的注解,Spring尽管能够读取AspectJ5的注解用于切面元数据的配置,然而在运行的时候应用的依然是Spring AOP进行代理,而没有应用AspectJ的编译器或者织入逻辑。咱们会在后续探讨如何在Spring中应用AspectJ的编译器和织入逻辑。
启用@AspectJ
Spring默认没有启用AspectJ,如果你须要Spring反对@AspectJ注解对应的切面,能够通过在配置类上增加注解或者应用XML启用配置(AspectJ所在的包是:aspectjweaver.jar)。
通过Java注解启用AspectJ注解反对:
@Configuration@EnableAspectJAutoProxypublic class AppConfig {}
通过XML配置启用AspectJ注解
<aop:aspectj-autoproxy/>
定义一个切面
当启用AspectJ反对之后,开发者定义的任何Aspect切面会主动地被检测到,而后Spring AOP会对切面进行拦挡。上面两个例子展现了如何配置AspectJ切面,你能够通过增加@Component注解把切面Bean注册到Spring容器中。
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect"> <!-- configure properties of the aspect here --></bean>
package org.xyz;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class NotVeryUsefulAspect {}
申明一个切入点
切入点程序运行过程中咱们感兴趣的一个点,Spring的AOP框架只反对发现对Spring Bean办法上的切入点,因而你能够简略的把切入点了解为SpringBean的办法。
切入点确定感兴趣的连接点,从而使咱们可能管制何时运行告诉。springaop只反对springbean的办法执行连接点,因而能够将切入点看作与springbean上办法的执行相匹配。切入点申明由两局部组成:一部分是由名称和任何参数组成的签名,另一部分是确定咱们感兴趣的办法执行的切入点表达式。在AOP的@AspectJ正文款式中,切入点签名由惯例办法定义提供,切入点表达式由@pointcut正文批示(用作切入点签名的办法必须具备void返回类型)。切入点由两局部组成,一部分是用于区别不同切入点的标识(上面例子中的private void anyOldTransfer() {}
)),另外一部分是确定咱们感兴趣的Bean办法的表达式(上面例子中的@Pointcut("execution(* transfer(..))")
), 上面的例子展现了一个切入点的定义:
@Pointcut("execution(* transfer(..))") // the pointcut expressionprivate void anyOldTransfer() {} // the pointcut signature
Spring匹配切入点的语法应用了AspectJ5中的表达式语法,咱们能够参考AspectJ文档相干的语法.
常见的切入点匹配表白
Spring反对上面常见的AspectJ切面定义语法:
- execution:用于匹配办法的连接点。
@Pointcut("execution(public * *(..))")private void anyPublicOperation() {}
- within:用于匹配类型内的办法。
@Pointcut("within(com.xyz.myapp.trading..*)")private void inTrading() {}
- this:匹配以后AOP代理对象的执行办法
@target(org.springframework.transaction.annotation.Transactional)
- target:target匹配指标对象的类型,即被代理对象的类型,例如A继承了B接口,则应用target("B"),target("A")均能够匹配到A
// 以后AOP对象实现了 IPointcutService接口的任何办法@Pointcut("target(cn.javass.spring.chapter6.service.IPointcutService)")private void anyPublicOperation() {}
- args:用于限定切点办法的参数类型。
args(java.io.Serializable)
- @target:被代理对象应该蕴含指定的注解。
@target(org.springframework.transaction.annotation.Transactional)
- @args: 被代理对象的参数蕴含指定的注解。
@args(com.xyz.security.Classified)
- @within: 被代理的对象应蕴含指定注解
@within(org.springframework.transaction.annotation.Transactional)
- @annotation:切入点蕴含指定的注解。
@annotation(org.springframework.transaction.annotation.Transactional)
咱们能够通过“&&”和“||”对多个条件进行组合,AspectJ还有很多其它的表达式,然而Spring不反对除上述表达式以外的其它表达式。AspectJ其它表达式蕴含: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode等。
咱们在应用Spring的代理办法之前,应该晓得其代理原理。Java动静代理只能拦挡public接口办法上的调用,CGLib只能拦挡public、protected和defult办法。如果你须要更深层次的拦挡,能够思考应用底层的Aspectj。
切面的加强
咱们在下面的步骤定义好了一个切入点,咱们当初就能够对这个切入点进行额定操作,这些额定操作被称为加强,Spring反对四种加强形式:前加强、后加强、异样加强和盘绕加强。Spring反对在加强办法的定义上间接定义切入点。
前加强BeforeAdvice
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample { @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()") public void doAccessCheck() { // ... }}
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class BeforeExample { @Before("execution(* com.xyz.myapp.dao.*.*(..))") public void doAccessCheck() { // ... }}
后加强
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterReturning;@Aspectpublic class AfterReturningExample { @AfterReturning( pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()", returning="retVal") public void doAccessCheck(Object retVal) { // ... }}
异样加强
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.AfterThrowing;@Aspectpublic class AfterThrowingExample { @AfterThrowing( pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()", throwing="ex") public void doRecoveryActions(DataAccessException ex) { // ... }}
盘绕加强
import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.ProceedingJoinPoint;@Aspectpublic class AroundExample { @Around("com.xyz.myapp.CommonPointcuts.businessService()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { // start stopwatch Object retVal = pjp.proceed(); // stop stopwatch return retVal; }}
代理机制
咱们后面说过,Spring AOP通过动静代理和CGLIB实现AOP对象的代理。咱们能够通过如下配置设置动静代理全副走CGLIB。
<aop:config proxy-target-class="true"> <!-- other beans defined here... --></aop:config>
代理工厂的应用
Spring AOP实现代理的外围类是AspectJProxyFactory
,咱们能够应用这个类编程式生成代理对象:
// create a factory that can generate a proxy for the given target objectAspectJProxyFactory factory = new AspectJProxyFactory(targetObject);// add an aspect, the class must be an @AspectJ aspect// you can call this as many times as you need with different aspectsfactory.addAspect(SecurityManager.class);// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspectfactory.addAspect(usageTracker);// now get the proxy object...MyInterfaceType proxy = factory.getProxy()
本文最先公布至微信公众号,版权所有,禁止转载!