目录
2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:
3.3、InvocationHandler的实现类,Cache调用处理器
4、增加InvocationHandler的一个实现类SyCache
一、前言
设计模式这个东西,作为程序员还是需要掌握一些的。小到软件中级设计师考试、面试、读框架源码,大到日常开发、系统架构等都会碰到。而笔者也是早在上大学的那段时间,已经对设计模式有了一知半解的认识。
JDK动态代理,在源码的世界里扮演的角色格外重要,如MyBatis的日志模块、binding模块等,Spring的AOP等。在日常开发中,你也可以捕捉到它的身影。使用代理的一个重要目标是控制对被代理类的访问,可以在代理类中做一些前置/后置处理,如:缓存、日志、事务、权限等等。使用JDK动态代理的前提是:被代理类必须是一个接口或者其实现类。如果被代理类不是接口或者接口实现类,而你又想使用动态代理,可考虑使用CGLIB、Javassist技术。这是一项硬技能,好的Java工程师、架构师都必须具备的知识。
装饰模式,可以动态的为对象添加功能,这是一个经典的概论。日常开发中可能不会经常碰到,但是在源码里面你还是可以捕捉到它的身影。
JDK动态代理模式与装饰模式其实历来都有很多争议,许许多多的学者都认为它们没有什么多大区别,甚至直呼它们根本就是同一回事。今天就从实验角度探讨一下它们的区别,正如邓小平爷爷所说:“实践是检验真理的唯一标准”。
二、JDK动态代理模式
为什么代理模式有这么多种,而这里只是提到其中一种。因为在日常开发中,常见的就是使用JDK动态代理。感兴趣的读者可以自行去研究研究代理模式的其它种类。
1、开发中使用的组件及其方法
开发中用到的JDK动态代理组件为InvocationHandler接口以及Proxy类,它们的职责如下表:
类 | 类的职责 |
Proxy类 | 负责生成代理类并关联调用处理器(InvocationHandler接口的实现类) |
InvocationHandler接口 | 负责处理代理的逻辑,如:缓存、日志、事务、权限等等 |
1.1、描述
Proxy生成的代理类是不处理逻辑的,抽象又很真实你连代理类体都没看到如何写逻辑,但是它会委派给调用处理器的invoke()方法处理逻辑,这也就是为什么多了个InvocationHandler接口及其实现类的原因。我们都知道,要调用一个类的非静态方法,一般都得经过它的实例去调用方法。好办,Proxy生成的代理类不处理逻辑,而委托给调用处理器的invoke()方法处理,那么要调用调用处理器的方法,你至少要拿到它的实例。故Proxy的newProxyInstance()方法(这个方法是开发中经常用到的)中就得把调用处理器的实例传入进来了。我们来看看源码中这个方法有哪些参数:
源码:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
方法职责是创建代理类实例,并且代理类关联接管处理逻辑任务的调用处理器。
loader:类加载器,负责加载代理类。
interfaces:数组类型,表示提供了一组接口对象数组,那么代理类就实现了这些接口的所有方法并可调用。
h:InvocationHandler接口类型,代理类会通过InvocationHandler实现类的实例去调用invoke()方法处理逻辑。
2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
参数描述:
proxy:被代理的类。
method:调用时被代理的类的方法的Method对象。
args:调用时被代理的类的方法的参数。
3、实验数据
3.1、Subject接口
public interface Subject {
String doSomething();
}
3.2、Subject接口的实现类RealSubject
public class RealSubject implements Subject {
@Override
public String doSomething() {
System.out.println("我是真实的Object");
return "我是RealSubject";
}
}
3.3、InvocationHandler的实现类,Cache调用处理器
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Cache implements InvocationHandler {
/**
* 这里封装真实类一般可以是:Object类型、接口类型、Class类型
*/
// private Object object;
//
// Cache(Object object) {
// this.object = object;
// }
private Subject object;
/**
* 初始化字段,method.invoke(object, args)中会用到该字段
*
* @param object 真实对象
*/
Cache(Subject object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----Before handler----");
// 调用真实对象的方法,返回值result
Object result = method.invoke(object, args);
System.out.println((String)result);
System.out.println("----After handler----");
// 返回返回值
return result;
}
}
3.4、测试类
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
public static void main(String[] args) {
// 声明父类创建子类
Subject subject = new RealSubject();
// 封装真实的类
InvocationHandler cache = new Cache(subject);
// 生成代理实例,关联调用处理器
/**
* 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
*/
Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, cache);
String result = proxy.doSomething();
System.out.println("返回结果:" + result);
System.out.println("***********************************");
Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
subject.getClass().getInterfaces(), cache);
proxy2.doSomething();
}
}
3.5、控制台打印结果:
4、增加InvocationHandler的一个实现类SyCache
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class SyCache implements InvocationHandler {
/**
* 这里封装真实类一般可以是:Object类型、接口类型、Class类型
*/
// private Object object;
//
// Cache(Object object) {
// this.object = object;
// }
private Subject object;
/**
* 初始化字段,method.invoke(object, args)中会用到该字段
*
* @param object 真实对象
*/
SyCache(Subject object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----SyCache Before handler----");
// 调用真实对象的方法,返回值result
Object result = method.invoke(object, args);
System.out.println((String)result);
System.out.println("----SyCache After handler----");
// 返回返回值
return result;
}
}
5、修改补充JDKProxyTest
package com.ceam.designer.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class JDKProxyTest {
public static void main(String[] args) {
// 声明父类创建子类
Subject subject = new RealSubject();
// 封装真实的类
InvocationHandler cache = new Cache(subject);
// 生成代理实例,关联调用处理器
/**
* 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
*/
Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, cache);
String result = proxy.doSomething();
System.out.println("返回结果:" + result);
System.out.println("***********************************");
Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
subject.getClass().getInterfaces(), cache);
// 封装真实的类
InvocationHandler syCache = new SyCache(subject);
proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
subject.getClass().getInterfaces(), syCache);
proxy2.doSomething();
}
}
打印结果:
6、总结
无论从idea中的代码,还是打印的结果都可以看出:使用JDK动态代理,你无法让几个InvocationHandler接口的实现类自由组合,只能单独的使用其中一个为代理类提供服务。
三、装饰模式
1、装饰模式可以解决哪些问题
在实践生产中,新需求总是不断出现的。当有新的需求时,就需要为某些组件添加新的功能来满足这些需求。我们可以直接的修改已有的组件代码并添加新的功能,这很明显会破坏已有组件的稳定性,修改完毕后,整个组件还需要重新进行测试,才能上线使用。这种做法明显违反了“开放-封闭”原则。
装饰模式可以帮助我们解决上述问题,装饰模式可以动态地为对象添加功能,它是基于组合方式实现功能的。在实际开发中,业界都是推荐使用组合的方式扩展系统的功能,而少用继承的方式。请读者们记住一句话:组合优于继承。
2、从类图走近装饰模式
图2-1
Component(组件):组件接口定义了全部组件实现类所有装饰器实现的行为。
CorrectComponent(具体组件实现类):具体组件实现类实现了Component(组件)接口。
Decorator(装饰器):所有装饰器的父类,它是一个实现了Component接口的抽象类,并在其中封装了一个Component对象,也就是被装饰的对象。而这个被装饰的对象只要是Component类型即可,这就实现了装饰器的组合和复用。但是它不一定会出现,有的设计也可以把它省略掉,而直接用具体的装饰器就行,我们得会灵活运用。
CorrectDecorator(具体装饰器):具体的装饰器实现类,它会向被装饰者添加某些功能,也就是新添加的需求功能在该实现类进行实现。当然具体装饰器可以有多个,而且它们之间都是相互独立的。
下面的类图是自己一些研究设计类图,主要部分还是上面的类图结构
图2-2
DecoratorWrapper从图2-2就可以看出,它的职责是负责创建装饰器的,具体代码请看下面2.6。
2.1、Component组件接口
package com.ceam.designer.decorator.two;
/**
* Component.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public interface Component {
void doSomething();
}
2.2、CorrectComponent具体组件实现类
package com.ceam.designer.decorator.two;
/**
* CorrectComponent.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class CorrectComponent implements Component {
@Override
public void doSomething() {
System.out.println("我是具体组件");
}
}
2.3、CorrectDecorator具体装饰器类1
package com.ceam.designer.decorator.two;
/**
* CorrectDecorator.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class CorrectDecorator implements Component {
private final Component component;
CorrectDecorator(Component component) {
this.component = component;
}
@Override
public void doSomething() {
component.doSomething();
this.enchange();
}
private void enchange() {
System.out.println("后置增强");
}
}
2.4、CorrectDecorator2具体装饰器类2
package com.ceam.designer.decorator.two;
/**
* CorrectDecorator2.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class CorrectDecorator2 implements Component {
private final Component component;
CorrectDecorator2(Component component) {
this.component = component;
}
@Override
public void doSomething() {
component.doSomething();
this.enchange();
}
private void enchange() {
System.out.println("后置增强2");
}
}
2.5、Condition辅助条件类
package com.ceam.designer.decorator.two;
/**
* Condition.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class Condition {
public static Boolean d1;
public static Boolean d2;
public Condition d1(boolean d1) {
this.d1 = d1;
return this;
}
public Condition d2(boolean d2) {
this.d2 = d2;
return this;
}
}
2.6、DecoratorWrapper装饰器包装类
package com.ceam.designer.decorator.two;
/**
* DecoratorWrapper.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class DecoratorWrapper implements Component{
private static Condition condition;
public static Boolean d1;
public static Boolean d2;
DecoratorWrapper() {
}
DecoratorWrapper(Condition condition) {
this.condition = condition;
}
@Override
public void doSomething() {
this.en();
}
private void en() {
Component component = new CorrectComponent();
if (Condition.d1) {
component = new CorrectDecorator(component);
}
if (Condition.d2) {
component = new CorrectDecorator2(component);
}
component.doSomething();
}
public DecoratorWrapper d1(boolean d1) {
this.d1 = d1;
return this;
}
public DecoratorWrapper d2(boolean d2) {
this.d2 = d2;
return this;
}
}
2.7、 测试类
package com.ceam.designer.decorator.two;
/**
* Test.java
* <p>
* Copyright ©
* All Rights Reserved.
* Project: test-center
* Description:
*
* @author : Administrator
* History:
* 2021-09-04 created by Administrator
*/
public class Test {
public static void main(String[] args) {
// Component component = new CorrectComponent();
// component = new CorrectDecorator(component);
// component = new CorrectDecorator2(component);
// component.doSomething();
Component component1 = new DecoratorWrapper(new Condition()
.d1(false)
.d2(false));
component1.doSomething();
System.out.println("2----------------------------------------");
Component component2 = new DecoratorWrapper(new Condition()
.d1(true)
.d2(false));
component2.doSomething();
System.out.println("3----------------------------------------");
Component component3 = new DecoratorWrapper(new Condition()
.d1(true)
.d2(true));
component3.doSomething();
System.out.println("4----------------------------------------");
Component component4 = new DecoratorWrapper().d1(true).d2(true);
component4.doSomething();
}
}
2.8、测试结果
2.9、总结
从实验结果可以看出:装饰模式可以动态组合,对被装饰对象添加功能。
四、动态代理与装饰模式有什么不同
动态代理与装饰模式有什么不同,笔者就不多说了,一切尽在实验数据当中,请读者仔细去研究研究。毕竟笔者也是下了很多功夫的,从源码知识,再经实验结论,都已经总结给读者了。如果有疑问的可以留言,笔者尽量为你解答。