Spring Aop和代理

一 java代理

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。通过代理,可以实现对目标对象的间接访问,即通过代理对象访问目标对象

 

代理模式中涉及到两方:

委托方、代理方

目标对象、代理对象

 

1 静态代理(掌握)

代理类和目标类实现相同的接口,在代理类中维护目标类的对象,以此实现对目标对象方法的调用。

优点:可以实现不修改目标对象代码的情况下,对目标对象的功能进行扩展。

缺点:代理对象与目标对象一样的接口,不易维护,一旦接口增加方法,则目标对象和代理类都需要维护

 

例如:老板通知开会,秘书传达指示

老板目标对象

秘书代理对象

 

动态代理

2 jdk代理

通过jdk代理,可以动态的在内容中构建代理对象(在程序运行时运用反射机制动态创建)

使用动态代理,要求目标对象必须实现了接口

 

1)定义接口

2)目标对象类实现接口

3)代理对象类实现InvocationHandler接口

public class XXX implements InvocationHandler{

 

//目标对象

private Object target;

 

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// TODO Auto-generated method stub

 

    String name = method.getName();

   if(name.equals("xianglong18")){

     //调用目标对象方法

     Object ret = method.invoke(this.target, args);

      System.out.println("增加的内容");

      return ret;

   }else{

     return method.invoke(this.target, args);

   }

}

 

//设置目标对象,返回代理对象

public Object bind(Object target){

   this.target = target;

   return Proxy.newProxyInstance(target.getClass().getClassLoader(), //类加载器

         target.getClass().getInterfaces(), //对象接口

          this);

    }

 

}

 

3 cglib代理

针对没有实现接口的目标对象进行代理

原理:生成目标对象的子类,子类中覆盖父类的方法

需要支持cglib的jar文件

本例中使用spring中提供的相关类

//实现方法拦截器接口

public class BookProxy implements MethodInterceptor{

 

//目标对象

private Object target;

 

public Object bind(Object target){

    this.target = target;

//增强类

    Enhancer en = new Enhancer();

//设置父类

    en.setSuperclass(this.target.getClass());

//设置回调

     en.setCallback(this);

//创建代理对象

    return en.create();

}

 

@Override

public Object intercept(Object proxy, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {

// TODO Auto-generated method stub

 

    System.out.println("调用方法前");

    System.out.println("Method:" + arg1.getName());

    System.out.println("Method:" + methodProxy.getSuperName());

//调用目标的方法(父类的方法)

    Object ret= methodProxy.invokeSuper(proxy, arg2);

    System.out.println("调用方法后");

    return ret;

}

}

 

 

spring在运行期创建代理,有两种代理方式:

若目标对象实现了若干接口,spring就会使用JDK动态代理。

若目标对象没有实现任何接口,spring就使用CGLIB库生成目标对象的子类。

 

二 AOP

1 AOP概述

Aspect Oriented Program面向切面的编程,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

AOP是OOP(Object Oriented Programming,面向对象编程)的补充和完善。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

面向切面的编程将关注点的代码与业务逻辑分离

 

2 实现原理

AOP技术是建立在Java语言的反射机制与动态代理(jdk代理、cglib代理)机制之上的。

业务逻辑组件在运行过程中,AOP容器会动态创建一个代理对象供使用者调用,该代理对象已经将切面成功切入到目标方法的连接点上,从而使切面的功能与业务逻辑的功能同时得以执行。

调用者直接调用的其实是AOP容器动态生成的代理对象,再由代理对象调用目标对象完成原始的业务逻辑处理,而代理对象则已经将切面与业务逻辑方法进行了合成。

 

  1. 连接点(Joinpoint):是程序执行的某一个特定位置,是一个虚拟概念。如类中某一个方法调用前/调用后,方法抛出异常后等等,一个类或一段程序代码拥有一些具有边界性质的特定点
  1. 关注点(Advice通知、增强):织入目标类连接点上特定的代码
  1. 切面(aspect):关注点形成的类,切面类
  1. 切入点(pointcut切点):需要执行的目标方法
  1. 织入:将增强添加到目标类具体连接点上的过程

 

2 手动实现aop

 

3 xml配置实现aopspring给咱们提供的

  1. 需要导入jar包:

spring-aop-5.1.5.RELEASE.jar

aopalliance.jar

aspectjrt.jar              aspectj-1.8.2\lib

aspectjweaver.jar          aspectj-1.8.2\lib

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>1.8.2</version>

</dependency>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.2</version>

</dependency>

