java代理模式
代理模式: 目标对象不可直接访问,而需要通过代理对象进行访问,在这过程中代理对象可以增强目标对象访问的功能进行增强(可以理解为额外添加一下功能)。
生活中:
找房:
房东 ===> 目标对象;
房屋中介 ===> 代理对象;
你,我 ===> 客户端对象;
买衣服:
服装生产厂===>目标对象;
门店===>代理对象;
你,我===>客户端对象;
开发中:
发送短信功能:
运营商(移动,电信,联通)===>目标对象;
第三方公司===>代理对象;
开发的应用程序需要发送短信的功能===>客户端对象;
代理模式的作用:
- 控制目标对象的访问;
- 增强功能;
代理模式的分类:
- 静态代理;
- 动态代理;
- JDK动态代理;
- CGLib动态代理(子类代理);
1. 静态代理
静态代理是代理模式的一种,它具备以下特点:
- 目标对象和代理对象必须实现同一个业务接口;
- 代理对象在程序运行之前就已经存在;
- 能够灵活的进行目标对象的切换,却无法进行功能的灵活扩展(使用动态代理解决此问题);
- 适合用于目标对象功能固定而目标对象灵活切换的场景;
1.1 静态代理的实现:
a>理论原理
加入有这个业务功能:请明星进行节目表演。
明星刘德华,即目标对象(无法直接访问);
刘德华代理:代理对象(我们可以访问,他也可以直接跟明星进行对接);
我们:客户端对象
b>代码实现
业务接口:
package com.xuan;
/**
* 唱歌的业务类,目标对象与代理对象需要同时实现该接口。
*/
public interface SingService {
void sing();
}
目标对象:
package com.xuan.impl;
import com.xuan.SingService;
/**
* 唱歌业务的目标对象:刘德华,主要是实现具体的业务。
*/
public class SuperStarLiu implements SingService {
@Override
public void sing() {
System.out.println("我是刘德华,我正在唱歌!");
}
}
代理对象:
package com.xuan.impl;
import com.xuan.SingService;
/**
* 唱歌业务的代理对象:刘德华的助理,主要是完成除了主业务之外的工作,如场地预约、时间安排、费用结算等。
*/
public class Agent implements SingService {
@Override
public void sing() {
System.out.println("Agent场地预约");
System.out.println("Agent时间安排");
// 主业务
SuperStarLiu starLiu = new SuperStarLiu();
starLiu.sing();
System.out.println("Agent费用结算");
}
}
客户端对象:
package com.xuan;
import com.xuan.impl.Agent;
import org.junit.Test;
public class MyTest {
@Test
public void testClient(){
/*
要求明星唱歌,这里不能直接与明星进行沟通,必须与他的代理进行协商。
*/
// 使用多态
SingService singService = new Agent();
singService.sing();
}
}
1.2 面向接口编程(重要)
类中的成员变量设计为接口,方法的形参设计为接口,方法的返回值设置为接口,程序在调用时会自动指向接口的实现类。
1.3 使用接口编程实现目标对象的灵活切换
public class Agent implements SingService {
// 目标对象
private SingService target;
public Agent() {
}
// 传入目标对象,方法设计为接口,有利于程序的灵活切换。
public Agent(SingService target) {
this.target = target;
}
@Override
public void sing() {
System.out.println("Agent场地预约");
System.out.println("Agent时间安排");
// 主业务
target.sing(); // 面向接口编程,调用时会自动指向接口具体的实现类。
System.out.println("Agent费用结算");
}
}
@Test
public void testClient02(){
// 使用面向接口编程,这里需要用户提供一个需要唱歌的明星。
SingService singService = new Agent(new SuperStarZhou());
singService.sing();
}
2. JDK动态代理
代理对象在程序运行的过程中动态在内存构建,可以灵活地进行业务功能的扩展。JDK动态代理具有以下特性:
- 目标对象必须实现业务接口;
- JDK代理对象不需要实现业务接口;
- JDK动态代理的对象在程序运行前不存在,在程序运行时动态在内存中构建;
- JDK动态代理可以灵活地进行业务功能能的切换;
- 接口实现类中的方法(非接口中的方法)不能被代理,即适用于接口实现类方法增强的情况;
2.2 JDK动态代理用到的类和接口
它是使用现有的工具类来完成JDK动态实现的;
a>Proxy类
它是java.lang.reflect.Proxy包下的类. 它有一个方法Proxy.newProxyInstance(…)专门用来生成动态代理对象。
public static Object newProxyInstance(
ClassLoader loader, //类加载器
Class<?>[] interfaces,//目标对象实现所实现的接口
InvocationHandler h //它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这
)throws IllegalArgumentException{
...
}
b>Method类
反射用的类,用来对目标对象的方法进行反射调用。
c>InvocationHandler接口
它是实现代理和业务功能的,我们在调用时使用匿名内部实现。
2.3 代码实现
package com.xuan.proxy;
import com.xuan.service.SingService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂。
*/
public class ProxyFactory {
// 类中的成员变量(即目标对象)设计为接口
SingService target;
// 传入目标对象
public ProxyFactory(SingService target){
this.target = target;
}
// 返回动态代理对象
public Object getAgent(){
return Proxy.newProxyInstance(
// 类加载器,完成目标对象的加载
target.getClass().getClassLoader(),
// 目标对象实现的所有接口
target.getClass().getInterfaces(),
// 实现代理功能的接口,这里传入匿名内部实现
new InvocationHandler() {
@Override
public Object invoke(
// proxy就是创建的动态代理对象
Object proxy,
// method就是目标对象所要执行的方法
Method method,
// 目标方法的参数
Object[] args) throws Throwable {
// 代理对象所实现的功能
System.out.println("预定时间");
System.out.println("预定场地");
// 主业务功能实现,注意这里不能写具体的方法而是使用method的invoke()方法,该方法会返回所调用的方法的返回值。
Object obj = method.invoke(target, args);
System.out.println("金费的预算");
// 重点主要,该返回值是调用的方法的返回值
return obj;
}
});
}
}
public class MyTest {
@Test
public void test01(){
ProxyFactory proxy = new ProxyFactory(new SuperStarLiu());
SingService agentLiu = (SingService) proxy.getAgent();
// proxyAgent.sing();
System.out.println(agentLiu.perform("沿河土家族"));
}
}
3. CGLib动态代理
CGLib代理又称为子类。通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强。如果目标对象没有实现接口,则只能通过CGLib子类代理来进行功能增强。子类代理是对象字节码框架ASM来实现的。注意:
- 被代理的类不能为final, 否则报错;
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;
public Object getProxyInstance(){
//1.使用工具类
Enhancer en=new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理)对象
return en.create(); ===>返回的是子类代理对象