Java中代理和装饰者模式的区别
装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;
代理模式:给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用;
装饰模式为所装饰的对象增强功能;代理模式对代理的对象施加控制,并不提供对象本身的增强功能
简而言之,装饰者是指的是自身,对功能的增强,而另一种是调用接口,实现对代理对象的控制
在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()); … } }