Java设计模式篇代理模式

什么是代理

代理对象一般用来对既有对象增加或者修改一些功能使用,是源对象的副本。一般拥有源对象的所有方法,java中一般都是继承原来的对象。可以不修改原来对象前提下,实现我们期待的行为。

在实际应用中,根据单一职责原则,代理对象一般只是负责代理,不负责其它处理逻辑,处理逻辑一般有handler完成

如何创建代理

  • 静态代理:利用组合关系实现静态代理,组合关系注入源对象,然后可以在外层对象进行其它逻辑的注入。静态代理的问题是不同的逻辑注入需要不同的代理对象,一直增加代理对象显然不实际。
  • 动态代理:在java中,可以直接利用java.lang.refect.Proxy类直接创建代理类或者代理对象。

静态代理

静态代理主要把握两点:

  • 代理类和源对象必需实现同一个接口
  • 通过组合,调用源对象方法,并进行自定义逻辑的注入

举例如下:

源对象:

public class UserManagerImpl implements UserManager {
 
	@Override
	public void addUser(String userId, String userName) {
		System.out.println("UserManagerImpl.addUser");
	}
 
	@Override
	public void delUser(String userId) {
		System.out.println("UserManagerImpl.delUser");
	}
 
	@Override
	public String findUser(String userId) {
		System.out.println("UserManagerImpl.findUser");
		return "张三";
	}
 
	@Override
	public void modifyUser(String userId, String userName) {
		System.out.println("UserManagerImpl.modifyUser");
 
	}
}

代理类(实现同一个接口,组合关系引用)

public class UserManagerImplProxy implements UserManager {
 
	// 目标对象
	private UserManager userManager;
	// 通过构造方法传入目标对象
	public UserManagerImplProxy(UserManager userManager){
		this.userManager=userManager;
	}
	@Override
	public void addUser(String userId, String userName) {
		try{
				//添加打印日志的功能
				//开始添加用户
				System.out.println("start-->addUser()");
				userManager.addUser(userId, userName);
				//添加用户成功
				System.out.println("success-->addUser()");
			}catch(Exception e){
				//添加用户失败
				System.out.println("error-->addUser()");
			}
	}
 
	@Override
	public void delUser(String userId) {
		userManager.delUser(userId);
	}
 
	@Override
	public String findUser(String userId) {
		userManager.findUser(userId);
		return "张三";
	}
 
	@Override
	public void modifyUser(String userId, String userName) {
		userManager.modifyUser(userId,userName);
	}
 
}

客户端调用:

public class Client {
 
	public static void main(String[] args){
		//UserManager userManager=new UserManagerImpl();
		UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());
		userManager.addUser("1111", "张三");
	}
}

静态代理模式简单易用,但是有个问题,代理类必需实现源对象的接口,如果要代理多个接口,需要很多的代理对象,类的数量增加太多,有没有什么优化的方式呢?请看动态代理

动态代理

JDK动态代理

Jdk动态代理我们先利用jdk自带的Proxy类创建一个代理对象,代码如下:

package com.puhui.goosecard.bank.utils;

import com.puhui.goosecard.bank.service.AccountService;
import com.puhui.goosecard.bank.service.impl.AccountServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 2 * @Author: kerry
 * 3 * @Date: 2018/8/24 19:48
 * 4
 */
public class Test {


    private static AccountService accountService = new AccountServiceImpl();

    public static void main(String[] args) {

        AccountService accountServiceProxy =
                (AccountService) Proxy.newProxyInstance(AccountService.class.getClassLoader(), new Class[]{AccountService.class}, new AccountHandler());

        accountServiceProxy.openAccount(null);

    }

    static class AccountHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            method.invoke(accountService, args);
            System.out.println("after");
            return null;
        }
    }
}

上述代码注意以下几点:

  • 利用Proxy类的newProxyInstance方法创建代理对象,第一个参数是classloader,第二个是接口类的数组(jdk代理支持多个接口的关键所在),第三个是invocationhandler
  • InvocationHander实现类中,invoke方法的入参,第一个是生成的代理对象,第二个是方法对象,第三个是参数,所以hadler中需要注入源对象,handler是没有源对象的引用的。
  • jdk动态代理通过反射调用被代理对象的方法,cglib是通过fastclass的索引方式

