Java动态代理

代理和动态代理
Java动态代理

代理模式
为其他对象提供一个代理以控制对某个对象的访问。
代理类主要负责为委托了(真实对象)预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,并将执行结果封装处理。(就像经纪人(代理类)只是接受邀约剧本,但是并不自己唱歌演戏,而是明星(委托类)自己表演)
其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。
代理类和被代理类通常会存在关联关系(即上面提到的持有的被带离对象的引用),代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务



静态代理

  • 创建一个接口
  • 创建被代理的类去实现该接口并且实现该接口中的抽象方法。
  • 创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法

接口:

public interface HelloInterface {
    void sayHello();
}

被代理类(真正干活的明星):

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}

代理类:

public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Before invoke sayHello" );
        helloInterface.sayHello();
        System.out.println("After invoke sayHello");
    }
}

代理类调用:
被代理类被传递给了代理类HelloProxy,代理类在执行具体方法时通过所持用的被代理类完成调用。

    public static void main(String[] args) {
        HelloProxy helloProxy = new HelloProxy();
        helloProxy.sayHello();
    }
    
输出:
Before invoke sayHello
Hello zhanghao!
After invoke sayHello


使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。




动态代理

利用反射机制在运行时创建代理类。
接口、被代理类不变,我们构建一个handler类来实现InvocationHandler接口。

public class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "  + method.getName());
        method.invoke(object, args);
        System.out.println("After invoke " + method.getName());
        return null;
    }
}

执行动态代理:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        HelloInterface hello = new Hello();
        
        InvocationHandler handler = new ProxyHandler(hello);

        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        proxyHello.sayHello();
    }
    输出:
    Before invoke sayHello
    Hello zhanghao!
    After invoke sayHello

通过Proxy类的静态方法newProxyInstance 返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。
如果新来一个被代理类Bye,如:

public interface ByeInterface {
    void sayBye();
}
public class Bye implements ByeInterface {
    @Override
    public void sayBye() {
        System.out.println("Bye zhanghao!");
    }
}

那么执行过程:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        HelloInterface hello = new Hello();
        ByeInterface bye = new Bye();

        InvocationHandler handler = new ProxyHandler(hello);
        InvocationHandler handler1 = new ProxyHandler(bye);

        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        ByeInterface proxyBye = (ByeInterface) Proxy.newProxyInstance(bye.getClass().getClassLoader(), bye.getClass().getInterfaces(), handler1);
        
        proxyHello.sayHello();
        proxyBye.sayBye();
    }
    输出:
    Before invoke sayHello
    Hello zhanghao!
    After invoke sayHello
    Before invoke sayBye
    Bye zhanghao!
    After invoke sayBye


动态代理底层实现
动态代理具体步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

Java提供了一个Proxy类,**调用它的newInstance方法可以生成某个对象的代理对象,**该方法需要三个参数:

在这里插入图片描述
•参数一:生成代理对象使用哪个类装载器【一般我们使用的是被代理类的装载器】

•参数二:生成哪个对象的代理对象,通过接口指定【指定要被代理类的接口】

•参数三:生成的代理对象的方法里干什么事【实现handler接口,我们想怎么实现就怎么实现】

在编写动态代理之前,要明确几个概念:

•代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口,代理对象会实现接口的所有方法】

•用户调用代理对象的什么方法,都是在调用处理器的invoke方法。【被拦截】

•使用JDK动态代理必须要有接口【参数二需要接口】

上面也说了:代理对象会实现接口的所有方法,这些实现的方法交由我们的handler来处理!

•所有通过动态代理实现的方法全部通过invoke()调用
在这里插入图片描述


所以动态代理调用过程是这样子的:
在这里插入图片描述




典型应用

我们之前写中文过滤器的时候,需要使用包装设计模式来设计一个request类。如果不是Servlet提供了实现类给我们,我们使用包装设计模式会比较麻烦

现在我们学习了动态代理了,动态代理就是拦截直接访问对象,可以给对象进行增强的一项技能

中文过滤器:

   public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        final HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("UTF-8");


        //放出去的是代理对象
        chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //判断是不是getParameter方法
                if (!method.getName().equals("getParameter")) {

                    //不是就使用request调用
                   return method.invoke(request, args);
                }

                //判断是否是get类型的
                if (!request.getMethod().equalsIgnoreCase("get")) {
                   return method.invoke(request, args);
                }

                //执行到这里,只能是get类型的getParameter方法了。
                String value = (String) method.invoke(request, args);
                if (value == null) {
                    return null;
                }
                return new String(value.getBytes("ISO8859-1"), "UTF-8");
            }

        }), response);

    }

要实现动态代理必须要有接口的,动态代理是基于接口来代理的(实现接口的所有方法),如果没有接口的话我们可以考虑cglib代理。

cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!

总的来说:代理模式是我们写代码中用得很多的一种模式了,Spring的AOP底层其实就是动态代理来实现的–>面向切面编程。具体可参考我之前写的那篇文章:

记住一点:原有的对象需要额外的功能,想想动态代理这项技术

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值