设计模式-代理模式

168 阅读3分钟
  • 静态代理
  • 基于JDK的动态代理


静态代理

    角色分析

         抽象角色:一般使用接口或抽象类来解决

         真实角色:被代理的角色

         代理角色:代理真实角色,然后做一些附加操作

         客户:访问代理对象的人


现在有一个类,封装了真实角色行为

public class UserServiceImpl implements UserService {

	@Override
	public void add() {
		System.out.println("代码原有增加逻辑");
	}

	@Override
	public void del() {
		System.out.println("代码原有删除逻辑");
	}

	@Override
	public void update() {
		System.out.println("代码原有更新逻辑");
	}

	@Override
	public void query() {
		System.out.println("代码原有查询逻辑");
	}

}

现在要增加打印日志的功能,如果直接在每个方法中修改,则违反了开闭原则。

需要一个代理类来实现

代理类在构造或者setter方法中接收被代理的对象,实现被代理对象的方法,在此基础上,又添加了自己的功能。

public class Proxy implements UserService {
	private UserServiceImpl userService;

	public Proxy() {
	}

	public Proxy(UserServiceImpl userService) {
		this.userService = userService;
	}

	@Override
	public void add() {
		log("add");
		userService.add();

	}

	@Override
	public void del() {
		log("del");
		userService.del();

	}

	@Override
	public void update() {
		log("update");
		userService.update();

	}

	@Override
	public void query() {
		log("query");
		userService.query();

	}

	private void log(String msg) {
		System.err.println("用户使用了"+msg+"方法");
	}
}


客户调用代理

public class Client {
	public static void main(String[] args) {
		UserServiceImpl arg = new UserServiceImpl();
		Proxy proxy = new Proxy(arg);
		proxy.add();
		proxy.del();
	}

}

静态代理的优点是符合开闭原则,不更改原有代码,弊端是每次增加一个被代理类,必然要多加一个代理类,导致代码量增加,开发效率变低。动态代理解决了这个弊端


基于JDK的动态代理(需要接口)

同样的,有userService 的接口和实现

public interface UserService {
	
	void add();
	void del();
	void update();
	void query();
}

public class UserServiceImpl implements UserService {

	@Override
	public void add() {
		System.out.println("代码原有增加逻辑");
		
	}

	@Override
	public void del() {
		System.out.println("代码原有删除逻辑");
	}

	@Override
	public void update() {
		System.out.println("代码原有更新逻辑");
	}

	@Override
	public void query() {
		System.out.println("代码原有查询逻辑");
	}

}

新建一个代理类

//动态代理类需要实现InvocationHandler接口 ,重写invoke方法
public class JdkProxy<T> implements InvocationHandler {
        //持有一个被代理的对象
	private T target;
        //在构造方法中传入被代理对象
	public JdkProxy(T target) {
		this.target = target;
	}

	@SuppressWarnings("unchecked")
    //不写泛型用object也没有问题
	public T getProxy() {
    //返回一个代理对象,代理target类,参数含义见如下下方代码块
   	return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                                     target.getClass().getInterfaces(),
                                                             this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before(method);
                //target为当前被代理对象,args为对象调用方法的方法参数(反射)
		method.invoke(target, args);
		after(method);
		return null;
	}
	
	public void after(Method method) {
		System.out.println("调用" + method.getName() + "方法后");
	}
	public void before(Method method) {
		System.out.println("调用" + method.getName() + "方法前");
	}
}

    

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,    //当前对象类加载器
                                          Class<?>[] interfaces,   //当前对象接口
                                          InvocationHandler h)    //代理类本身


调用代理类

public class Client {
	
	public static void main(String[] args) {
		JdkProxy<UserService> jdkProxy = new JdkProxy<UserService>(new UserServiceImpl());
		//如果没有泛型制约,此处不会要求强转,需要自己有意识的转成被代理类,才能调用接口方法
		UserService proxy = (UserService) jdkProxy.getProxy();
		proxy.add();
		proxy.query();
	}

}

如果现在我有一个admin类要实现加入日志功能,只需要在构造中传入该类就可以,无需再针对admin类建立新的代理类