1. 反射的应用:动态代理
代理设计模式的原理:
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时(
区别于编译期间
)根据需要动态创建目标类的代理对象。
动态代理使用场合:调试;远程方法调用。
动态代理相比于静态代理的优点:
抽象角色中(
接口
)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
简
言
之
:
一
个
代
理
类
扛
下
所
有
被
代
理
类
想
要
实
现
的
功
能
!
简言之:一个代理类扛下所有被代理类想要实现的功能!
简
言
之
:
一
个
代
理
类
扛
下
所
有
被
代
理
类
想
要
实
现
的
功
能
!
利
用
反
射
机
制
实
现
更
为
灵
活
的
设
计
模
式
!
利用反射机制实现更为灵活的设计模式!
利
用
反
射
机
制
实
现
更
为
灵
活
的
设
计
模
式
!
简言之,代理是一种常用的
设计模式
,其目的就是为真实对象提供一个代理对象以控制对真实对象的访问。代理类负责为
委托类(被代理类、真实类)
预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续操作。其大致关系如图所示:
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
1.1 静态代理的例子
为更好的理解代理这种设计模式,我们先来看一个静态代理的例子:
package atguigu;
/**
* 静态代理举例
*/
//模拟一个【生产衣服】相关的工厂接口
interface ClothFactory{
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理类对象进行实例化
public ProxyClothFactory() {
}
public ProxyClothFactory(ClothFactory factory) {
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作...");
factory.produceCloth();
System.out.println("代理工厂做一些后续的收尾工作...");
}
}
//被代理类
class LiNingClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("LiNing服装厂生产了一批衣服...");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类对象
LiNingClothFactory liNing = new LiNingClothFactory();
//创建代理类对象
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(liNing);
proxyClothFactory.produceCloth();
}
}
代理类和被代理类在编译期间就已经确定下来了
。执行结果如图所示:
1.2 Java动态代理类
为实现动态代理,我们离不开
Proxy
这个类的使用,我们先来学习一下Proxy类的相关语法内容。
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
(1)
Interface InvocationHandler
:该接口中定义了一个方法👇
public Object invoke(Object obj,Method method,Object[] args)
obj:一般指代理类;
method:被代理的方法;
args:method方法中的参数组成的数组。
注意:该接口中的invoke()方法会在代理类中动态实现。
(2)Proxy:该类即动态代理类,其中主要包含以下内容:
protected Proxy(InvocationHandler h)
//构造函数,用于给内部的h赋值。
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
//获得一个代理类,其中loader是类加载器,interfaces是真实类所拥有的全部接口的数组。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
所谓的动态代理:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。
在使用动态代理类时,我们必须实现InvocationHandler接口。
动态代理步骤:
为了增加阅读体验感,步骤里面添加了自定义的变量名。
1.创建被代理的类(委托类、真实类)(RealSubject)及其接口(RealSubjectInterface)
2.创建一个实现了接口InvocationHandler的类(InvocationHandlerImpl),它必须实现接口的invoke方法
3.通过Proxy的静态方法newProxyInstance(),创建一个代理对象(realSubjectProxy)
4.通过代理对象(realSubjectProxy)调用委托类对象(realSubject)的方法
1.3
JDK动态代理实现步骤
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
//1.创建被代理的类的接口 --> RealSubjectInterface
interface RealSubjectInterface{
void sayHello(String name);
void sayGoodBye();
}
//2.创建被代理的类(委托类、真实类)--> RealSubject
static class RealSubject implements RealSubjectInterface{
@Override
public void sayHello(String name) {
System.out.println(name+":sayHello!");
}
@Override
public void sayGoodBye() {
System.out.println("good bye!");
}
}
//3.创建一个实现接口InvocationHandler的类InvocationHandlerImpl,即调用处理器实现类
static class InvocationHandlerImpl implements InvocationHandler{
//我们要代理的真实对象
private Object realObject;
//构造器,为真实对象赋初值
public InvocationHandlerImpl(Object realObject) {
this.realObject = realObject;
}
/**
* invoke方法负责处理动态代理类中所有方法调用
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在代理真实对象前可以添加额外操作
System.out.println("在用代理对象调用真实对象的方法之前做一些准备工作...");
System.out.println("**************用来模拟实际场景下的业务需求**************");
System.out.println("代理对象调用的方法"+method.getName());
//当代理对象调用真实对象的方法时,其会自动跳转到代理对象关联的Handler对象的invoke()方法来调用
Object returnValue = method.invoke(realObject, args);
System.out.println("********************************************************");
//在代理真实对象前可以添加额外操作
System.out.println("在用代理对象调用真实对象的方法之后调用处理器做一些后续工作...");
return returnValue;
}
}
/**
* 动态代理测试
* @param args
*/
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandlerImpl handler = new InvocationHandlerImpl(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Class<?>[] interfaces = realSubject.getClass().getInterfaces();
RealSubjectInterface realSubjectProxy = (RealSubjectInterface)Proxy.newProxyInstance(classLoader, interfaces, handler);
realSubjectProxy.sayHello("Tom");
//realSubjectProxy.sayGoodBye();
}
}
运行结果如图所示:
1.4 修改1.2节静态代理的例子
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 将1.2节静态代理改为动态代理场景
*/
//被代理类需要实现的接口
interface ClothInterface{
void produceCloth();
}
//被代理类1去实现ClothInterface接口
class LiNingClothFactory_1 implements ClothInterface{
@Override
public void produceCloth() {
System.out.println("LiNing_1服装厂生产了一批衣服...");
}
}
//被代理类2去实现ClothInterface接口
class LiNingClothFactory_2 implements ClothInterface{
@Override
public void produceCloth() {
System.out.println("LiNing_2服装厂生产了一批衣服...");
}
}
/**
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
* 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
*/
class DynamicProxyFactory{
//通过调用此方法,返回一个代理类的对象,解决问题一
//obj:被代理类的对象
public static Object getProxyInstance(Object obj){
ClassLoader classLoader = obj.getClass().getClassLoader();
Class<?>[] interfaces = obj.getClass().getInterfaces();
MyInvocationHandler handler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(classLoader, interfaces, handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;
public MyInvocationHandler() {
}
//使用构造器赋值
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
//当我们通过代理类的对象调用某个方法fun()时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法fun()的功能声明在invoke()中。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = method.invoke(obj, args);
return returnValue;
}
}
public class DynamicProxyTest {
//测试
public static void main(String[] args) {
//1.创建被代理类对象
LiNingClothFactory_1 liNing_1 = new LiNingClothFactory_1();
///2.使用动态代理类获取被代理类的对象,进而动态创建一个代理类对象
ClothInterface proxyInstance_1 = (ClothInterface)DynamicProxyFactory.getProxyInstance(liNing_1);
proxyInstance_1.produceCloth();
ClothInterface proxyInstance_2 = (ClothInterface)DynamicProxyFactory.getProxyInstance(new LiNingClothFactory_2());
proxyInstance_2.produceCloth();
}
}
运行结果如图所示:
1.5 AOP与动态代理
前面介绍的Proxy和InvocationHandler,我们举例的场景比较小,没有很好的体现出来动态代理的优势,下面介绍一种更实用的动态代理机制。
我们先来看一张图:
我们很常用的一种方式就是可以将不同代码段的相同代码部分抽出来作为一个公共的方法(函数)来调用,这样代码段1、2、3就和深色代码段分离开了,但是代码段1、2、3又和一个特定的方法A耦合了!完犊子了,这可咋整。。。相当于我们虽然抽离了一个方法出来,但是这个方法被我们“写死了”。
最理想的方式就是:
代码段1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代段的方法
。这就不得不介绍
AOP
思想了。见图:
我们在1.3节的运行结果图里面其实已经展示了AOP思想的影子了,你发现了吗?
有关AOP相关内容可以参考个人博客[Spring之AOP篇]