Java中代理和裝飾者模式的區別
阿新 • • 發佈:2019-12-04
裝飾模式:以對客戶端透明的方式擴充套件物件的功能,是繼承關係的一個替代方案;
代理模式:給一個物件提供一個代理物件,並有代理物件來控制對原有物件的引用;
裝飾模式為所裝飾的物件增強功能;代理模式對代理的物件施加控制,並不提供物件本身的增強功能
簡而言之,裝飾者是指的是自身,對功能的增強,而另一種是呼叫介面,實現對代理物件的控制
在Spring AOP中,主要使用了兩種代理方式:jdkProxy、cjlibProxy
cjlibProxy:
package com.cjlib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author weining * @date 2019/10/31 8:45 */ public class CglibProxyExample implements MethodInterceptor { public Object getProxy(Class cls) { //CGLIB enhancer 增強類物件 Enhancer enhancer = new Enhancer(); //設定增強型別 enhancer.setSuperclass(cls); //定義邏輯物件,要求實現當前物件實現MethodInterceptor方法 enhancer.setCallback(this); //返回代理物件 return enhancer.create(); } /** * 代理邏輯方法 * * @param o 代理物件 * @param method 方法 * @param objects 方法引數 * @param methodProxy 方法代理 * @return 代理邏輯返回 * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("呼叫真是物件之前"); Object invokeSuper = methodProxy.invokeSuper(o, objects); System.out.println("呼叫真是物件之後"); return invokeSuper; } }
測試方法:
package com.cjlib; import com.jdk.HelloWorld; import com.jdk.impl.HelloWorldImpl; /** * @author weining * @date 2019/10/31 9:03 */ public class testCGLIBProxy { public static void main(String[] args) { CglibProxyExample cglibProxyExample = new CglibProxyExample(); HelloWorld helloWorld = (HelloWorldImpl) cglibProxyExample.getProxy(HelloWorldImpl.class); helloWorld.sayHello(); } }
jdkProxy:
package com.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author weining * @date 2019/10/31 8:29 * 在jdk動態代理時 必須要實現InvocationHandler介面 * 自動生成介面中的invoke方法 */ public class JdkProxyExample implements InvocationHandler { private Object target = null; public Object bind(Object target){ this.target = target; /* 三個屬性分別是:類載入器,把生成的動態代理物件掛在哪個介面之上,定義實現方法邏輯的代理類 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } /** * invoke可以實現代理物件 * @param proxy bind 返回的物件 * @param method 當前排程的方法 * @param args 排程方法的引數 * @return 代理的結果返回 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("進入代理邏輯方法"); System.out.println("在呼叫真實物件的服務"); Object obj=method.invoke(target,args); System.out.println("在呼叫真實物件之後的服務"); return obj; } }
這時,建立一個HelloWorld介面:
package com.jdk; /** * @author weining * @date 2019/10/31 8:27 */ public interface HelloWorld { public void sayHello(); }
建立介面的實現類:
package com.jdk.impl; import com.jdk.HelloWorld; /** * @author weining * @date 2019/10/31 8:27 */ public class HelloWorldImpl implements HelloWorld { public void sayHello() { System.out.println("Hello,World!"); } }
最後呼叫測試方法:
package com.jdk; import com.jdk.impl.HelloWorldImpl; /** * @author weining * @date 2019/10/31 8:40 */ public class test { public static void main(String[] args) { JdkProxyExample jdkProxyExample = new JdkProxyExample(); HelloWorld proxy = (HelloWorld) jdkProxyExample.bind(new HelloWorldImpl()); proxy.sayHello(); } }
兩者的區別
1)JDK動態代理只能對實現了介面的類生成代理,而不能針對類。
2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,
並覆蓋其中方法實現增強,但是因為採用的是繼承,所以該類或方法最好不要宣告成final,
對於final類或方法,是無法繼承的。
裝飾者模式:
//裝飾器模式 public class Decorator implements Component{ private Component component; public Decorator(Component component){ this.component = component } public void operation(){ …. component.operation(); …. } } //裝飾器的客戶 public class Client{ public static void main(String[] args){ //客戶指定了裝飾者需要裝飾的是哪一個類 Component component = new Decorator(new ConcreteComponent()); … } }
&n