Java代理是什么?为什么要用代理?以及代理的分类

Java 动态代理作用是什么?为什么要动态代理

转载自:https://blog.csdn.net/u010325193/article/details/83307473

① 首先你要明白静态代理的作用
我们有一个字体提供类,有多种实现(从磁盘,从网络,从系统)

public interface FontProvider {
 
Font getFont(String name);
 
}
 
 
 
public abstract class ProviderFactory {
 
public static FontProvider getFontProvider() {
 
return new FontProviderFromDisk();
 
}
 
}
 
 
 
public class Main() {
 
public static void main(String[] args) {
 
FontProvider fontProvider = ProviderFactory.getFontProvider();
 
Font font = fontProvider.getFont("微软雅黑");
 
......
 
}
 
}
现在我们希望给他加上一个缓存功能,我们可以用静态代理来完成

 
public class CachedFontProvider implements FontProvider {
 
private FontProvider fontProvider;
 
private Map<String, Font> cached;
 
 
 
public CachedFontProvider(FontProvider fontProvider) {
 
this.fontProvider = fontProvider;
 
}
 
 
 
public Font getFont(String name) {
 
Font font = cached.get(name);
 
if (font == null) {
 
font = fontProvider.getFont(name);
 
cached.put(name, font);
 
}
 
return font;
 
}
 
}
 
 
 
 
 
/* 对工厂类进行相应修改,代码使用处不必进行任何修改。
 
这也是面向接口编程以及工厂模式的一个好处 */
 
public abstract class ProviderFactory {
 
public static FontProvider getFontProvider() {
 
return new CachedFontProvider(new FontProviderFromDisk());
 
}
 
}

 

当然,我们直接修改FontProviderFromDisk类也可以实现目的,但是我们还有FontProviderFromNet, FontProviderFromSystem等多种实现类,一一修改太过繁琐且易出错。

况且将来还可能添加日志,权限检查,异常处理等功能显然用代理类更好一点。

② 然而为什么要用动态代理?
考虑以下各种情况,有多个提供类,每个类都有getXxx(String name)方法,每个类都要加入缓存功能,使用静态代理虽然也能实现,但是也是略显繁琐,需要手动一一创建代理类。

public abstract class ProviderFactory {
 
public static FontProvider getFontProvider() {...}
 
public static ImageProvider getImageProvider() {...}
 
public static MusicProvider getMusicProvider() {...}
 
......
 
}

 

使用动态代理怎么完成呢?

public class CachedProviderHandler implements InvocationHandler {
 
private Map<String, Object> cached = new HashMap<>();
 
private Object target;
 
 
 
public CachedProviderHandler(Object target) {
 
this.target = target;
 
}
 
 
 
public Object invoke(Object proxy, Method method, Object[] args)
 
throws Throwable {
 
Type[] types = method.getParameterTypes();
 
if (method.getName().matches("get.+") && (types.length == 1) &&
 
(types[0] == String.class)) {
 
String key = (String) args[0];
 
Object value = cached.get(key);
 
if (value == null) {
 
value = method.invoke(target, args);
 
cached.put(key, value);
 
}
 
return value;
 
}
 
return method.invoke(target, args);
 
}
 
}
 
 
 
public abstract class ProviderFactory {
 
public static FontProvider getFontProvider() {
 
Class<FontProvider> targetClass = FontProvider.class;
 
return (FontProvider) Proxy.newProxyInstance(targetClass.getClassLoader(),
 
new Class[] { targetClass },
 
new CachedProviderHandler(new FontProviderFromDisk()));
 
}
 
}

 

③ 这也是为什么Spring这么受欢迎的一个原因

Spring容器代替工厂,Spring AOP代替JDK动态代理,让面向切面编程更容易实现。
在Spring的帮助下轻松添加,移除动态代理,且对源代码无任何影响。

===================================================================================================

代理的分类

1、动态代理和静态代理

2. 动静态代理的区别,什么场景使用?(2015-11-25)
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的
业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行
时,动态修改字节码达到修改类的目的。
AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、Hibernate 框架等等都是动态代理的使用例子。
3、写一个 ArrayList 的动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * @author cdlr
 * @version 1.0
 * @description:
 * @date 2020/4/28 11:05
 */
public class ArrayListProxyDemo {

    public static void main(String[] agrs){
        final List<String> list = new ArrayList<String>();

        List<String> proxyInstance = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(),  new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return method.invoke(list, args);
            }
        });

        proxyInstance.add("ArrayList动态代理");
        System.out.println(list);
    }
}

 

posted @ 2020-05-14 09:57  Gentleman-cx  阅读(776)  评论(0编辑  收藏  举报