设计模式之代理模式(二)
- 代理模式总结
- JDK动态代理
- CGLIB代理
代理模式总结:
- 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
- 优点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
- 使用场景: 按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
区别: 有代理类,就是静态代理。而动态代理是由工具生成的(JDK或者CGLIB等),在Java运行时根据反射机制动态生成的。
JDK动态代理
用上面的例子来说,律师是在官司发生之后,才由当事人聘请的,所以代理关系是在”官司”发生之后确立的。
相关注意事项:
- 使用JDK的proxy代理,要求目标类必须实现接口 因为底层的实现和静态代理的相同;
- 获得动态代理对象 在运行时 在内存中动态的为Target创建一个虚拟的代理对象
package com.test.proxyDynamicJDK.service;
/**
* 业务方法接口
*/
public interface ISomeService {
void doSome(String str);
String doOther(String str);
}
package com.test.proxyDynamicJDK.service;
public class SomeServiceImpl implements ISomeService{
@Override
public void doSome(String str) {
System.out.println(str);
}
@Override
public String doOther(String str) {
return str;
}
}
测试JDK动态代理:
package com.test.proxyDynamicJDK.test;
import com.test.proxyDynamicJDK.service.ISomeService;
import com.test.proxyDynamicJDK.service.SomeServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Application类加载器 – CLASSPATH环境变量, 由-classpath或-cp选项定义,或者是JAR中的Manifest的classpath属性定义.
这个是加载我们自己定义的类 加载之后在 classpath中找 如果没有找到就会报出异常
动态代理处理中文乱码问题
*/
public class TestDynamic {
public static void main(String[] args) {
final ISomeService target = new SomeServiceImpl();
//使用JDK的proxy代理,要求目标类必须实现接口 因为底层的实现和静态代理的相同
//获得动态代理对象 在运行时 在内存中动态的为Target创建一个虚拟的代理对象
ISomeService objProxy = (ISomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),// 与目标对象相同的类加载器
target.getClass().getInterfaces(), // 目标类所实现的所有接口或者写成new Class[]{ISomeService.class}
new InvocationHandler(){ // 这里可以创建这个类,但是也可以使用匿名内部类 接口是 不能new的,,但是可以创建一个匿名内部类对象
//invoke 代表的是执行代理对象的方法
//代表目标对象的方法 的 字节码对象
//代表的是目标对象的相应的方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理之前....");
//执行的是目标对象的方法
Object str = method.invoke(target, args);
if (str != null) { //第一个方法没有返回值要判断一下
str = ((String)str).toUpperCase();//增强
}
return str;
}
}
);
objProxy.doSome("doSome....");
System.out.println(objProxy.doOther("do Other.."));
}
}
运行结果:
代理之前....
doSome....
代理之前....
DO OTHER..
可以看到do Other全部转化成了大写。
CGLIB动态代理
原理: 代理对象是目标对象的子类;
此时SomeServiceImpl没有接口,我们写成SomeService:
package com.test.DynamicCglib.service;
/**
* 这个是 使用cglib中的代理 模式 理解本质 : 是使用的继承的原理 所以不用实现接口
*/
public class SomeService {
public void doSome(String str) {
System.out.println(str);
}
public String doOther(String str) {
return str;
}
}
package com.test.DynamicCglib.factory;
import com.test.DynamicCglib.service.SomeService;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 使用Cglib 进行动态的代理
* 使用的是继承的机制 不要实现接口
* 代理对象是目标对象的子类
*/
//当前这个类的对象就是一个回调对象
//注意MethodInterceptor 接口 继承了CallBack接口 所以
//回调设计模式
public class MyCglibFactory implements MethodInterceptor {//相当于实现了Callback
private SomeService target; //要加强的目标对象
public MyCglibFactory() {
target = new SomeService();
}
//这里要创建一个
public SomeService myCglibCreator(){
Enhancer enhancer = new Enhancer(); //创建增强器对象
enhancer.setSuperclass(SomeService.class);//指定一个目标类 也就是父类
//设置回调接口对象
enhancer.setCallback(this); //当前类实现了Callback //this 调用的是当前类的对象 当前创建的类的对象
return (SomeService) enhancer.create(); //这个返回的就是我们指定的代理对象 (就是一个子类的模式 )
}
//这个就是回调方法 MethodInterceptor接口 继承自Callback接口
//这里就是写具体的实现了
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object res = method.invoke(target, objects);
if(null != res){
res = ((String)res).toUpperCase();
}
return res;
}
}
测试类:
package com.test.DynamicCglib.test;
import com.test.DynamicCglib.factory.MyCglibFactory;
import com.test.DynamicCglib.service.SomeService;
/**
* 当然CGlib 也可以代理接口的
*/
public class MyTest {
public static void main(String[] args) {
//创建的就是代理的
SomeService service = new MyCglibFactory().myCglibCreator(); //生成代理对象
service.doSome("doSome...."); //执行这个的时候 执行了 MyCglibFactory(A类)的intercept方法_
System.out.println(service.doOther("do Other...."));
}
}
效果:
doSome....
DO OTHER....
do Other也加强了。
注意这里使用到了回调设计模式 :
其中我们的MyCglibFactory就是A类,而Enhancer类是B类,A类中调用了Enhancer类中的setCallback(this)方法,并将回调对象this作为实参传递给了Enhancer类,Enhancer类在后续的执行过程中,会调用A类中的intercept()方法,这个intercept()方法就是回调方法。
注意,并不是有接口就不能使用CGlib ,也是可以的。