代理模式
为了后面的学习,代理模式的理解很重要,在spring的aop中,就是利用动态代理实现的
静态代理模式
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
一个简单的例子,北漂青年租客,北京土著房东大爷,黑心中介公司
租客想租房子,但在大北京城中找不到房子
房东想租房子,但找不到适合租的人
中介公司,专业的,集结了大量的房源,以及大量的租客需求,中介代理房东租房
北漂青年找到了中介公司,大爷将房子交给了中介公司,北漂青年租到了大爷的房子.
package com.lx.demo01;
//租房
public interface Rent {
public void rent();
}
package com.lx.demo01;
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
package com.lx.demo01;
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房");
}
//看房
public void hetong(){
System.out.println("签租赁合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
package com.lx.demo01;
public class Client {
public static void main(String[] args) {
//房东要租房子
Host host = new Host();
//代理,中介帮房东租房子,但是呢?代理角一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房即可!
proxy.rent();
}
}
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
案例2
对于程序员中,经常性的增删改查 ,模拟一个增删改查的业务
package com.lx.demo02;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
package com.lx.demo02;
//真实对象
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
真实对象实现了增删改查的接口,但是如果想对每个功能添加日志功能,如果直接在原有代码上增加,可能会改错(实际中代码量应该是很大的),就像我们会在一个类中引用另一个类,而不是再创建,而代理模式就可以帮助解决这个问题
package com.lx.demo02;
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.add();
}
public void update() {
log("update");
userService.add();
}
public void query() {
log("query");
userService.add();
}
//日志方法
public void log(String msg){
System.out.println("[Debug] 使用了"+msg+"方法");
}
}
package com.lx.demo02;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.query();
}
}
利用代理模式增加了日志功能,再不改变原有业务代码的情况下实现了日志功能
但是类多了 , 多了代理类 , 工作量变大了 . 开发效率降低,所以需要动态代理模式
动态代理
(需要反射的基础)
动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理 .
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy
【InvocationHandler:调用处理程序】
Object invoke(Object proxy, 方法 method, Object[] args); //参数 //proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。
方法对象的声明类将是该方法声明的接 口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。
原始 类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
【Proxy : 代理】
代码实现
package com.lx.Demo;
//租房
public interface Rent {
public void rent();
}
package com.lx.Demo;
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子!");
}
}
package com.lx.Demo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvokeHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}
// Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
// 为某个接口创建代理Foo :
//
// InvocationHandler handler = new MyInvocationHandler(...);
// Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
// Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
// newInstance(handler);
//
// ## 或更简单地:
// Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class<?>[] { Foo.class },handler);
// 动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。
// 代理接口是由代理类实现的接口。
// 代理实例是代理类的一个实例。
// 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。
// 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例,
// java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。
// 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
//
//生成得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质就是利用反射机制实现!
Object result = method.invoke(rent, args);
Fare();
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void Fare(){
System.out.println("收中介费");
}
}
package com.lx.Demo;
public class Client {
public static void main(String[] args) {
//真实角色
Rent host = new Host();
//代理角色 需要处理程序生成一个代理类 ProxyInvoHandler
ProxyInvokeHandler proxyInvokeHandler = new ProxyInvokeHandler();
//通过调用处理程序处理角色来处理我们要调用的接口对象
proxyInvokeHandler.setRent(host);
//当调用代理类的方法,会通过反射机制调用左边的invoke方法来实现
//因为直接调用相当于写死在代码中了,而这种方式可以从外部传入任意的方法名和参数来执行方法
//这里的proxy就是动态生成的,我们并没有写~
Rent proxy =(Rent) proxyInvokeHandler.getProxy();
proxy.rent();
}
}
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
一个动态代理 , 一般代理某一类业务
一个动态代理可以代理多个类,代理的是接口!