深入了解 Spring AOP

前言

使用了这么久spring,会发现,凡是java面试基本会被问到spring原理,源码,同时就会问到IOC和AOP这两大概念,下文主要讲解AOP的概念, AOP 的概念比较多, 而且这些概念经过了中文翻译后, 变得面目全非, 鉴于此, 在本章的开头, 着重整理一下Spring AOP 的各项术语的基本含义。

什么是 AOP

AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)

实际应用场景

实际运用总结八个字:纵向重复 横向抽取

比如:日志记录,性能统计,安全控制,权限管理,事务处理,异常处理,身份认证等

术语

Aspect(切面)

aspect 由 pointcut 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义. Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中。AOP的工作重心在于如何将增强织入目标对象的连接点上, 这里包含两个工作:

    如何通过 pointcut 和 advice 定位到特定的 joinpoint 上

    如何在 advice 中编写切面代码

可以简单地认为, 使用 @Aspect 注解的类就是切面。

advice(增强)

由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码.
许多 AOP框架, 包括 Spring AOP, 会将 advice 模拟为一个拦截器(interceptor), 并且在 join point 上维护多个 advice, 进行层层拦截。例如 HTTP 鉴权的实现, 我们可以为每个使用 RequestMapping 标注的方法织入 advice, 当 HTTP 请求到来时, 首先进入到 advice 代码中, 在这里我们可以分析这个 HTTP 请求是否有相应的权限, 如果有, 则执行 Controller, 如果没有, 则抛出异常. 这里的 advice 就扮演着鉴权拦截器的角色了。

advice的类型

  • before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)

  • after return advice, 在一个 join point 正常返回后执行的 advice

  • after throwing advice, 当一个 join point 抛出异常后执行的 advice

  • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.

  • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.

连接点(join point)

程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理。在 Spring AOP 中, 所谓连接点是指那些被拦截到的点。在 spring 中指的是目标对象中所有可以增强的方法,因为 spring 只支持方法类型的连接点。

切入点(point cut)

匹配 joinpoint 的谓词(a predicate that matches join points).Advice 是和特定的 pointcut 关联的, 并且在 pointcut 相匹配的joinpoint 中执行。在 Spring 中, 所有的方法都可以认为是 joinpoint,但是我们并不希望在所有的方法上都添加 Advice,而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint,给满足规则的 joinpoint 添加 Advice,简单来说所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。

关于joinpoint 和 pointcut 的区别

在 Spring AOP 中, 所有的方法执行都是 joinpoint.。而 pointcut 是一个描述信息, 它修饰的是 joinpoint, 通过 pointcut, 我们就可以确定哪些 joinpoint 可以被织入 Advice. 因此 joinpoint 和 pointcut 本质上就是两个不同纬度上的东西.advice 是在 join point 上执行的, 而 point cut 规定了哪些 join point 可以执行哪些 advice

目标对象(Target)

织入 advice 的目标对象. 目标对象也被称为 advised object 因为 Spring AOP 使用运行时代理的方式来实现 aspect, 因此 adviced object 总是一个代理对象(proxied object),注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类。

Proxy(代理)

一个类被 AOP 织入增强后,就形成代理对象。在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。

织入(Weaving)

是指把通知应用到切入点..

AOP Proxy

Spring AOP 默认使用标准的 JDK 动态代理(dynamic proxy)技术来实现 AOP 代理, 通过它, 我们可以为任意的接口实现代理。
当一个业务逻辑对象没有实现接口时, 此时Spring AOP 就默认使用 CGLIB 来实现 AOP 代理, Spring AOP 建议基于接口编程, 对接口进行 AOP 而不是类,下面编写两个代理模式的案例来说明

首先定义一个接口和实现类

//接口
public interface BuyHouse {

    void buyHosue();
    
}

//实现类
public class BuyHouseImpl implements BuyHouse {

    @Override
    public void buyHosue() {
        System.out.println("我要买房");
    }
}


JDK 的动态代理

/**
 * @author dingzan
 * jdk动态处理器
 */
public class DynamicProxyHandler implements InvocationHandler{

	private Object object;

    public DynamicProxyHandler(final Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("买房前准备");
        Object result = method.invoke(object, args);
        System.out.println("买房后装修");
        return result;
    }
    
}

//测试
public class DynamicProxyTest {

	public static void main(String[] args) {
            //创建目标类 
            BuyHouse buyHouse = new BuyHouseImpl();

//	    ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
//	    Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
//	    InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

		BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(
				BuyHouse.class.getClassLoader(), 
				new Class[]{BuyHouse.class}, 
				new DynamicProxyHandler(buyHouse)
		);
		proxyBuyHouse.buyHosue();
	}
}

    
Cglib 的动态代理

针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术 生成当前类的子类对象
 

public class CglibProxy implements MethodInterceptor {
	
    private Object target;
    
    public Object getInstance(final Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("买房前准备");
        Object result = methodProxy.invoke(object, args);
        System.out.println("买房后装修");
        return result;
    }
}

//测试
public class CglibProxyTest {

	public static void main(String[] args) {
		//目标对象
		BuyHouse buyHouse = new BuyHouseImpl();
		CglibProxy cglibProxy = new CglibProxy();
		//获取代理对象
		BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
		buyHouseCglibProxy.buyHosue();
	}
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值