42_ spring的aop

1.实现事务代码重用

思路:
1) 提供了一个代理类(Proxy)
调用通知类的invoke方法
获取方法对象和方法实际参数
与目标要实现相同的接口(目的是让使用者察觉不出是代理替换了原来的目标)
2) 提供了一个通知类(Advice)
实现了重复代码(事务的重复代码)
反射调用了目标对象的方法
把重复代码和目标方法联系在了一起

jdk提供了一个通用的接口 InvocationHandler (通知接口)

2.动态代理

刚才自己实现的代理类称之为静态代理, jdk 还提供了动态代理技术, 它是指在程序运行期间,
由jdk生成这个代理类和他的实例对象.

正常使用类:*.java -> javac -> .class -> java -> 加载该class到虚拟机
动态代理 直接生成了
.class字节码, 加载该class到虚拟机

    java    2行

Proxy.newProxyInstance(类加载器, 要实现的接口数组, InvocationHandler);

3. spring的aop

3.1 pom.xml 新增依赖

    xml    19行

<!-- spring aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.3.17.RELEASE</version>
</dependency>

<!-- 第三方 aop依赖 aspectJ -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>

3.2 在spring的配置文件中加入aop的相关标签

    xml    27行

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目标类 -->
    <bean id="userService" class="spring.service.UserServiceTarget">
    </bean>

    <!-- 通知类 -->
    <bean id="transactionAdvice" class="spring.advice.TransactionAdvice">
    </bean>

    <!-- 让相关注解生效, 还能够帮我们生成底层的代理对象 -->
    <aop:aspectj-autoproxy/>
</beans>

3.3 实现通知类

    java    26行

// 切面
@Aspect
public class TransactionAdvice {

    @Around("within(spring.service.UserServiceTarget)")
    // 通过切点表达式,把通知和目标结合在一起
    // 作用当UserServiceTarget类中任意一个方法执行时(biz1,biz2) 就会和下面的transaction方法结合在一起了


    // 通知类型:环绕通知,决定了这个通知方法会和哪些目标方法结合
    // 这个方法称为通知方法,它其中包含了一些重复逻辑,另外它负责调用目标方法
    public Object transaction(ProceedingJoinPoint pjp) { // pjp 内部去调用目标的方法
        System.out.println("开始事务");
        Object r = null;
        try {
            // 调用目标
            r = pjp.proceed(); // 目标  biz1,  biz2...

            System.out.println("提交事务");
        } catch (Throwable e) {
            System.out.println("回滚事务");
        }
        return r;
    }
}

3.4 使用

使用上,尽量实现了无侵入的效果,原来的代码不受影响

    java    12行

ClassPathXmlApplicationContext context
        = new ClassPathXmlApplicationContext("spring.xml");

// getBean方法返回的不是target对象,而是由spring容器生成的代理对象,底层产生代理的技术就是:jdk动态代理技术
UserService service = context.getBean(UserService.class);

service.biz1();
// 代理对象内部调用了通知TransactionAdvice对象, 检查是否和通知方法上的切点表达式符合
// 如果符合,就执行该通知方法, 通知方法内部再调用目标

System.out.println("service真正类型是:"+service.getClass());

aop 的概念

代理 proxy
目标 target 被代理的对象
通知 advice 包含了可重用代码(例如事务代码),并调用目标
切点 pointcut 把通知和目标结合一起,它定义的是一种匹配规则
切面 aspect 切面=通知+切点

aop (aspect切面 oriented 面向 programming 编程) 面向切面编程
oop 面向对象编程

切点 pointcut

aspectj中定义的一些切点表达式

  • within(包名.类名) 这个类中所有方法执行时,都会应用通知方法
  • execution(访问修饰符 返回值类型 包名.类名.方法名( 参数类型... ))
    注意* 可以匹配任意类型, 可以出现在方法返回值,方法名,类名当中
    注意.. 可以匹配方法参数,表示参数的个数和类型都是任意的
  • @annotation(包名.注解名)
    它是根据方法上的注解进行匹配, 如果有注解则匹配

通知类型

环绕通知: @Around(切点表达式) 加在通知方法上(功能最全的), 通知方法环绕着目标方法
前置通知: @Before 通知代码只会出现在目标方法之前
正常结束通知: @AfterReturning 在目标方法正常结束后,调用通知方法
异常通知: @AfterThrowing 在目标方法出现异常后,调用通知方法
结束通知: @After 在目标方法结束后(正常或异常),总会调用的通知

统计每个业务方法的执行时间

调用流程

容器启动时

  1. spring会检查容器,看是否需要为这些bean创建代理对象
  2. 检查所有的切点表达式,如果匹配到了目标,就为该目标创建代理对象

获取对象时(getBean, 依赖注入)
会检查这个类型是否有代理对象,如果有,优先返回代理对象

调用方法时
调用了代理对象的方法,代理对象会首先经过通知类(多个)

检查切点,如果切点匹配,再进行下面的通知调用

根据不同的通知类型进行调用:
例如前置通知先调通知,再调目标
如果是环绕通知,先调用通知,通知内部调用目标
...

概念

面向切面编程:就是把重复的逻辑抽取为一个通知方法,
然后通过切点匹配来决定哪些目标要应用这些通知。
其中利用了代理的技术,在代理中检查切点是否匹配,调用通知(多个),最后调用目标

生成代理的常见技术

jdk的动态代理, 只能针对接口实现代理, jdk动态代理在高版本中性能优于cglib
cglib动态代理, 既可以针对接口实现代理,也可以生成子类充当目标父类的代理

spring默认使用jdk动态代理,如果没有实现接口,就使用cglib代理

javasist动态代理 struts2, hibernate

猜你喜欢

转载自blog.csdn.net/Lisiluan/article/details/88369165
今日推荐