目录
3.2.3,创建动态代理类并实现InvocationHandler接口
一,介绍
代理模式(Proxy Pattern)的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。这种类型的设计模式属于结构型模式。
二,模式结构
一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理
对象实现同一个接口,先访问代理类再访问真正要访问的对象。
代理模式分为:
- 静态代理、
- 动态代理。
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
应用实例:1,黎治跃是个包工头,家里几栋房子卖不出去想租给别人,茫茫人海中不容易找到租他房子的人,因此最好的方式就是找个房产中介去帮他兜销。这样中介就成为一个代理对象了,当别人找中介租房子时,中介就能帮李帅把房子给租出去了,而不需要别人千里迢迢都打电话找黎治跃了;2,spring中的AOP实现原理。
三,实现
3.1,静态代理模式
大概逻辑图如图所示:
最通俗易懂的就是实现租房子的效果
3.1.1,抽取业务接口
package designModel.proxy.static_proxy;
/**
* @author young
* @date 2022/9/16 17:04
* @description: 公共业务业务抽取出来
*/
public interface Service {
/**
* 租房子
*/
void rendHouse();
}
3.1.2,房东想要把房子租出去
package designModel.proxy.static_proxy;
/**
* @author young
* @date 2022/9/16 17:08
* @description: 房东要租房
*/
public class Host implements Service{
@Override
public void rendHouse() {
System.out.println("出租房东的房子");
}
}
3.1.3,房东找一个中介帮他把房子租出去
package designModel.proxy.static_proxy;
/**
* @author young
* @date 2022/9/16 17:07
* @description: 中介机构代理房东租房
*/
public class Agency implements Service{
private Host host;
public void setHost(Host host) {
this.host = host;
}
@Override
public void rendHouse() {
other1(); //加入其他相关工作
host.rendHouse(); //干房东安排的事
}
//中介自己的事
public void other1(){
System.out.println("帮业主做宣传");
}
public String method(String msg){
return msg;
}
}
3.1.4,张三找上中介,在中介那块拿到了房东要租的房子
package designModel.proxy.static_proxy;
/**
* @author young
* @date 2022/9/16 16:58
* @description: 静态代理测试(张三)
*/
public class StaticProxyTest {
public static void main(String[] args) {
Agency agency = new Agency();
agency.setHost(new Host());
//帮房东买房子,做宣传
agency.rendHouse();
//自己摸鱼
System.out.println(agency.method("我自己摸鱼"));
}
}
这样可以看到,张三不仅获取了房东租房的信息,还能在中介那了解中介在干的事(帮房东做宣传,上班摸鱼啊啥的),这样就扩展了被代理类(房东)的业务范围,实现了在不影响被代理类的情况下扩展业务水平的需求。
但是这种传统的代理模式也有很明显的弊端:一个代理类只为一个功能接口服务,也就是说,随着业务接口越来越多,代码量也会翻倍!
因此为了解决静态代理的不足,最好的做法是为所有功能一致的业务接口提供有统一的代理处理操作,而这就可以通过动态代理机制来实现。
3.2,动态代理
实现动态代理需要解决两个问题:
- 代理类要接收所有实现业务子类对象
- 动态获取接口信息
在进行动态代理设计的时候对于动态对象的创建是由JVM底层完成的,此时主要依靠的是java.lang.reflect.Proxy程序类,而这个程序类之中提供一个核心方法:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
通过JDK1.8开发帮助文档,我们也能很好了解这个类的信息
大概的逻辑图如图所示:
实现过程如下:
3.2.1,提取业务接口
package designModel.proxy.dynamic_proxy;
/**
* @author young
* @date 2022/9/16 19:24
* @description: 业务接口
*/
public interface Service {
int add(Object o);
int delete(Integer id);
boolean update(Integer id);
int query();
}
3.2.2,业务类实现
package designModel.proxy.dynamic_proxy;
/**
* @author young
* @date 2022/9/16 19:25
* @description: 业务实现类
*/
public class ServiceImpl implements Service{
@Override
public int add(Object o) {
System.out.println("增加一个对象:"+o);
return 1;
}
@Override
public int delete(Integer id) {
System.out.println("删除一个值:"+id);
return 1;
}
@Override
public boolean update(Integer id) {
System.out.println("更新一个id内容:"+id);
return true;
}
@Override
public int query() {
System.out.println("查询数据:");
return 1;
}
}
3.2.3,创建动态代理类并实现InvocationHandler接口
package designModel.proxy.dynamic_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author young
* @date 2022/9/16 19:30
* @description: 利用Proxy类自动生成代理类
*/
/**
* 实现InvocationHandler接口,这是个共用类。可以实现复用!
*/
public class ProxyInvocationHandle implements InvocationHandler {
//private Service service;
private Object target;
// public void setService(Service service) {this.service = service;}
/**
* 提取成公共动态实现
* @param target 需要被代理的接口
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* 生成代理类
* @return 返回service的代理类
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
/**
* @param proxy the proxy instance that the method was invoked on
*要代理的对象
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*执行的接口方法名称
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*传递的参数
* @return 通过反射机制实现,返回处理代理实例结果
* @throws Throwable 方法调用出现错误就抛出异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("返回处理后的代理对象,他又添加了一个方法,方法名通过反射获取后传给addMethod");
addMethods(method.getName());
return method.invoke(target, args);
}
/**
* 模仿添加一个新的方法
* @param newMethod
*/
public void addMethods(String newMethod){
System.out.println("添加一个新增业务的方法"+newMethod+"---》这是代理类里的");
}
}
3.2.4,测试动态代理效果
package designModel.proxy.dynamic_proxy;
/**
* @author young
* @date 2022/9/16 17:04
* @description: 动态代理测试
*/
public class DynamicProxyTest {
public static void main(String[] args) {
//真实业务
ServiceImpl service = new ServiceImpl();
//创建代理角色
ProxyInvocationHandle role = new ProxyInvocationHandle();
//设置代理的业务对象
role.setService(service);
//动态生成代理类
Service proxy = (Service) role.getProxy();
//每次执行业务类的方法后都会加上新添的业务功能,达到切面编程的效果
proxy.add(new String("我是个对象"));
proxy.delete(1);
proxy.update(2);
proxy.query();
}
}
经过运行后可得到如下结果:
可以看到,业务再无侵入的情况下实现了扩展。并且,只要有一个公共的代理类存在去设置代理对象的接口就行了 ,避免的静态代理模式的缺陷。
四,总结
静态代理模式:
- 优点:职责清晰,有一定扩展性
- 缺点:每个服务依赖于代理类,并且代码量会翻倍,更改也比较麻烦
动态代理模式:
- 优点:减少接口依赖,提高开发效率,降低耦合度
- 缺点:JDK原生动态代理只能基于接口代理
至于另外的一种CGLIB动态代理,后续慢慢补充……