首先明白代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
借用网上别人画的代理模式UML类图:

举个例子,我们生活中经常到火车站去买车票,但是人一多的话,就会非常拥挤,于是就有了代售点,我们能从代售点买车票了。这其中就是代理模式的体现,代售点代理了火车站对象,提供购买车票的方法。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
代理之静态代理:
实现逻辑:这种代理方式需要代理对象和目标对象实现一样的接口。
其优点:在于可以不修改目标对象的前提下扩展目标对象的功能。
缺点:
1.代码产生多余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
2.维护成本高。一旦接口增加方法,目标对象与代理对象都要进行修改。
下面举个保存人类信息功能的静态代理实现,步骤如下:
1.定义一个人类接口(IPersonDao):
package com.test.cgb;
/**
*
* @Description: 接口类
* @author: caigb
* @createTime: 2019年10月24日
* @version:
*/
public interface IPersonDao {
// 定义一个保存的方法
public void savePerson();
}
2.编写目标对象实现接口:
package com.test.cgb;
/**
*
* @Description: 目标对象
* @author: caigb
* @createTime: 2019年10月24日
* @version:
*/
public class PersonDao implements IPersonDao{
/**
* 实现接口的方法
*/
@Override
public void savePerson() {
System.out.println("保存人类信息"); // 这是目标对象的方法
}
}
3.接着编写静态代理对象:PersonDaoProxy,该类需要实现IPersonDao接口如:
package com.test.cgb;
/**
*
* @Description: 静态代理对象,实现IUserDao接口
* @author: caigb28355
* @createTime: 2019年10月24日
* @version:
*/
public class PersonDaoProxy implements IPersonDao{
// 定义一个变量,类型是接口
private IPersonDao target;
// 在初始化时调用带参的构造函数,将引用赋值给target,也就是让代理对象拿到目标对象句柄,就可以操作目标对象了
public PersonDaoProxy(IPersonDao itarget) {
this.target = itarget;
}
@Override
public void savePerson() {
System.out.println("代理开始");
target.savePerson(); // 此处的target的值为com.test.cgb.PersonDao@7ce6a65d,也就是相当这个代理对象操作的是目标对象的方法
System.out.println("代理结束");
}
}
4.编写测试类,进行调用测试:
package com.test.cgb;
import org.junit.Test;
public class StaticPersonProxy {
@Test
public void testStaticProxy(){
// 向上转型,初始化目标对象
IPersonDao target = new PersonDao();
// 调用代理类的有参构造函数,将目标对象引用赋值给代理对象,从而代理对象获得目标对象的句柄,这样代理对象可以操作目标对象
PersonDaoProxy proxy = new PersonDaoProxy(target);
proxy.savePerson(); // 调用代理类的savePerson方法
}
}
解析:在测试类使用了PersonDaoProxy proxy = new PersonDaoProxy(target)之后,PersonDaoProxy中的target变量的值为
如图:

------------------------------------------------------------------------------------------------------------------------------------
代理之动态代理:
动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。
静态代理与动态代理的区别主要在:
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
- 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
特点:动态代理对象不需要实现接口,但是要求目标对象必须实现接口,否则不能使用动态代理。
代码实现例子如下:
1.定义一个用户接口(IUserDao):
package com.test.cgb;
public interface IUserDao {
public void save();
}
2.创建目标对象:UserDao
package com.test.cgb;
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("保存数据");
}
}
3.创建动态代理对象:ProxyFactory
package com.test.cgb;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;// 维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return null;
}
});
}
}
4.编写测试类:TestProxy
package com.test.cgb;
import org.junit.Test;
public class TestProxy {
@Test
public void testDynamicProxy (){
IUserDao target = new UserDao();
// 输出目标对象信息如完整的包路径加类名com.test.cgb.UserDao
System.out.println(target.getClass());
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass()); //输出代理对象信息
proxy.save(); //执行代理方法
}
}
执行测试方法时候,打印出:
class com.test.cgb.UserDao
class com.sun.proxy.$Proxy4
开启事务
保存数据
提交事务
扩展:
JDK中生成代理对象主要涉及的类有
- java.lang.reflect Proxy,主要方法为
static Object newProxyInstance(ClassLoader loader, //指定当前目标对象使用类加载器
Class<?>[] interfaces, //目标对象实现的接口的类型
InvocationHandler h //事件处理器
)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
Object invoke(Object proxy, Method method, Object[] args)
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
proxy: 指代我们所代理的那个真实对象 method: 指代的是我们所要调用真实对象的某个方法的Method对象 args: 指代的是调用真实对象某个方法时接受的参数
浙公网安备 33010602011771号