Spring框架(五)—— Spring 框架代理模式

一、Spring 框架代理模式

1、代理模式概述

(1)代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能。

(2)代理模式通常是通过持有目标对象的引用来实现的,为了便于描述我们将代理模式中被代理对象称为目标对象,负责进行代理的对象被称为代理对象。
在这里插入图片描述

2、代理模式之静态代理

(1)静态代理概述
若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

(2)静态代理步骤
① 新建业务层接口 ProductService 接口

public interface ProductService<E> {
	public void insertProduct() throws Exception;
	public void deleteProduct() throws Exception;
}

② 新建业务层 ProductServiceImp 实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class ProductServiceImp<E> implements ProductService<E>{
	@Override
	public void insertProduct() throws Exception {
		System.out.println("执行业务层新增操作代码");
	}
	@Override
	public void deleteProduct() throws Exception {
		System.out.println("执行业务层删除操作代码");
	}
}

③ 新建业务层代理类 ServiceProxy 实现业务层接口,在代理类中定义一个业务层接口的成员变量

public class ServiceProxy<E> implements ProductService<E>{
	private ProductService<Products> productService;
	public ServiceProxy(ProductService<Products> productService){
		this.productService = productService;
	}
	@Override
	public void insertProduct() throws Exception {
		try {
			System.out.println("执行获取连接,打开事务的代码");
			productService.insertProduct();
			System.out.println("执行关闭事务的代码");
		} catch(Exception e) {
			System.out.println("执行事务回滚的代码");
			throw e;
		} finally{
			System.out.println("关闭连接");
		}
	}

	@Override
	public void deleteProduct() throws Exception {
		try {	
			System.out.println("执行获取连接,打开事务的代码");
			productService.deleteProduct();
			System.out.println("执行关闭事务的代码");
		} catch(Exception e) {
			System.out.println("执行事务回滚的代码");
			throw e;
		} finally{
			System.out.println("关闭连接");
		}
	}
}

④ 测试类

