关于java:Spring基础只是AOP的概念介绍

34次阅读

共计 5824 个字符,预计需要花费 15 分钟才能阅读完成。

Spring 容器蕴含两个重要的个性:面向切面编程(AOP)和管制反转(IOC)。面向切面编程是面向对象(OOP)的一种补充,在面向对象编程的过程中编程针对的指标是一个个对象,而面向切面编程中编程针对的指标是一个个切面。切面反对跨类型跨对象(如事务的切面能够加在任何中央)进行模块化。

<!–more–>

前言

AOP 是 Spring 的要害个性之一,尽管 Spring 的 IOC 个性并不依赖于 AOP(意味着你能够只应用 Spring 的 IOC 个性而不应用 AOP 个性),然而二者联合起来能够灵便的实现很多中间件解决方案。比方咱们常常应用的事务 (@Transaction),就是通过 AOP 计划实现的。本文重点介绍 AOP 编程中的一些术语,这些术语不仅仅局限于 Spring,它实用于所有的 AOP 编程。

  1. 切面 (Aspect):面向切面编程能够跨类跨对象进行切面编程,一个切面就是对一类横切关注点的模块化。
  2. 切入点 (JoinPoint):程序执行过程中的一个点,如办法调用、字段拜访和异样抛出等。
  3. 加强(Advice):用于对切面加强,蕴含前加强、后加强和盘绕加强。大多数 AOP 框架会对切入点进行拦挡,并在切入点保护一个拦截器链。
  4. 指标对象(TargetObject):蕴含一个或者多个切面的对象。
  5. AOP 代理(AOPProxy):通过 Java 动静代理或者 CGLib 加强失去的代理对象。
  6. 织入(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
@EnableAspectJAutoProxy
public 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;

@Aspect
public 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 expression
private 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;

@Aspect
public class BeforeExample {@Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    public void doAccessCheck() {// ...}
}
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {@Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {// ...}
}

后加强

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public 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;

@Aspect
public 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;

@Aspect
public 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 object
AspectJProxyFactory 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 aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy()

本文最先公布至微信公众号,版权所有,禁止转载!

正文完
 0