关键字:动态代理、代理模式、spring中的代理相关、装饰器模式
静态代理
首先建立一个接口interface,想要被代理的对象类A实现接口interface,想要使用的代理对象类B实现接口,并且在类中私有建立一个对象A,并且在B的构造函数里面初始化这个对象A(或者提供一个setter方法)。在接口实现的方法里可以编写要代理的逻辑并且调用对象A里面实现的方法。使用的时候先new一个对象A,然后将对象传给一个new对象B,调用B的方法,就实现了静态代理。
public interface Intervieew {
public void mianshi();
}
public class Myintervieew implements Intervieew {
@Override
public void mianshi() {
System.out.println("面试中-----");
}
}
创建静态代理:
public class MyintervieewProxy implements Intervieew {
private Intervieew interView;
public void setIntervieew(Intervieew intervieew ) {
this.interView = interView;
}
@Override
public void mianshi() {
System.out.println("请个大神过来帮我面试");
interView.mianshi();
}
}
动态代理:有proxy和cglib两种
首先建立一个接口interview和被代理的类与上面相似
和静态代理不同的是,代理逻辑对象实现invocationHandler接口
public class MyHandler implements InvocationHandler {
private Object intverview ;
public MyHandler(Object myintervieew){
this.intverview = myintervieew;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res;
System.out.println("请个大神来帮忙");
res = method.invoke(intverview,args);
System.out.println("面试结束,回家养猪");
return res;
}
}
其实到这里就可以使用了,但是我们为了代码的美感,进行封装一个代理工厂类。
封装成一个工厂类
public class ProxyFactory {
public static Object getProxy(Object myintervieew){
MyHandler handler = new MyHandler(myintervieew);
return Proxy.newProxyInstance(myintervieew.getClass().getClassLoader(),myintervieew.getClass().getInterfaces(),handler);
}
}
动态代理的关键便是实现invocationHandler接口和使用Proxy.newProxyInstance方法(可以注意到这里是返回了一个全新的对象)。
下面是测试案例
public class Test {
public static void main(String[] args) {
Intervieew myintervieew = new Myintervieew();
Intervieew proxy = (Intervieew) ProxyFactory.getProxy(myintervieew);
proxy.mianshi();
}
}
cglib包需要另行下载包,就没有记录。invocationHandler接口代理需要代理类实现一个接口,然后通过反射的方式。cglib则不需要接口,他是建立一个被代理类的子类来实现代理,所以cglib不能代理由final修饰的类
动态代理与静态代理的区别:
静态代理模式可以看到我们创建的代理对象是我们自己写好的,也就是MyintervieewProxy这个类,在运行时是会编译成一个class文件存在我们的项目中的。但是动态代理是没有编译成这个class文件存在的,MyHandler存放的只是我们需要代理的逻辑,但是最终创建的对象是Proxy.newProxyInstance返回的对象,这个对象是没有生成相应的class文件的,它是虚拟机在运行的时候生成字节码放在jvm中。这也就是所谓的动态与静态。
spring中的代理理解:
spring有一个叫容器的技术,就是可以存放bean对象,我们要用到这个对象时直接从容器中获取。加入有一个对象A存放在容器中,我们现在要获取A,但是A可能需要创建代理进行增强等。但是我们使用的时候并不需要知道A是否创建了代理,我们获取A后还是用A去接收。
public class Test implement BeanPostProcessor{
private Map container;
public static void main(String[] args) {
putIntoContainer(new A, a);
//存放对象到容器中,框架层处理的,一般对程序员不感知,程序员只知道容器中有a,但是存放的是不是a并不清楚,这里我们对a做了特殊处理放进去一个B(代理)
Object B = getFromContainer(a);
//这里我们用什么类型接收容器中获取到的a呢??
//如果a实现了某种接口,比如Intervieew,那么我们可以这样接收
//Intervieew B = getFromContainer(a); 这个就是文中动态代理的实现方式可以这样处理
// 如果A没有实现接口呢,那怎么办?
// A B = getFromContainer(a);如果B是A的子类不就可以这样接收了吗?
//cglib包就是这样操作的。它会动态生成一个代理类B extends A
}
private void putIntoContainer(Object bean, String key){
if(bean instanceof A) {
//创建代理对象B
container.put(key, B);
}
}
private Object getFromContainer(String key) {
return container.get(key);
}
}
其实不仅是spring,一个框架都应该考虑这种情况的处理。
代理模式和装饰器模式
这两种模式都是对原有功能的增强,装饰器典型应用就是java的文件流处理。
FileInputStream fileInputStream = new FileInputStream("");
InputStreamReader reader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(reader);
通过一层一层包装实现功能增强,如果关闭只需要关闭一个就好了
bufferedReader.close();
//底层的close代码
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
try {
in.close();
} finally {
in = null;
cb = null;
}
}
}
FileInputStream、InputStreamReader、BufferedReader这里这三个虽然功能不同,但本质上还是流,他们是有一定的内在联系的。但是代理模式因实现方式区别可能也会实现相同的接口,但是代理与被代理对象之间的联系我们不是很在意的,更加关注功能的不同。