 <dependency>

    <groupId>aopalliance</groupId>

    <artifactId>aopalliance</artifactId>

    <version>1.0</version>

  </dependency>

 

  1. 引入aop的名称空间

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd"> 

 

3)创建切面类对象和aop配置

<!-- aop的配置 -->

   <aop:config>

    <!-- 切入点,对哪些方法织入切面方法 -->

    <!--

execution([修饰符] 返回值类型 方法名(参数) [异常模式]) []表示可选部分

第一个* 任意返回类型 

    com.rr.aop 包

    第二个*  包下面的任意类

    *(..)  任意名称和参数的方法

    -->

    <aop:pointcut expression="execution(* com.rr.aop.*.*(..))" id="pc"/>

   

    <!-- 切面类,通过ref指定对应的切面类的bean -->

    <aop:aspect ref="logAOP">

    <!-- 前置通知 ,执行目标方法前执行的方法 -->

    <aop:before method="before" pointcut-ref="pc"/>

    <!-- 后置通知 ,不管目标方法正常执行还是异常,都会调用 -->

    <aop:after method="end" pointcut-ref="pc"/>

    <!-- 正常返回通知 -->

    <aop:after-returning method="afterReturn" pointcut-ref="pc"/>

    <!-- 环绕通知 -->

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

<!-- 异常返回通知

   throwing属性的值要和异常通知使用的方法中的异常变量的参数名 保持一致

    -->

   <aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="pc"/>

    </aop:aspect>

   

   </aop:config>

 

切入点表达式用法:

参考帮助文档11.2.3 Declaring a pointcut  切点的表达式

Some examples of common pointcut expressions are given below.

  • the execution of any public method:

execution(public * *(..))

  • the execution of any method with a name beginning with "set":

execution(* set*(..))

  • the execution of any method defined by the AccountService interface:

execution(* com.xyz.service.AccountService.*(..))

  • the execution of any method defined in the service package:

execution(* com.xyz.service.*.*(..))

  • the execution of any method defined in the service package or a sub-package:

execution(* com.xyz.service..*.*(..))

  • any join point (method execution only in Spring AOP) within the service package:

within(com.xyz.service.*)

  • any join point (method execution only in Spring AOP) within the service package or a sub-package:

within(com.xyz.service..*)

  • any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:

this(com.xyz.service.AccountService)

 

 

五类通知类型:

before前置通知[Before advice]:在切点前面执行,前置通知不会影响点的执行,除非此处抛出异常。 
after-returning正常返回通知[After returning advice]:在切点正常执行完成后执行,如果切点抛出异常,则不会执行。 
after-throwing异常返回通知[After throwing advice]:在切点抛出异常后执行。 
afrter返回通知[After (finally) advice]:在切点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。 
around环绕通知[Around advice]:环绕通知围绕在切点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。 

 

54 注解方式实现aop

@Aspect  表示切面类

@Order   执行顺序,数字越小,越先执行

@PointCut  切点

@Before  前置通知

...

 

@Component

@Aspect  //指定当前类为切面类

public class LogAop {

 

// 指定切入点表单式: 拦截哪些方法; 即为哪些类生成代理对象

@Pointcut("execution(* com.rr.xmlaop.MyDao.*(..))")

public void pointcut(){

 

}

 

//前置通知 执行目标方法前执行

//JoinPoint 连接点对象,可以不写,通过该对象,可以获取目标方法相关的信息

@Before("pointcut()")

public void begin(JoinPoint jp) {

      System.out.println("begin:");

      System.out.println(jp.getSignature().getName());

}

 

//后置通知,执行目标方法后执行,不论是否异常

@After("pointcut()")

// public void end(){

public void end(JoinPoint jp) throws Exception, SecurityException {

 

}

 

//正常返回后通知,调用目标方法结束后执行,在代理返回前执行,异常不执行

@AfterReturning("pointcut()")

public void afterReturn(){

    System.out.println("after return");

}

 

//环绕方法,必须使用ProceedingJoinPoint参数

@Around("pointcut()")

public void around(ProceedingJoinPoint pjp) throws Throwable{

       System.out.println("环绕前....");

       pjp.proceed();  // 执行目标方法

       System.out.println("环绕后....");

}

 

// 异常返回时执行的方法

// 异常返回通知

@AfterThrowing(pointcut="pointcut()", throwing="e")

public void afterThrowing(Exception e){

       System.out.println("after throw :" + e.getMessage());

     }

}

<!-- aop的扫描 -->

   <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值