public class Test {
	public static void main(String[] args) {
		ProductService<Products> productService = new ProductServiceImp<Products>();
		ServiceProxy<Products> proxy = new ServiceProxy<Products>(productService);
		try {
			proxy.insertProduct();
			proxy.deleteProduct();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

(3)静态代理缺点
静态代理适用性不强,代理对象和被代理对象之前是一种强耦合,代理对象必须知道被代理对象具体的变量或方法,从而进行调用,一旦被代理对象多起来,那就需要创建多个代理,工作量太大,同样不好维护和管理。

3、代理模式之 JDK 动态代理

(1)JDK 动态代理概述

  • 代理类在程序运行时创建的代理方式被成为动态代理。
  • 在JDK1.3之后加入了可协助开发的动态代理功能,不必为特定对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理者(Handler)服务于各个对象,也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
  • 相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
  • 一个处理者的类设计必须实现 java.lang.reflect.InvocationHandler 接口,通过 InvocationHandler 接口实现的动态代理只能代理接口的实现类。
  • 动态代理的基本思路都是在程序运行期间动态的生成了新的 class 文件,可以理解为是 java 根据我们的需求动态的编写了一个类。通过该类对我们的目标对象进行代理。
    在这里插入图片描述
    (2)重要API
  • InvocationHandler:直译为调用处理器,使用该类可以封装我们需要执行的代码体,最后 Proxy 会根据该对象中包含的代码体动态的生成一个新的 class 文件,该 class 文件就是我们的代理对象类。
  • Proxy:代理类,通过该类生成动态代理对象

(3)JDK 动态代理步骤:
① 新建业务层接口

public interface ProductsService {
	public void insert();
}

② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class ProductsServiceImp implements ProductsService{
	@Override
	public void insert(){
		System.out.println("正在执行新增操作");
	}
}
	```

③ 新建调用处理器封装代理类中的方法体
```java
public class TransactionHander implements InvocationHandler{
	private Object target;
	public TransactionHander(Object target) {
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;
		try {
			System.out.println("获取连接打开事务");
			result = method.invoke(target, args);
			System.out.println("提交事务");
		}catch (Exception e) {
			System.out.println("事务回滚");
		}
		return result;
	}
}
  • TransactionHander 是一个实现了 InvocationHandler 接口的调用处理器类,成员变量 object 表示的是需要被代理的目标对象,核心方法 invoke 中的 method 参数表示的是目标对象执行的目标方法,args 数组表示方法执行时所需要的参数。通过 method 可以执行目标对象中的方法,在执行该方法前后都可以执行一些其他的代码来对功能进行增强。
  • 当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

④ 使用Proxy类创建代理类对象

public class Test {
	public static void main(String[] args) {
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		//创建目标对象
		ProductsService productsService = new ProductsServiceImp();
		//创建调用处理器对象
		TransactionHander handler = new TransactionHander(productsService);
		//创建代理对象
		ProductsService serviceProxy = (ProductsService) Proxy.newProxyInstance(productsService.getClass().getClassLoader(), productsService.getClass().getInterfaces(),handler);
		serviceProxy.insert();
	}
}

Proxy.newProxyInstance() 方法用于创建代理对象,其中第一个参数表示动态代理对象的class文件生成完毕之后交给哪一个类加载器来加载。通常使用目标对象类对应的加载器。第二个参数表示的是在生成class文件时这个代理类需要实现的接口,由于最后生成的代理对象必须是可以强转为目标对象的,所以代理对象实现的接口必须和目标对象保持一致,第三个参数是调用处理器对象,调用处理器对象封装了代理对象执行的方法体。

⑤ 创建代理对象工厂类(可选)
JDK动态代理已经足够优秀,可以使代理方式更加灵活更易于程序的拓展,但是美中不足的是,JDK动态代理只能代理实现了接口的类,如果要创建没有实现接口的类,需要使用第三方提供的CGLIB代理。

3、代理模式之 CGLIB 动态代理

(1)CGLIB 动态代理概述
CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,通俗说CGLIB可以在运行时动态生成字节码。

(2)重要步骤
① 新建业务层接口

public interface CGLIBService {
	public void insert();
}

② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class CGLIBServiceImp implements CGLIBService{
	public void insert() {
		System.out.println("执行业务层新增操作");
	}
}

③ 新建方法拦截器 CGLIBInterceptor 类,通过该类的interceptor方法对目标方法进行增强处理

public class CGLIBInterceptor implements MethodInterceptor{
	private Object target;
	public Object getTarget() {
		return target;
	}
	public void setTarget(Object object) {
		this.target = object;
	}
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		Object result = null;;
		try {
			System.out.println("获取连接,开启事务");
			result = method.invoke(target, args);
			System.out.println("提交事务");
		} catch (Exception e) {
			System.out.println("事务回滚");
		}
		return result;
	}
}

④ 在方法拦截器 CGLIBInterceptor 类中自定义 getProxy() 方法用于生成代理对象

public Object getProxy(Object object) {
	//将目标对象保存到成员变量中
	this.target = object;
	//增强器,动态代码生成器,创建代理对象
	Enhancer enhancer = new Enhancer();
	//设置回调对象
	enhancer.setCallback(this);
	//设置类的父类类型,其实就是目标对象的类型,生成的代理对象汇集成目标对象
	//设置目标对象的类信息,代理类汇集成目标类
	enhancer.setSuperclass(object.getClass());
	//动态生成字节码,并返回代理对象
	return enhancer.create();
}

⑤ 新建测试类,调用方法拦截器类的 getProxy 方法生成代理对象

public class CGLIBTest {
	public static void main(String[] args) {
		CGLIBService service = new CGLIBServiceImp();
		CGLIBInterceptor interceptor = new CGLIBInterceptor();
		CGLIBService proxy = (CGLIBService) interceptor.getProxy(service);
		proxy.insert();
	}
}

二、Spring 代理模式原理区别

java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。而 cglib 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TwoYellowGuns

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值