代理模式

一、代理模式

1.代理模式定义

  • 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
  • 定义可以比较难以理解,咱们把这个抽象的概念换成咱们现实中的代理:
    火车票代理点
    房屋中介
    小王的好朋友小宋

2.开发术语

  • ① 抽象主题角色:

    • 声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题。
  • ② 代理主题(Proxy)角色:

    • 代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;
    • 代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);
    • 代理角色通常在将客户端调用传递给真实的主题之前或之后(前置增强/通知,后置增强/通知),都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
  • ③ 真实主题角色:

    • 定义了代理角色所代表地真实对象

3.静态代理

  • 保存的对象:
public class Person {
    private String name;
    private IRent zj;
	// getter,setter略
}
  • 抽象主题角色
/**
 * 抽象主题角色
 */
public interface IRent {
    /**
     * 看房子
     */
    void look();
    /**
     * 签合同
     */
    void sign();
    /**
     * 收租金
     */
    void rent();
}
  • 代理主题角色
/**
 * 代理主题角色【中介】
 */
public class Intermediary implements IRent {

    private String name;

    private Fangdong fd;

    @Override
    public void look() {
        fd.look();
    }

    @Override
    public void sign() {
        fd.sign();
    }

    @Override
    public void rent() {
        System.out.println("租金 涨了200");
        fd.rent();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Fangdong getFd() {
        return fd;
    }

    public void setFd(Fangdong fd) {
        this.fd = fd;
    }
}
  • 真实主题角色
/**
 * 真实主题角色
 */
public class Fangdong implements IRent {

    private String name;

    @Override
    public void look() {
        System.out.println("预约 " + name + "看江与城13栋12-8的房子");
    }

    @Override
    public void sign() {
        System.out.println("与 " + name + " 签订租房合同");
    }

