Java中静态代理和动态代理
一、静态代理
1、静态代理引入
代理模式在我们生活中很常见,比如我们购物,可以从生产工厂直接进行购物,但是在生活中往往不是这样,一般都是厂家委托给超市进行销售,而我们不直接跟厂家进行关联,这其中就引用了静态代理的思想,厂家相当于真实角色,超市相当于代理角色,我们则是目标角色。代理角色的作用其实就是,帮助真实角色完成一些事情,在真实角色业务的前提下,还可以增加其他的业务。AOP切面编程就是运用到了这一思想。
2、静态代理案例
写一个小小的案例,通过婚庆公司,来实现静态代理
/**
* @ClassName StaticProxy
* @Description TODO 静态代理(模拟婚庆公司实现)
* @Author ZhangHao
* @Date 2022/12/11 11:38
* @Version: 1.0
*/
public class StaticProxy {
public static void main(String[] args) {
Marry marry = new WeddingCompany(new You());
marry.happyMarry();
//注意:真实对象和代理对象要实现同一个接口
}
}
//结婚
interface Marry{
//定义一个结婚的接口
void happyMarry();
}
//你(真实角色)
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("张三结婚了!");
}
}
//婚庆公司(代理角色)
class WeddingCompany implements Marry{
//引入真实角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void happyMarry() {
//在结婚前后增加业务
before();
target.happyMarry();
after();
}
private void before(){
System.out.println("结婚之前:布置婚礼现场");
}
private void after(){
System.out.println("结婚之后:收尾工作");
}
}
对于Java中的Thread底层就使用的静态代理模式,源码分析
//Thread类实现了Runnable接口
public class Thread implements Runnable{
//引入了真实对象
private Runnable target;
//代理对象中的构造器
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
}
当我们开启一个线程,其实就是定义了一个真实角色实现了Runnable接口,重写了run方法。
public void TestRunnable{
public static void main(String[] args){
MyThread myThread = new MyThread();
new Thread(myThread,"张三").start();
//Thread就是代理角色,myThread就是真实角色,start()就是实现方法
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("我是子线程,同时是真实角色");
}
}
二、动态代理
1、动态代理的引入
上面使用到了静态代理,代理类是自己手工实现的,自己创建了java类表示代理类,同时要代理的目标类也是确定的,如果当目标类增多时,代理类也需要成倍的增加,代理类的数量过多,当接口中的方法改变或者修改时,会影响实现类,厂家类,代理都需要修改,于是乎就有了jdk动态代理。
2、动态代理的好处
- 代理类数量减少
- 修改接口中的方法不影响代理类
- 实现解耦合,让业务功能和日志、事务和非事务功能分离
3、动态代理的实现步骤
- 创建接口,定义目标类要完成功能。
- 创建目标类实现接口。
- 创建InvocationHandler接口实现类,在invoke()方法中完成代理类的功能。
- 使用Proxy类的静态方法,创建代理对象,并且将返回值转换为接口类型。
以下是代码案例:
/**
* @ClassName DynamicProxy
* @Description TODO 动态代理
* @Author ZhangHao
* @Date 2022/12/11 15:11
* @Version: 1.0
*/
public class DynamicProxy {
public static void main(String[] args) {
//创建目标对象
Marry target = new You();
//创建InvocationHandler对象
MyInvocationHandler handler = new MyInvocationHandler(target);
//创建代理对象
Marry proxy = (Marry)handler.getProxy();
//通过代理执行方法,会调用handle中的invoke()方法
proxy.happyMarry();
}
}
//创建结婚接口
interface Marry{
void happyMarry();
}
//目标类实现结婚接口
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("张三结婚了!");
}
}
//创建工具类,即方法增强的功能
class ServiceTools{
public static void before(){
System.out.println("结婚之前:布置婚礼现场");
}
public static void after(){
System.out.println("结婚之后:清理结婚现场");
}
}
//创建InvocationHandler的实现类
class MyInvocationHandler implements InvocationHandler{
//目标对象
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
//通过代理对象执行方法时,会调用invoke()方法
/**
* @Param [proxy:jdk创建的代理类的实例]
* @Param [method:目标类中被代理方法]
* @Param [args:目标类中方法的参数]
* @return java.lang.Object
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强功能
ServiceTools.before();
//执行目标类中的方法
Object obj = null;
obj = method.invoke(target,args);
ServiceTools.after();
return obj;
}
//通过Proxy类创建代理对象(自己手写的嗷)
/**
* @Param [ClassLoader loader:类加载器,负责向内存中加载对象的,使用反射获取对象的ClassLoader]
* @Param [Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。]
* @Param [InvocationHandler h: 我们自己写的,代理类要完成的功能。]
* @return java.lang.Object
**/
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
}
三、总结
- 代理分为静态代理和动态代理
- 静态代理需要手动书写代理类,动态代理通过Proxy.newInstance()方法生成
- 不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,本质面向接口编程
- 代理模式本质上的目的是为了在不改变原有代码的基础上增强现有代码的功能
转载自:https://juejin.cn/post/7270906612339130429