Spring:AOP之代理模式

本文详细介绍了Spring中的AOP概念,包括代理模式的基本原理、静态代理和动态代理的实现,以及如何通过配置文件和注解方式实现AOP。此外,还对比了JDK动态代理和CGLIB代理的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式简介

问题提出

当项目的一些方法完成后,需要增加一些功能如安全性检查,若在原来方法上增加这些功能,就需要修改源代码。
最好的办法是使用一个程序,完成增加的功能,并能调用原来方法 ,这样就不需要修改原来的代码。该程序就是代理

代理原则

可控制原对象,但不会改变原对象的接口

代理的种类

静态代理(能看到的,实际存在的代理类)
动态代理(是在运行期生成出来的)

被代理对象的要求

实现接口的方式完成方法的创建。

静态代理

代理类

  • 必须和目标类实现同一个接口
  • 设置关联属性,通过关联属性指向目标类对象。

代理过程

在应用程序中调用代理类中的方法,完成要求的功能。

例: 在指定方法中加入权限验证

目标类:

在这里插入图片描述
代理类(切面类,实现和目标类同一个接口):

在这里插入图片描述
主配文件:

在这里插入图片描述
应用程序中直接调用代理类中的方法:

加粗样式

动态代理

在程序运行过程中能够创建代理对象,完成自动装配。

思想: 把横切性的关注点单独拿出来模块化,提高可维护性。

特点: 代理类不再存在,将在运行中生成。

切面类

切面类的特点

  • 实现invocationHandler接口;
  • 关联属性,指向目标对象;
  • 重写invoke方法。

代码示例:

package com.bjsxt.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SecurityHandler implements InvocationHandler {

	/*用来指向目标对象的关联属性*/
	private Object targetObject;
	
	public Object newProxy(Object targetObject) {
		/*关联属性指向目标对象的地址*/
		this.targetObject = targetObject;

		System.out.println("----------newProxy()---------------");	
		/**
		 * 生成代理类对象
		 * 第一个参数设置代码使用的类装载器,一般采用和目标类相同的类装载器
		 * 第二个参数设置代理类实现的接口,和目标类实现同一个接口
		 * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
		 */
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
									  targetObject.getClass().getInterfaces(), 
									  this); 
				
	}

	/*
	 * 重写invoke方法
	 * method封装目标对象的方法名
	 * args封装目标对象的参数
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		checkSecurity();
		Object ret = null;
		try {
			//调用目标方法的真实实现
			ret = method.invoke(this.targetObject, args);
			
		}catch(Exception e) {
			e.printStackTrace();
			throw new java.lang.RuntimeException(e);
		}
		return ret;
	}

	/*
	 * 新增的安全检查方法
	 */
	private void checkSecurity() {
		System.out.println("----------checkSecurity()---------------");
	}
	
}

使用切面类

切面类直接在应用程序中运行:

在这里插入图片描述
使用方法:

  1. 创建切面类对象(Aspect),向Aspect中注入目标对象;
  2. 调用Aspect的方法:
    ① 将目标对象传给关联属性(targetObject);
    ② Aspect根据传入的3个参数——目标对象的类加载器,目标对象实现的接口,实现invoke方法的对象(切面类对象)——创建代理对象
    ③ 返回创建的代理对象。

生成的代理对象具有以下特点:

  1. 和目标类实现同一个接口
  2. 和目标对象有相同的方法声明
  3. 一定拥有实现InvocationHandler接口的对象的地址

注: 目标对象的类加载器和实现的接口都是通过反射获得的。

invoke方法:

一旦调用目标类的方法,默认先通过代理类对象执行Aspect中的invoke方法:

  • 根据关联属性通过反射和传给invoke的参数method中封装的方法名拿到目标对象中对应的方法(如本例:this.targetObject.getClass().getMethod(addUser(String.class,String.class))););

  • 通过传给invoke的参数args数组对象中封装的实参调用对应的方法;

  • 返回调用结果。

采用配置文件的方式对AOP支持

定义切面类

在这里插入图片描述
xml配置文件

在这里插入图片描述
应用示例:

package com.bjsxt.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

	public static void main(String[] args) {
		BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
		
		UserManager userManager = (UserManager)factory.getBean("userManager");
		
		userManager.addUser("张三", "123");
		userManager.deleteUser(1);
	}
}

查看运行结果:

在这里插入图片描述
在add开头的方法前执行了checkSecurity方法

查看对象的状态:

在这里插入图片描述

PS: COI容器中原本目标对象的id(userManager)对应的value指向的是代理对象,而目标对象纳入代理对象的管理。

注: 如果Pointcut中没有任何方法,则不创建代理对象,不会织入Advice,而是直接执行目标对象的方法。

采用注解方式实现对AOP的支持

引进相关的spring依赖库:

  • SPRING_HOME/dist/spring.jar
  • SPRING_HOME/lib/jakarta-commons/commons-logging.jar
  • SPRING_HOME/lib/log4j/log4j-1.2.14.jar
  • SPRING_HOME/lib/aspectj/*.jar(非常强大的AOP实现)

配置xml文件:

  • 启用对Annotation的支持:在配置文件applicationContext.xml中加入<aop:aspectj-autoproxy/ >
  • 将Aspect和目标对象配置在IOC容器中

在这里插入图片描述
注: 在spring3.0及以上的版本中是默认启动注释支持的,无需手动启动。

代码示例:

切面类:
在这里插入图片描述

目标类:

在这里插入图片描述

测试代码:

package com.bjsxt.spring;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

	public static void main(String[] args) {
		BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
		
		UserManager userManager = (UserManager)factory.getBean("userManager");
		
		userManager.addUser("张三", "123");
		userManager.deleteUser(1);
	}
}

运行结果:

在这里插入图片描述

使用CGLIB代理实现对AOP的支持

说明:
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

引入CGLIB库: SPRING_HOME/cglib/*.jar

配置:
与通过配置文件实现对AOP的支持相同

在这里插入图片描述
如果目标类没有实现任何接口,自动使用CGLIB代理。

JDK动态代理和CGLIB字节码生成的区别

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法

注: 因为是继承,所以该类或方法最好不要声明成final

说明:本文仅用作学习笔记,无其他用途,如有冒犯可联系本人删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值