动态代理和AOP详解

在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功能,我们更需要学习的是其底层是怎么样的一个原理,而AOP的原理就是java的动态代理机制

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

InvocationHandler:

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

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

我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

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

proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

接下来我们来看看Proxy这个类

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了

h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

代理类在程序运行时创建代理类的方式叫做动态代理。与静态代理自己定义代理类,并且代理类在程序运行之前就编译完成的方式不同,动态代理的代理类并不是由代码所定义的,而是由系统的运行时动态生成的。

动态代理的用处:
既然动态代理是AOP的实现,动态代理的作用就应该同AOP相同,即主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。

动态代理的具体实现

动态代理就是在代理类和被代理类之间加入了一个实现了InvocationHandler的类,也叫事务处理器。

其具体实现步骤如下:

        1.创建被代理的类和接口

        2.创建一个接口实现InvocationHandler的类,且它必须实现invoke()方法

        3.通过Proxy的静态方法newProxyInstance来创建代理类的对象

        4.通过代理类的对象来调用方法
 

 

 

AOP 

AOP Aspect Oriented Programming )意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Aop Spring 中的作用
提供声明式事务;允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要
关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
切面( ASPECT ):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知( Advice ):切面必须要完成的工作。即,它是类中的一个方法。
目标(
Target ):被通知对象。
代理( Proxy ):向目标对象应用通知之后创建的对象。
切入点( PointCut ):切面通知 执行的 地点 的定义。
连接点( JointPoint ):与切入点匹配的执行点。
SpringAOP 中,通过 Advice 定义横切逻辑, Spring 中支持 5 种类型的 Advice:

 

  
使用 Spring 实现 Aop
【重点】使用 AOP 织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>

通过 Spring API 实现
public class Log implements MethodBeforeAdvice {

    // 要执行目标对象的方法
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName());
    }
}

 <bean id="Log" class="log.Log"/>
   <bean id="userService" class="Bean.UserServiceImpl"/>


    <!--aop配置-->
    <aop:config>
        <!--切入点--><!--切入点 expression:表达式匹配要执行的方法-->    <!--两个点便是任意参数-->
        <aop:pointcut id="pointcut" expression="execution(* Bean.UserServiceImpl.*(..))"/>
        <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
        <aop:advisor advice-ref="Log" pointcut-ref="pointcut"/>
    </aop:config>

  /*代理的是接口,所以转换接口类型*/
        UserService userService = (UserService) applicationContext.getBean("userService");

        // 执行方法
        userService.add();

第二种方式
自定义类来实现 Aop
目标业务类不变依旧是 userServiceImp
第一步 : 写我们自己的一个切入类
 



public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}

spring 中配置

<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二种方式:使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(*
com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>

第三种方式
使用注解实现
第一步:编写一个注解实现的增强类
package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}

Guess you like

Origin blog.csdn.net/weixin_47277897/article/details/120936958