Java设计模式篇代理模式

java常见知识 专栏收录该内容
69 篇文章 2 订阅

什么是代理

代理对象一般用来对既有对象增加或者修改一些功能使用,是源对象的副本。一般拥有源对象的所有方法,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
    点赞
  • 2
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

1) 优秀的程序应该是这样的:阅读时,感觉很优雅;新增功能时,感觉很轻松;运行时,感觉很快速,这就需要设计模式支撑。<br /> <br /> 2) 设计模式包含了大量的编程思想,讲授和真正掌握并不容易,网上的设计模式课程不少,大多讲解的比较晦涩,没有真实的应用场景和框架源码支撑,学习后,只知其形,不知其神。就会造成这样结果: 知道各种设计模式,但是不知道怎么使用到真实项目。本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,比如 单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等<br /> <br /> 3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧<br /> <br /> 课程内容和目标<br /> <br /> 本课程是使用Java来讲解设计模式,考虑到设计模式比较抽象,授课采用 图解+框架源码分析的方式<br /> <br /> 1) 内容包括: 设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种实现)、抽象工厂模式、原型模式、建造者模式、工厂模式。结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)<br /> <br /> 2) 学习目标:通过学习,学员能掌握主流设计模式,规范编程风格,提高优化程序结构和效率的能力。<br /> <div> <br /> </div>
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值