spring的aop简介

反射

  1. 用另外的手段来创建对象、调用对象方法、使用对象的属性,常用在框架编程

  2. 获取类对象(*)
    Class<?> c = Class.forName(“包.类”); // 不需要事先import
    Class<?> c = 类.class; // 需要事先import

  3. 创建对象实例(*)
    c.newInstance(); // 调用无参构造,创建对象
    c.getConstructor(构造方法的参数类型).newInstance(构造方法参数)

  4. 获取方法(*)
    c.getMethods(); // 得到的是本类以及继承的所有public的方法
    c.getDeclaredMethods(); // 得到本类的所有方法(不考虑访问修饰符)
    c.getMethod(方法名字, 方法的参数类型…)
    c.getDeclaredMethod(方法名字, 方法的参数类型…)

  5. 获取属性
    c.getFields(); // 得到的是本类以及继承的所有public的属性
    c.getDeclaredFields(); // 得到本类的所有属性
    c.getField(“属性名”);
    c.getDeclaredField(“属性名”);

  6. 方法对象 Method(*)
    对象.方法名(参数); // 正常调用方法
    方法.invoke(对象, 参数); // 反射调用方法

  7. 属性对象 Field
    对象.属性 = 值; // 正常赋值
    对象.属性; // 正常取值

    属性.set(对象, 值); // 反射赋值
    属性.get(对象); // 反射取值

  8. 突破private的限制
    方法对象 Method.setAccessible(true); // 允许私有方法可以访问
    属性对象 Field.setAccessible(true); // 允许私有属性可以访问

spring的aop

1. pom.xml 新增依赖

<!-- 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>

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

<?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 .实现通知类

切点表达式匹配方式:

@Around("within(spring.service.UserServiceTarget)")

匹配到某一个目标类中的所有方法
@Around("execution(public * spring.service.UserServiceTarget.biz*(..) )")
匹配到某个类中的具体方法,可以根据方法名和参数列表进行匹配,此处可以匹配目标类下任意的biz开头的方法。
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
通过注解匹配,此处可以匹配所有加了Transactional注解的方法。

// 切面
@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;
    }
}

4. 使用

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

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, 依赖注入)
会检查这个类型是否有代理对象,如果有,优先返回代理对象

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

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

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

aop理解

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

生成代理的常见技术

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值