Andy Back-end Dev Engineer

设计模式-代理模式


代理模式

使用框架时有句话叫无代理无框架,无反射无框架,今天就来分析一下代理模式。 使用代理模式,是为了在不修改目标对象的基础上,增强主业务逻辑,客户端想要访问的是目标对象,但是客户类可以真正访问的对象是代理对象,代理类与目标类要实现同一接口。

注意一下几点:

  1. 代理类与目标类要实现统一接口,即业务接口。
  2. 客户类对目标类的调用均是通过代理类完成的。
  3. 代理类的执行既执行了对目标类的增强业务逻辑,又调用了目标类的主业务逻辑

静态代理

静态代理指, 代理类在程序运行前就已经定义好,其与目标类的关系在程序运行前就已经确立。 类比企业与企业的法律顾问之间的代理关系,这种代理关系不是在“官司”发生后才建立的,而是之前就确立号的一种关系。

静态代理实现转账:

  1. 定义业务接口
    public interface ISomeService {
     String doFirst();
    }
    
  2. 定义目标类
    // 目标类,代理类要增强的类
    public class ISomeServiceImp implements ISomeService{
    
     @Override
     public String doFirst() {
         return "hello";
     }
    }
    
  3. 定义代理类
    // 静态代理类
    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();
     }
    }
    
  4. 定义测试类
    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代码中还涉及到方法回调设计模式,这个以后补充。

Comments

Content