JAVA
1、静态代理
1.1、为什么要代理
当我们需要添加与业务无关的操作时,一般不会改动业务代码,而是使用代理。
也就是寻找第三方来帮助我们实现我们需要实现的功能,同时添加新的操作。
比如当我们找工作时,我们会寻找中介。
我本来只需要找一份工作就好了(原本业务),但是我现在想到要在找工作的时候记录找工作的时间并且(新的日志需求)。
我---->公司
我并不会去要求招聘者记录工作时间(改动实现类),同样我也不想记录(改动控制类)。那么我需要一个人(代理)来帮我在找工作的时候自动记录时间。
我---->中介(代理)---->公司
由中介(代理)来记录时间,我一样只需要完成找工作的任务就行了,公司也只需要正常招人(不需要改动原有代码)。
1.2、静态代理
静态代理很简单,我们只需要创建一个新的类,在它的内部生成一个我们需要的类的实例,由他来代替我们操作就好了
角色分析
- 抽象角色:一般为接口或者抽象类
- 真实角色:一般为被代理的角色
- 代理角色:代理真实角色,代理真实角色后做一些附属操作
- 客户:访问代理对象的人
接下来我们只要一一实现上面的四个类就行了
-
抽象角色
-
我们只需要创建一个方法就好了,我们需要实现的方法是找招聘(公司招聘员工)
-
public interface Recruit { public void hiring(); }
-
-
真实角色
-
这时候我们要去实现上一个方法,实现的人是公司
-
公司---实现-->招聘
-
public class Company implements Recruit { @Override public void hiring() { System.out.println("面试中"); } }
-
-
代理角色
-
代理角色要代理公司实行面试(当然面试还是由公司进行,但是会做一些附加操作)
-
import java.util.Date; public class StaticProxy implements Recruit{ private Company company; //设置company既可以用有参构造也可以用set public void setCompany(Company company) { this.company = company; } public StaticProxy(Company company) { this.company = company; } @Override public void hiring() { Date date = new Date(); System.out.println("记录时间:" + date.toString()); //还是由公司执行面试 //原有业务不变 company.hiring(); } }
-
-
客户
-
此时我们只需要正常调用我们的代理角色来进行操作就可以了
-
(一般而言实际开发中是控制层但是因为要方便测试所以直接写在main函数中)
-
public class User { public static void main(String[] args) { Recruit sp = new StaticProxy(new Company()); sp.hiring(); } } //执行结果 //记录时间:Fri Aug 20 12:21:25 CST 2021 //面试中
-
这样静态代理就完成了,当我们需要添加一些业务无关的操作是,我们可以用静态代理来实现,就不用改动原有代码。
但是有个问题是如果许多业务都需要添加同一个代理,比如都需要记录时间时,就需要写很多个静态代理,一个类中有20个方法就需要将20个方法都添加一个静态代理。
此时,为了减少代码,加入了动态代理。
1.3、动态代理
动态代理有一个标准的类,几乎不需要任何改动,只要在invoke中修改需要执行的操作就可以了
先上代码,再慢慢解释
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyIvocationHandler implements InvocationHandler {
//创建一个对象,此时无所谓我们的对象是什么,只要是一个类就可以了
private Object target;
//设置我们的类
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在方法运行之前
System.out.println("before running");
//运行方法时
Object result = method.invoke(target,args);
//在方法运行在之后
System.out.println("after running");
if(result!=null)
System.out.println("result:"+result.toString());
return null;
}
}
当创建类时继承InvocationHandler,会要求实现invoke方法,也就是当程序中的方法执行时自动唤醒的方法
将上面的几个方法拆开来看
//创建需要实现的对象,比如我们要在公司招聘的时候添加日志此时对象就是公司
//通过set方法传入一个公司就行了
private Object target;
public void setTarget(Object target);
//获取代理对象,当通过getProxy获取了代理对象之后就可以使用代理来实现原有功能
public Object getProxy();
//invoke方法,在程序方法被唤醒时调用的函数
public Object invoke(Object proxy, Method method, Object[] args);
getProxy()方法
查看方法所需要的参数,一个loader,一个方法类,一个InvocationHandler
loader和InvocationHandler直接用自身的方法就可以了,具体代码里面有
而中间的方法类就是我们之前传入的类target,上方setTarget();
target要传实现类,不然没有具体的实现类仅仅代理方法类也没有办法实现业务。
而此处需要的是一个方法类那么通过target.getClass().getInterfaces()
来得到target的方法类。
此方法代码几乎不需要变动
invoke()方法
invoke的参数在你重写方法时会自动填充,不需要管,调用方法的也是java的虚拟机,不需要手动调用。
动态代理的重点就在于当你的代理执行方法时,会调用invoke方法,而原来的方法在invoke中被调用
原方法
我--调用方法-->company.method---系统执行--->company.method
动态代理
我--调用方法-->proxy.method
--系统执行-->invoke
invoke其中的操作-->日志 -->company.method
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在方法运行之前
System.out.println("before running");
//运行方法时
//如果将这一行注释掉当你使用代理执行方法时方法也不会被执行
//这一行就是唤醒方法本身
//result就是方法的返回值,如果没有返回值就是null
Object result = method.invoke(target,args);
//在方法运行在之后
System.out.println("after running");
return null;
}
主程序
public class User {
public static void main(String[] args) {
//创建一个动态代理类的对象
ProxyIvocationHandler test = new ProxyIvocationHandler();
//设置target为Company实现类,接下来直接获得proxy代理就可以使用了
//同样对于不同的类,只要将setTarget传入所需的类就可以动态生成代理类
test.setTarget(new Company());
Recruit hr = (Recruit) test.getProxy();
hr.hiring();
}
}
//执行结果
//before running
//面试中
//after running
如果需要记录方法的返回值,在invoke方法中直接打印result就可以了,
但是要注意有些方法返回值是null,所以记得判断当返回值不为空时再打印,否则会报错。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在方法运行之前
System.out.println("before running");
//运行方法时
Object result = method.invoke(target,args);
//打印返回值
if(result!=null)
System.out.println("result:"+result.toString());
else
System.out.println("result:null");
//在方法运行在之后
System.out.println("after running");
return null;
}