AOP动态代理

AOP底层原理:动态代理

动态代理

特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
1)基于接口的动态代理
涉及的类:Proxy
提供者:JDK官方
创建代理对象:使用Proxy类中newProxyInstance方法
要求:被代理类最少实现一个接口,如果没有则不能使用

newProxyInstance方法的参数

  • ClassLoader:类加载器
    用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
  • Class[]:字节码数组
    用于让代理对象和被代理对象有相同方法。固定写法
  • InvocationHandler:用于提供增强的代码
    一般都是写一个该接口的实现类,通常情况下都是匿名内部类 此接口的实现类都是谁用谁写
接口名 新对象名 = (接口名)Proxy.newProxyInstance(
    被代理的对象.getClass().getClassLoader(),	// 被代理对象的类加载器,固定写法
    被代理的对象.getClass().getInterfaces(),	// 被代理对象实现的所有接口,固定写法
    new InvocationHandler() {	// 匿名内部类,通过拦截被代理对象的方法来增强被代理对象
        /* 被代理对象的任何方法执行时,都会被此方法拦截到
        	其参数如下:
                proxy: 代理对象的引用,不一定每次都用得到
                method: 被拦截到的方法对象
                args: 被拦截到的方法对象的参数
        	返回值:
        		被增强后的返回值
		*/
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if("方法名".equals(method.getName())) {
            	// 增强方法的操作
                rtValue = method.invoke(被代理的对象, args);
                // 增强方法的操作
                return rtValue;
            }          
        }
    });

2)基于子类的动态代理
涉及的类:Enhancer
提供者:第三方cglib库
创建代理对象 :使用Enhancer类中create方法
要求:被代理类不能是最终类

方法的参数

  • Class:字节码
    用于指定被代理对象的字节码。
  • Callback:用于提供增强的代码
    一般都是写一个该接口的实现类,通常情况下都是匿名内部类,此接口的实 现类都是谁用谁写。
    一般都是写该接口的子接口实现类:MethodInterceptor

AOP

相关术语

  • Joinpoint(连接点): 被拦截到的方法
  • Pointcut(切入点): 我们对其进行增强的方法
  • Advice(通知/增强): 对切入点进行的增强操作
  • 包括前置通知,后置通知,异常通知,最终通知,环绕通知
  • Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。
  • Aspect(切面): 是切入点和通知的结合

spring中基于XML的AOP配置步骤

1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
属性

  • id :是给切面提供一个唯一标识
  • ref:是指定通知类bean的Id。

4、在aop:aspect标签的内部使用对应标签来配置通知的类型
具体的通知类型:
aop:before: 配置前置通知
aop:after-returning: 配置后置通知
aop:after-throwing: 配置异常通知
aop:after: 配置最终通知
aop:around: 配置环绕通知,可以在代码中手动控制增强方法何时执行
属性

  • method:用于指定通知类中哪个方法是增强方法
  • pointcut:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
  • ponitcut-ref: 指定切入点的表达式的id
    5、aop:pointcut标签配置切入点表达式,指定对哪些方法进行增强
    属性
  • id: 指定切入点表达式的id
  • expression: 指定切入点表达式
<aop:config>
    <aop:aspect id="logAdvice" ref="logger">
        <!--配置切入点表达式-->
        <aop:pointcut expression="execution(* cn,maoritian.service.impl.*.*(..))" id="pt1"></aop:pointcut>
        <!--配置各种类型的通知-->
        <aop:before method="printLogBefore" pointcut-ref="pt1"></aop:before>
        <aop:after-returning method="printLogAfterReturning" pointcut-ref="pt1"></aop:after-returning>
        <aop:after-throwing method="printLogAfterThrowing" pointcut-ref="pt1"></aop:after-throwing>
        <aop:after method="printLogAfter" pointcut-ref="pt1"></aop:after>
		<!--环绕通知一般单独使用-->       
        <!-- <aop:around method="printLogAround" pointcut-ref="pt1"></aop:around> -->
    </aop:aspect>
</aop:config>

6、切入点表达式的写法:
关键字:executinn(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名.类名.方法名(参数列表)
一般我们都是对业务层所有实现类的所有方法进行增强
因此切入点表达式写法通常为
* com.potato.service.impl.*.*(..)
访问修饰符可以省略

7、Spring框架为我们提供一个接口:ProceedingJoinPoint
该接口的proceed方法相当于明确调用切入点方法
该接口可以作为环绕通知的方法参数,程序执行时,spring会为我们提供该接口的实现类
ProceedingJoinPoint对象的getArgs()方法返回被拦截的参数
ProceedingJoinPoint对象的proceed()方法执行被拦截的方法

// 环绕通知方法,返回Object类型
public Object printLogAround(ProceedingJoinPoint pjp) {
    Object rtValue = null;
    try {
        Object[] args = pjp.getArgs();        
        printLogBefore();			// 执行前置通知
    	rtValue = pjp.proceed(args);// 执行被拦截方法
        printLogAfterReturn();		// 执行后置通知
    }catch(Throwable e) {
        printLogAfterThrowing();	// 执行异常通知
    }finally {
        printLogAfter();			// 执行最终通知
    }
    return rtValue;
}

使用注解配置AOP

半注解配置AOP

开启对注解AOP的支持

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

1 用于声明切面的注解

@Aspect: 声明当前类为通知类,该类定义了一个切面.相当于xml配置中的aop:aspect标签

2 用于声明通知的注解

@Before: 声明该方法为前置通知.相当于xml配置中的aop:before标签
@AfterReturning: 声明该方法为后置通知.相当于xml配置中的aop:after-returning标签
@AfterThrowing: 声明该方法为异常通知.相当于xml配置中的aop:after-throwing标签
@After: 声明该方法为最终通知.相当于xml配置中的aop:after标签
@Around: 声明该方法为环绕通知.相当于xml配置中的aop:around标签
属性:
value: 用于指定切入点表达式或切入点表达式的引用

3 用于指定切入点表达式的注解
@Pointcut: 指定切入点表达式,其属性如下:
value: 指定表达式的内容
@Pointcut注解没有id属性,通过调用被注解的方法获取切入点表达式

@Component("logger")
@Aspect	//表示当前类是一个通知类
public class Logger {

    // 配置切入点表达式
    @Pointcut("execution(* cn.maoritian.service.impl.*.*(..))")
    private void pt1(){} 
    
    // 通过调用被注解的方法获取切入点表达式
	@Before("pt1()")
    public void printLogBefore(){
        System.out.println("前置通知Logger类中的printLogBefore方法开始记录日志了。。。");
    }

    // 通过调用被注解的方法获取切入点表达式
    @AfterReturning("pt1()")
    public void printLogAfterReturning(){
        System.out.println("后置通知Logger类中的printLogAfterReturning方法开始记录日志了。。。");
    }
    
    // 通过调用被注解的方法获取切入点表达式
	@AfterThrowing("pt1()")
    public void printLogAfterThrowing(){
        System.out.println("异常通知Logger类中的printLogAfterThrowing方法开始记录日志了。。。");
    }
}

纯注解配置AOP

在Spring配置类前添加@EnableAspectJAutoProxy注解,可以使用纯注解方式配置AOP

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值