如果接口类数组传入多个接口呢?这个毕竟是jdk动态代理要解决的问题,我们看代码:

package com.puhui.goosecard.bank.utils;

import com.puhui.goosecard.bank.service.AccountService;
import com.puhui.goosecard.bank.service.TradeService;
import com.puhui.goosecard.bank.service.impl.AccountServiceImpl;
import com.puhui.goosecard.bank.service.impl.TradeServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 2 * @Author: kerry
 * 3 * @Date: 2018/8/24 19:48
 * 4
 */
public class Test {


    private static AccountService accountService = new AccountServiceImpl();


    private static TradeService tradeService = new TradeServiceImpl();

    public static void main(String[] args) {

        Object proxyInstance =
                Proxy.newProxyInstance(AccountService.class.getClassLoader(), new Class[]{AccountService.class, TradeService.class}, new AccountHandler());
//        if (proxyInstance instanceof AccountService) {
//            AccountService accountService = (AccountService) proxyInstance;
//            accountService.openAccount(null);
//        }
        if (proxyInstance instanceof TradeService) {
            TradeService tradeService = (TradeService) proxyInstance;
            tradeService.processRefund(null, null);
        }


    }

    static class AccountHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (proxy instanceof AccountService) {
                System.out.println("before");
                method.invoke(accountService, args);
                System.out.println("after");
            }
            if (proxy instanceof TradeService) {
                System.out.println("before");
                method.invoke(tradeService, args);
                System.out.println("after");
            }
            return null;
        }
    }
}

上面的代码注意以下几点:

  • 接口数组传入了accouont service和trade service两个接口,返回的代理对象只能是object。处理时做instanceOf判断
  • handler中第一个参数proxy在一个接口是并没有使用,但是在多个接口类时,proxy可以判断当前是哪个接口
  • 第一个和第二条的if 都可以进入,也即是生成的代理对象实现了接口数组中的所有接口。

总结:

我们发现jdk动态代理解决了静态代理每个接口一个代理类的问题,但是关键点在于jdk代理是面向接口的,创建代理对象传入的是接口数组,如果没有接口怎么创建代理类,继续往下看

 

CGLib动态代理

在没有接口的情况下,如何生成代理对象,这就是CGlib解决的问题。CGLib对ASM字节码操作框架进行了封装,在运行时生成代理对象。很多开源框架都利用cglib生成代理对象,比如spring AOP,hibernate等。

先看一个例子:

定义一个service,注意没有接口,这也是利用cglib的原因

package com.puhui.goosecard.web.cglib;

public class UserServiceImpl {


    public void add() {
        System.out.println("This is add service");
    }

    public void delete(int id) {
        System.out.println("This is delete service:delete " + id);
    }
}

定义一个回调方法,也即是methodInterceptor,注入自定义的逻辑,也即是为什么用代理的原因。比如权限校验等。

package com.puhui.goosecard.web.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
        System.out.println("Before:" + method);
        Object object = proxy.invokeSuper(obj, arg);
        System.out.println("After:" + method);
        return object;
    }
}

生成代理对象,调用进行测试

package com.puhui.goosecard.web.cglib;

import org.springframework.cglib.proxy.Enhancer;

/**
 * 2 * @Author: kerry
 * 3 * @Date: 2018/8/29 19:41
 * 4
 */
public class Test {


    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(new MyMethodInterceptor());
        UserServiceImpl userService = (UserServiceImpl) enhancer.create();
        userService.add();

    }
}

通过以上代码,可以发现:

  • 通过Enhancer类生成代理对象,其中包括设置代理类(setSuperClass),设置回调方法(setCallBack)
  • 通过setSuperClass,可以发现代理类继承了被代理类,也就是UserServiceImpl,且不能处理被final关键字修饰的方法
  • jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值