Spring学习笔记-AOP

AOP概念

AOP:Aspect Oriented Programming,面向切面编程。

通过预编译运行期动态代理实现程序功能的统一维护的一种技术。

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

AOP相关概念

概念

说明

切面(Aspect

一个关注点的模块化,可能会横切多个对象。

连接点(Joinpoint

符合条件的每个被拦截处为连接点

通知/建言(Advice

在切面某个特定的连接点上执行的动作,Before、After、Around、After-returning、After-throwing。

切入点(Pointcut

匹配连接点的断言,在AOP中Advice和一个Pointcut表达式关联

引入(Introduction

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

目标对象(TargetObject)

被一个或者多个切面所通知的对象

AOP代理(AOP Proxy)

AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)

织入(Weaving)

把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入

 

Spring框架中AOP,提供了声明式的企业服务,特别是EJB的替代服务的声明。允许用户制定自己的切面,以完成OOP与AOP的互补使用。

Spring AOP,纯java实现,无需特殊的编译过程,不需要控制类加载器层次。目前只支持方法执行连接点(通知Spring Bean的方法执行)。侧重于提供一种AOP实现和Spring IoC容器的整合,用于帮助解决企业应用中的常见问题。

 

schema-defined aspects只支持singleton model,即所有基于配置文件的切面配置只支持单例模式。

 

基于配置的实现(理解)

配置切面Aspect<aop:aspect>

Spring所有切面和通知器都必须放在<aop:config>内,可以配置多个<aop:config>元素,每个可以包含pointcut,advisor,aspect元素(必须按照这个顺序声明)。

大量使用了Spring的自动代理机制。

<aop:config>
   <aop:aspect id="moocAspectAOP" ref="moocAspect">
         <aop:pointcut expression="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" id="moocPiontcut"/>
         <aop:before method="before" pointcut-ref="moocPiontcut"/>
         <aop:after-returning method="afterReturning" pointcut-ref="moocPiontcut"/>
         <aop:after-throwing method="afterThrowing" pointcut-ref="moocPiontcut"/>
         <aop:after method="after" pointcut-ref="moocPiontcut"/>
         <aop:around method="around" pointcut-ref="moocPiontcut"/>
      
         <aop:around method="aroundInit" pointcut="execution(* com.imooc.aop.schema.advice.biz.AspectBiz.init(String, int))
                     and args(bizName, times)"/>

         <aop:declare-parents types-matching="com.imooc.aop.schema.advice.biz.*(+)" 
                  implement-interface="com.imooc.aop.schema.advice.Fit"
                  default-impl="com.imooc.aop.schema.advice.FitImpl"/>
   </aop:aspect>
</aop:config>

 

配置切入点Pointcut<aop:pointcut>

expression表达式可以查阅相关文档,用到的时候再学习,不需死记硬背。。。

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

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

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

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

execution(* com.xyz.service...(..))//(三个点)切入点为执行该包及其子包下所有方法时

within()

this()

target()

args()

bean()

@target

@within

@annotation

 

<aop:pointcut expression="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" id="moocPiontcut"/>

 

配置通知Advice<aop:before>..<aop:after>

Advice的类型:前置通知(aop:before)、返回后通知(aop:after-returning)、抛出异常后通知(after-throwing)、后通知(aop:after)、环绕通知(aop:around)

<aop:aspect id="moocAspectAOP" ref="moocAspect">
      <aop:pointcut expression="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" id="moocPiontcut"/>
      <aop:before method="before" pointcut-ref="moocPiontcut"/>

说明:定义一个切面“MOOCAspectAOP”(对应的Java Bean为ref=”moocAspect”),定义切点为“moocPiontcut”(所有以Biz结尾的类的所有方法),声明前置通知“before”(在bean中实现的方法)。则会在Biz结尾类的方法调用前调用before方法,before/after-returning/after/after-throwing类似。

环绕通知Around advice

通知方法第一个参数必须是ProceedingJoinPoint类型

例1(方法无参数)

<aop:around method="around" pointcut-ref="moocPiontcut"/>

 

public Object around(ProceedingJoinPoint pjp) {
   Object obj = null;
   try {
      System.out.println("MoocAspect around 1.");
      obj = pjp.proceed();//代表具体业务方法执行,obj为返回值
      System.out.println("MoocAspect around 2.");
   } catch (Throwable e) {
      e.printStackTrace();
   }
   return obj;
}

 

 

 

例2(方法有参数)

<aop:around method="aroundInit" pointcut="execution(* com.imooc.aop.schema.advice.biz.AspectBiz.init(String, int))
            and args(bizName, times)"/>

 

public Object aroundInit(ProceedingJoinPoint pjp, String bizName, int times) {
   System.out.println(bizName + "   " + times);
   Object obj = null;
   try {
      System.out.println("MoocAspect aroundInit 1.");
      obj = pjp.proceed();//代表具体业务方法执行,obj为返回值
      System.out.println("MoocAspect aroundInit 2.");
   } catch (Throwable e) {
      e.printStackTrace();
   }
   return obj;
}

如果两个都声明则都会执行环绕通知,顺序和声明顺序有关。

 

配置Introductions<aop:declare-parents>

简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些(被匹配的)对象。

由<aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent

<aop:declare-parents
types-matching="com.imooc.aop.schema.advice.biz.*(+)"
implement-interface="com.imooc.aop.schema.advice.Fit"
default-impl="com.imooc.aop.schema.advice.FitImpl"/>

types-matching=匹配什么样的类

implement-interface=具体使用哪一个接口(匹配的类中并没有实现)

default-impl=指定接口的实现类

Fit fit = (Fit)super.getBean("aspectBiz");
fit.filter();
fit.biz();

网上的一个解释,有助于理解:

我来解释(逗逼)一下,赞同楼上说法,introduction译成“引入”较合理。

那个introduction的意思是:

有个类叫小明(biz),小明隔壁住着老王(FitImpl),老王实现了一个技能叫开豪车(Fit

现在上帝声明了一个切面,这个切面给小明指定一个新的爹叫老王,于是小明每次叫爸爸的时候就能开豪车了~

类匹配(小明):type-matching

接口(开豪车):implement-interface

接口的实现类(老王):default-impl

引入的作用:小明干自己的事的时候(叫爸爸)能莫名其妙地开上豪车而不用做多余的工作,这些工作由上帝(AOP)帮他完成,这叫“解耦”

 

配置advisors???

 

基于注解的实现(主流)

l  @Aspect声明是一个切面

l  @After、@Before、@Around定义建言(advice),可以直接将拦截规则(切点)作为参数

l  @PointCut专门定义拦截规则,然后在建言的参数中调用

 

定义拦截规则的注解,使用@Action注解其他方法(切点)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
    String name() default "默认name";
}

定义Service类,使用注解定义切点,也可以使用匹配规则(在切面中定义)

@Service
public class DemoAnnotationService {
    @Action(name = "注解式拦截的信息")//不传参数则用默认值
    public void fun1(){System.out.println("提供服务1");}

    public void fun2(){System.out.println("提供服务2");}
}

定义切面类,

@Aspect//定义为切面
@Component//Spring管理的普通Bean
public class LogAspect {
    @Pointcut("@annotation(com.sunlin.test_SpringByMaven.aop.Action)")//注解声明的切点
    public void annotationPointCut(){}

    @Before("annotationPointCut()")//声明Before建言,使用@PointCut为切点
    public void before1(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        Action action = method.getAnnotation(Action.class);
        System.out.println("注解式拦截:"+action.name());
    }

    //使用拦截规则作为参数
    @Before("execution(* com.sunlin.test_SpringByMaven.aop.DemoAnnotationService.fun2(..))")
    public void before2(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("方法规则式拦截:"+method.getName());
    }
}

定义配置类

@Configuration
@ComponentScan("com.sunlin.test_SpringByMaven.aop")
@EnableAspectJAutoProxy//开启SpringAspectJ的支持
public class AopConfig {
}

运行

@Test
public void test_AnnotationAOP() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(AopConfig.class);

    DemoAnnotationService demoAnnotationService =
            context.getBean(DemoAnnotationService.class);

    demoAnnotationService.fun1();
    demoAnnotationService.fun2();
    context.close();
}

输出:

注解式拦截:注解式拦截的信息

提供服务1

方法规则式拦截:fun2

提供服务2

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值