    @Override
    public void rent() {
        System.out.println(name + " 收取租金");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 测试:
public class StaticProxyTest {

    @Test
    public void test() throws Exception {
        Fangdong fd = new Fangdong();
        fd.setName("Baby");

        Intermediary zj = new Intermediary();
        zj.setName("张淑珍");
        zj.setFd(fd);

        Person p = new Person(zj);
        p.setName("刘伟");

        p.look();
        p.sign();
        p.rent();

    }

}

图解:
在这里插入图片描述

  • 总结
  • 静态代理咱们已经完成,但是大家仔细想一下,会觉得它的作用其实不大,它就像是一个中介公司,只代理一个房东(类)的一间房子(方法)。如果这个房东(类)有多间房子(方法),我们还需要继续添加代码,而如果要代理多个房东(类)的房子(方法),我们也还需要添加相应的代理类。 试想一下,这样用代理模式的话代码反而增多。 解决方案:使用动态代理

4.动态代理

  • CGLIB动态代理:没有接口的类使用CGLIB动态代理(类有没有接口都可以支持)
  • JDK动态代理:有接口的类使用JDK的动态代理(JDK动态代理不支持没有接口的类)
  • 1.JDK动态代理
  • jdk的动态代理只允许完成有接口的代理
    在这里插入图片描述在这里插入图片描述
    测试:
package cn.itsource._08_DynamicProxy_JDK;

import org.junit.Test;

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

public class DynamicProxyTest {

    /**
     * JDK自带的动态代理技术
     *  Proxy.newProxyInstance
     *  InvocationHandler
     * @throws Exception
     */
    @Test
    public void test() throws Exception {

        //被代理对象
       IRent fd = new Fangdong();
        fd.setName("张三丰");

        //另一个被代理对象
        ITicket ds = new TicketDS();

        /**
         * ClassLoader loader 类加载器
         * Class<?>[] interfaces 被代理对象实现过的所有接口【数组】
         * InvocationHandler h 处理器
         */
        //类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //被代理对象实现过的所有接口
        Class<?>[] interfaces = ds.getClass().getInterfaces();


        //创建代理对象(强转的是接口)
      IRent irent = (IRent)Proxy.newProxyInstance(classLoader, interfaces, new MyInvocationHandler(fd));

        System.out.println(irent.getClass());


        //测试调用方法
//        proxy.look();
//        proxy.sign();
//        proxy.rent();
    irent.sale();

    }

}

自定义的一个处理代理功能类(这个类中我们加了一个方法可以直接创建代理对象)

package cn.itsource._08_DynamicProxy_JDK;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * InvocationHandler:是代理实例的调用处理程序 实现的接口。 
 */
public class MyInvocationHandler implements InvocationHandler {
	//定义真实主题角色:目标对象
    //持有一个被代理对象
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 代理对象执行指定方法的代码【反射执行方法】
     * @param proxy     经过jdk的代理对象【一般不用】
     * @param method    方法【调用的被代理的方法】
     * @param args      arguments 参数列表数组
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object obj = null;// //返回的结果
        try {
            System.out.println("执行目标方法之前添加的业务逻辑。。。。开启事务");
            obj = method.invoke(target, args);// //执行直接对象的方法
            System.out.println("执行目标方法之后添加的业务逻辑。。。。提交事务");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            System.out.println("发生异常了,事务回顾");
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            System.out.println("发生异常了,事务回顾");
        }
        return obj;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public String toString() {
        return "MyInvocationHandler{" +
                "target=" + target +
                '}';
    }
}

抽象主题角色

package cn.itsource._08_DynamicProxy_JDK;

/**
 * 抽象主题角色
 */
public interface IRent {

    /**
     * 看房子
     */
    void look();

    /**
     * 签合同
     */
    void sign();

    /**
     * 收租金
     */
    void rent();




}

真实主题角色

package cn.itsource._08_DynamicProxy_JDK;


/**
 * 真实主题角色
 */
public class Fangdong implements IRent {

    private String name;

    @Override
    public void look() {
        System.out.println("预约 " + name + "看江与城13栋12-8的房子");
    }

    @Override
    public void sign() {
        System.out.println("与 " + name + " 签订租房合同");
    }

    @Override
    public void rent() {
        System.out.println(name + " 收取租金");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

  • 2.CGLIB动态代理
  • cglib类似于javassist-3.18.1-GA.jar功能字节码增强,
    原来Hibernate3.2之前就是使用cglib来进行字节码增强
  • ①.下面是完成CGLIB的类:
    • org.springframework.cglib.proxy.Enhancer; 增强器
    • org.springframework.cglib.proxy.MethodInterceptor; 方法切面(代理实例处理方法功能的接口)
    • org.springframework.cglib.proxy.MethodProxy;
  • ②.CGLIB的实现代码
  • CGLIBProxy类代码:
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor{

	//定义参数,接收真实的目标对象
	private Object targetObject;
	//事务对象
	private TxManager txManager;
	
	public CglibProxy(Object targetObject,TxManager txManager) {
		this.targetObject = targetObject;
		this.txManager = txManager;
	}
	
	/**
	 * proxyObject:CGLIB代理后的对象,一般不用
	 * method:真实对象的方法
	 * args:方法的参数
	 * methodProxy:CGLIB代理后的方法,一般不用
	 */
	@Override
	public Object intercept(Object proxyObject, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {
		Object result = null; //返回的结果
		try {
			txManager.begin();
			result = method.invoke(targetObject, args); //执行直接对象的方法
			txManager.commit();
		} catch (Exception e) {
			txManager.rollback();
			e.printStackTrace();
		}finally{
			txManager.close();
		}
		return result;
	}
	
	/**
	 * 创建一个代理对象
	 * @return
	 */
	public Object createProxy(){
		//创建增强器
		Enhancer enhancer = new Enhancer();
		//创建的代理就是咱们真实目标对象的子类
		enhancer.setSuperclass(targetObject.getClass());
		//MethodInterceptor就是一个Callback回调
		enhancer.setCallback(this);
		//创建一个代理对象并返回
		return enhancer.create();
	} 
}

测试代码

@Test
	public void testProxy() throws Exception {
		User user = new User();
		//真实主题角色对象
		EmployeeServiceImpl employeeService = new EmployeeServiceImpl();
		//事务管理器
		TxManager txManager = new TxManager();
		//创建Cglib代理对象
		CglibProxy cglibProxy = new CglibProxy(employeeService, txManager);
		//拿到代理对象
		EmployeeServiceImpl proxy = (EmployeeServiceImpl)cglibProxy.createProxy();
		proxy.save(user);
	}
  • 总结
  • Spring使用JDK与CGLIB两种动态代理
  • JDK动态代理:
    • 有接口的类使用JDK的动态代理(JDK动态代理不支持没有接口的类)
    • 如果有n个接口,必然有n个实现,只用写1个代理类Proxy就可以对所有有接口进行处理
    • 如果有代理主题角色存在,必须修改调用方才能实现代理
  • CGLIB动态代理:
    • 没有接口的类使用CGLIB动态代理(类有没有接口都可以支持)
    • 只用写1个代理类CglibProxy就可以对所有没有接口的不能是final类都进行处理
    • 如果有代理主题角色存在,必须修改调用方才能实现代理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值