文章由B站动力节点相关课程视频整理而成。
代理模式
为其它对象提供一个代理以控制对这个对象的访问。在某些情况下,一个对象不适合或不能直接访问另一个对象,而代理可以在客户类与目标对象之间起到中介的作用。
具有这种访问关系呈现出来的模式称之为代理模式。
B是代理类,能够访问到C,也能增加新的业务功能,增强对C的访问。
代理的作用:
- 功能增强:在原有功能的基础上增加了额外的功能。
- 控制访问:代理类不让客户能直接访问目标对象。
代理的实现:
- 静态代理(如下图所示):
代理类是手工创建的一个java类,所要代理的目标是确定的。代理类包含了目标对象,在对目标对象的方法进行调用时可以进行功能增强。
优点:实现简单,容易理解
缺点:当项目中的目标类与代理类数量增多时,修改接口中的方法会使工作量增大。
代理类B、C和目标类D、E均实现了接口F中抽象方法出售U盘的功能。但厂家D、E不向个人客户A直接出售,只向代理商家B、C。某一天,F新增出售移动硬盘的功能,类B、C、D、E均要修改。
- 动态代理
当在接口中修改方法时,不用修改代理类
动态代理
在程序执行过程中,JDK利用反射机制创建对象能力创建代理对象,不用创建代理类文件。代理目标不是确定的,而是可活动的,变化的。
动态代理的实现:
- JDK动态代理:使用java反射包中的类和接口实现动态代理的功能。
反射包:java.lang.reflect
类:InvocationHandler(接口)、Method、Proxy - cgLib代理:第三方的开源项目,高效的code生成类库,可以在运行期间扩展java类,实现java接口,被广泛的AOP框架使用
使用JDL动态代理,要求目标类与代理类实现相同的接口。若目标类无接口,则不能使用该方法实现。
此时可以考虑使用cgLib。cgLib代理的生成原理使生成目标类的子类,子类对象即代理对象。因此目标类不能是final的。
JDK动态代理的实现
package com.yang.service;
//目标接口,定义目标类实现的功能
//JDK动态代理要求代理目标必须实现接口
public interface ISomeService {
void doService ();
}
package com.yang.service;
public class SomeService implements ISomeService {
@Override
public void doService() {
System.out.println("SomeServie类的doService方法执行," +
"此方法满足了一定的业务需要。");
}
}
package com.yang.proxyHandler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//创建InvocationHandler的实现类,在invoke方法中实现代理功能
public class myInvocationHandler implements InvocationHandler {
//动态代理的目标对象
private Object target = null;
//动态代理的目标对象通过构造方法传入,可以根据需要传入不同的对象,
//而不是唯一确定的,这就是“动态”的含义
public myInvocationHandler(Object target) {
this.target = target;
}
/*java.lang.reflect.InvocationHandler中的方法
此方法实现了对原有代理对象的业务方法的调用,
即SomeServie类的doService方法的调用
并在此基础上可以选择进行一定的功能增强
此方法的参数均为JDK管理,不需要人工传入
Object proxy:目标类的代理对象
Method method:目标类的业务方法对象
Object[] args:目标类的业务方法所需要的参数
(例如由于本例doService没有参数,args为空)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.调用目标方法
method.invoke(target,args);
//2.实现功能增强
System.out.println("myInvocationHandler类的invoke方法执行," +
"在原有业务方法结束后进行功能增强");
//如果代理的目标对象有返回值,那就要在“1.调用目标方法”中接收返回值
//在有必要的情况下进行强值类型转换,并在“2.实现功能增强”中修改
//最后在此处返回。
//本例中目标方法(doservice)无返回值
return null;
}
}
package com.yang;
import com.yang.proxyHandler.myInvocationHandler;
import com.yang.service.ISomeService;
import com.yang.service.SomeService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* Hello world!
* */
public class App
{
public static void main( String[] args )
{
//1.创建目标对象
ISomeService target = new SomeService();
//2.创建InvocationHandler对象,
//构造器参数决定了代理目标,传谁代理谁
InvocationHandler handler = new myInvocationHandler(target);
//3.获取代理对象,并将返回值转为接口类型
//强转成功的原因在于目标对象target实现了ISomeService接口
ISomeService proxy = (ISomeService) Proxy.newProxyInstance(
//第一个参数是目标类的类加载器,可看作固定写法
target.getClass().getClassLoader(),
//第二个参数是目标类的接口对象,可看作固定写法
//正是因为这个参数决定了JDK动态代理必须实现接口
target.getClass().getInterfaces(),
//第三个参数是包含目标类的InvocationHandler对象
handler);
//4.实现代理的业务功能
proxy.doService();
}
}
动态代理的好处
在实际开发中,将日志、事务等与也业务方法无关、但很多业务方法都要执行的代码放在InvocationHandler类invoke方法的功能增强中,这样一来:
- 解耦合,使业务代码与非业务代码分离,业务类专注于业务的实现。
- 减少代码复用。