代理模式
使用框架时有句话叫无代理无框架,无反射无框架,今天就来分析一下代理模式。 使用代理模式,是为了在不修改目标对象的基础上,增强主业务逻辑,客户端想要访问的是目标对象,但是客户类可以真正访问的对象是代理对象,代理类与目标类要实现同一接口。
注意一下几点:
- 代理类与目标类要实现统一接口,即业务接口。
- 客户类对目标类的调用均是通过代理类完成的。
- 代理类的执行既执行了对目标类的增强业务逻辑,又调用了目标类的主业务逻辑。
静态代理
静态代理指, 代理类在程序运行前就已经定义好,其与目标类的关系在程序运行前就已经确立。 类比企业与企业的法律顾问之间的代理关系,这种代理关系不是在“官司”发生后才建立的,而是之前就确立号的一种关系。
静态代理实现转账:
- 定义业务接口
public interface ISomeService { String doFirst(); }
- 定义目标类
// 目标类,代理类要增强的类 public class ISomeServiceImp implements ISomeService{ @Override public String doFirst() { return "hello"; } }
- 定义代理类
// 静态代理类 public class SomeServiceProxy implements ISomeService { // 声明业务接口对象 private ISomeService target; public SomeServiceProxy() { } // 业务接口对象作为构造器参数,用于接收目标对象 public SomeServiceProxy(ISomeService target) { super(); this.target = target; } // 增强目标方法 @Override public String doFirst() { return target.doFirst().toUpperCase(); } }
- 定义测试类
public class MyTest { public static void main(String[] args) { // 创建目标对象 ISomeService target = new ISomeServiceImp(); // 创建代理对象,并用目标对象初始化 ISomeService service = new SomeServiceProxy(target); String result = service.doFirst(); System.out.println(result); } }
测试结果为静态代理类将doFirst()方法返回结果增强为大写。
注意,静态代理模式与装饰者模式较为相似,但两者有区别,可以参考装饰者设计模式
动态代理
动态代理指,程序在整个运行过程中根本不存在目标类的代理类,目标对象是由代理工具(如代理工厂类)在程序运行时由JVM根据反射等机制动态生成的。代理对象与目标对象的关系在策划稿内需运行时才确立。 类比于当事人于聘请的律师之年的关系,实在官司发生之后才确立的。
Proxy
使用JDK的java.lang.reflect.Proxy类进行动态代理,使用newProxyInstance()方法生成动态代理对象。
代码实现:
// 业务接口
public interface ISomeService {
String doSome();
}
// 目标类
public class ISomeServiceImpl implements ISomeService{
@Override
public String doSome() {
return "abc";
}
}
// 动态代理测试类
public class MyTest {
public static void main(String[] args) {
// 定义目标对象
ISomeService target = new ISomeServiceImpl();
// 创建动态代理对象
ISomeService service = (ISomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 目标类实现的所有接口
new InvocationHandler() { // 匿名内部类,用于加强主页午逻辑
/**
* proxy 代表生成的代理对象
* method 代表目标方法 doSome()
* args 代表目标方法参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
if (result != null) {
// 调用目标方法
result = ((String) method.invoke(target, args)).toUpperCase();
}
return result;
}
});
// 测试代理对象方法
System.out.println("动态代理对象方法: " + service.doSome());
}
}
测试结果输出为:ABC
测试成功
CGLIB动态代理
使用Proxy实现动态代理时,要求目标类于代理类实现相同的接口,但是当目标类不存在接口时,无法用该方法实现。 对于无接口的类,通过CGLIB(Code Generation Library, Spring 用他来实现AOP的编程, hibernate用它实现持久对象的字节码的动态生成.)实现,原理是生成目标类的子类,其子类是增强过的,子类对象就是代理对象,所以使用CGLIB要求目标类能够被继承,即不能是final的类。
// 目标类 注意,这次没有接口
public class SomeService {
public String doSome() {
return "abc";
}
}
// 定义CglibFactory用于生成增强子类
public class CglibFactory implements MethodInterceptor{
private SomeService target;
public CglibFactory() {
super();
}
public CglibFactory(SomeService target) {
super();
this.target = target;
}
public SomeService myCglibCreator() {
Enhancer enhancer = new Enhancer();
// 指定父类,即目标类
enhancer.setSuperclass(SomeService.class);
// 设置回调接口对象
enhancer.setCallback(this);
// create()方法用于创建Cgliv动态代理对象
return (SomeService) enhancer.create();
}
// 回调接口的对象, 代理对象执行目标方法时会触发
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result = method.invoke(target, args);
if(result != null) {
result = ((String) result).toUpperCase();
}
return result;
}
}
// 定义测试类
public class MyTest {
public static void main(String[] args) {
SomeService target = new SomeService();
SomeService service = new CglibFactory(target).myCglibCreator();
System.out.println("增强之后: " + service.doSome());
}
}
测试输出ABC,测试成功
在测试时一直报错Exception in thread "main" java.lang.NoClassDefFoundError:org/objectweb/asm/Type
,原因java字节码操作和分析的第三方类库都引用了asm.jar文件,在官网下载asm.jar添加到项目路径就可解决
// TODO
在CglibFactory代码中还涉及到方法回调设计模式,这个以后补充。