代理模式和动态代理实战应用

代理设计模式

java有20多种设计模式,代理模式肯定是非常出名的一种。
代理模式可以理解为不直接访问对象,让代理对象去达到某种目的。
一般是用于对方法的增强,在不动原代码的情况下,对该方法运行前,后,异常加一些处理。
最出名的Sping的AOP(面向切面编程)底层就是动态代理帮实现的。
在平时开发中,使用设计模式可以让我们的代码更加具有可扩展性。

静态代理

这个静态代理非常简单的。理解了上述的意思,我们就可以写个差不多的出来。看代码

package com.zyc.proxydesignpattern.staticproxy;
//这个是登录方法接口
public interface UserService {
    String login(String username, String password);
}
package com.zyc.proxydesignpattern.staticproxy;
//这个是登录方法实现类,并实现了 login方法
public class UserServiceImpl implements UserService {
    @Override
    public String login(String username, String password) {
        if("admin".equals(username) && "123".equals(password)){
            System.out.println("登录成功");
            return "loginSuccess";
        }else{
            System.out.println("登录失败");
            return "error";
        }
    }
}

package com.zyc.proxydesignpattern.staticproxy;

//这个是代理类  也需要实现UserService接口
public class UserServiceProxy implements UserService {
    //定义 被代理的对象
    private UserService userService;

    public UserServiceProxy(UserService userService){
        this.userService = userService;
    }

    //在调用的时候,给该方法的前后添加操作。
    @Override
    public String login(String username, String password) {
        System.out.println("---前置---");
        String result = userService.login(username,password);
        System.out.println("---后置---");
        return result;
    }
}


    @Test
    public void staticTest(){
        new UserServiceProxy(userServiceByStatic).login("admin","123");
    }
    //当调用该方法后运行的结果为
    ---前置---
    登录成功
    ---后置---

上边的三段代码就是一个简单的静态代理的demo
静态代理的缺点比较明显,这是一个UserService的代理,那再有AService,BService呢?
就需要重新创建代理类,比较麻烦。所以静态代理在实际开发中使用并不多。

动态代理(JDK)

说完静态代理,就说说JDK本身自带的动态代理。
JDK的动态代理是靠反射完成的。直接看代码。

package com.zyc.proxydesignpattern.dynamicproxy.jdk;
//接口
public interface UserService {
    String login(String username,String password);
}
package com.zyc.proxydesignpattern.dynamicproxy.jdk;
//实现类,到此为止和静态代理是一模一样的。
public class UserServiceImpl implements UserService{
    //JDK的动态代理必须要有接口,这是和cglib最大不同的地方
    @Override
    public String login(String username, String password) {
        if("admin".equals(username) && "123".equals(password)){
            System.out.println("登录成功");
            return "loginSuccess";
        }else{
            System.out.println("登录失败");
            return "error";
        }
    }
}

JDK的动态代理最重要的就是这个 InvocationHandler
我们需要实现他的 invoke方法,实际上也就是让目标方法运行

package com.zyc.proxydesignpattern.dynamicproxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler{

    //目标方法
    private Object target;

    public MyInvocationHandler(Object target){
        this.target = target;
    }

    public Object getProxy(){
        //获取该类的代理
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    //通过反射让方法运行。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("---前置---");
        Object result = method.invoke(target,args);
        System.out.println("---后置---");
        return result;
    }
}

    @Test
    public void dynamicTest(){
        private UserService userService = new UserServiceImpl();
        //创建MyInvocationHandler 来获取代理类,从而让代理类运行login方法
        UserService proxy = (UserService) new MyInvocationHandler(userService).getProxy();
        proxy.login("admin","123");

    }
    //当调用该方法后运行的结果为
    ---前置---
    登录成功
    ---后置---

有了动态代理,可以说就比静态代理方便了很多。
你需要代理什么,就传什么值就可以。也可以泛型下就不用强转了。
这只是一个小demo。就不弄那么麻烦了。
一会说为什么被代理类要有接口,
如果不想要接口 ,。其实还有一种动态代理,。

动态代理(cglib)

cglib原理是让目标类生成一个子类,然后让子类去进行方法的增强。

package com.zyc.proxydesignpattern.dynamicproxy.cglib;
//被代理类
public class UserService {
    //在用cglib的时候, UserService可以没有接口。
    //但是 login 方法不能用final修饰,不能用private修饰 ,因为cglib的代理原理是找该类的子类去继承该方法去实现,
    //如果用private 或者 final 修饰  无法继承该方法。
    public String login(String username, String password) {
        if("admin".equals(username) && "123".equals(password)){
            System.out.println("登录成功");
            return "loginSuccess";
        }else{
            System.out.println("登录失败");
            return "error";
        }
    }
}
package com.zyc.proxydesignpattern.dynamicproxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
//看好包名  一定要用 cglib下的MethodInterceptor
//这个是cglib代理要实现的接口
public class MyCglibProxy implements MethodInterceptor {

    //实现 MethodInterceptor 中的 intercept 接口
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("---前置---");
        //实际方法运行的地方
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("---后置---");
        return result;
    }

}
package com.zyc.proxydesignpattern.dynamicproxy.cglib;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Method;

//代理工厂
public class ProxyFactory {
    /**
     * 防止在外边创建
     */
    private ProxyFactory(){}

    //代理工厂方法。
    //这几天有时间的话 在写个工厂设计模式
    public static UserService getUserServiceProxy(MyCglibProxy myCglibProxy){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService.class);
        enhancer.setCallback(new MyCglibProxy());
        //这里就不详细写了,cglib功能挺多的
        //setCallbacks可是设置多个代理,然后根据 setCallbackFilter 的 accept 方法 看哪个方法走哪个代理。
//        enhancer.setCallbacks(new Callback[]{new MyCglibProxy()});
//        enhancer.setCallbackFilter((Method method)-> {
//            
//            return 0;
//        });
        return (com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService)enhancer.create();
    }
}
    @Test
    public void cglibTest(){
        /*这样搞不方便,可以搞一个工厂

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService.class);
        enhancer.setCallback(new MyCglibProxy());
        com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService userService = (com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService)enhancer.create();
        userService.login("admin","4554");*/
        //如果要是不用代理工厂的话,
        //按照上边写就行,但是这样写更简洁一点。也更有拓展性
        //通过代理工厂获取 代理,然后用代理去运行login
        ProxyFactory.getUserServiceProxy(new MyCglibProxy()).login("123","3443");
    }
    //当调用该方法后运行的结果为
    ---前置---
    登录失败
    ---后置---

虽然大家都知道反射的效率是很低的。但是在1.8以后 JDK的动态代理还是要比cglib要效率高一点的。
然后我们看看JDK的动态代理和cglib的动态代理有什么不同吧。
借图:

代理模式和动态代理实战应用_第1张图片
借的图

参考:
深入理解CGLIB动态代理机制

动态代理在实际开发中的使用

在这里我们基于 JDK的动态代理来搞
比如,我们开发中获取一个数据需要有降级处理,先从redis中获取,如果没有获取到在从mysql中获取。
我们看代码。

package com.zyc.proxydesignpattern.inaction.service.impl;

import com.zyc.proxydesignpattern.inaction.entity.Project;
import com.zyc.proxydesignpattern.inaction.service.ProjectService;
import org.springframework.stereotype.Service;
//很普通的service层
@Service
public class ProjectServiceImpl implements ProjectService{
    public Project getProjectById(String id){
        return new Project("123","假装从数据库中取出的项目");
    }
}

package com.zyc.proxydesignpattern.inaction.util;


import com.zyc.proxydesignpattern.inaction.entity.Project;
import org.springframework.stereotype.Component;
//同样很普通的redis操作工具
//我们假设id为123的项目在缓存中存在
@Component
public class RedisUtil {
    public Project getProjectById(String id){
        if(id!=null && "123".equals(id)){

         return new Project(id,"假装从缓存中取出的项目");
        }else{
            return null;
        }
    }
}
package com.zyc.proxydesignpattern.inaction.util.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//也很普通的代理工具 ,就多了几个操作类
public class DataBaseProxyHandler implements InvocationHandler {

    //被代理对象
    private Object delegate;

    //需要去做事情的接口
    private ProxyInterface myProxyInterface;

    //需要去做事情的类
    private Object param;

    //构造器 在这里给赋值
    public DataBaseProxyHandler(Object v) {
        this.param = v;
    }

    //proxy方法,返回
    public  T proxy(T delegate, ProxyInterface myProxyInterface) {
        this.myProxyInterface = myProxyInterface;
        this.delegate = delegate;

        return (T) Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(),
                this.delegate.getClass().getInterfaces(), this);
    }

    //就多了个几个类,其他一模一样
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {


        Object obj = null;

        if (args != null && args.length > 0){
            obj = myProxyInterface.doBegin(param,args);
        }else{
            obj = myProxyInterface.doBegin(param,null);
        }
        if (obj != null)
            return obj;

        obj = method.invoke(this.delegate, args);

        if (args != null && args.length > 0){
            myProxyInterface.doEnd(obj, param,args);
        }else{
            myProxyInterface.doEnd(obj, param,null);
        }

        return obj;
    }
}
package com.zyc.proxydesignpattern.inaction.util.proxy;

public interface ProxyInterface {
    /**
     * T 参数代表需要操作对象的工具类
     * V 参数实体对象
     * @param
     * @return
     */
    Object doBegin(T t, Object[] param);

    /**
     * T 参数代表需要操作对象的工具类
     * returnObj invok 后返回的参数
     *
     * @param returnObj
     * @param t
     * @return
     */
     Object doEnd(V returnObj, T t,Object[] param);
}
package com.zyc.proxydesignpattern.inaction.controller;

import com.zyc.proxydesignpattern.inaction.entity.Project;
import com.zyc.proxydesignpattern.inaction.service.ProjectService;
import com.zyc.proxydesignpattern.inaction.util.RedisUtil;
import com.zyc.proxydesignpattern.inaction.util.proxy.DataBaseProxyHandler;
import com.zyc.proxydesignpattern.inaction.util.proxy.ProxyInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class ProjectController {


    @Autowired
    ProjectService projectService;


    @Autowired
    RedisUtil redisUtil;

    //如果前边动态代理搞明白了。这些代码很容易理解
    //就多了个ProxyInterface去进行前置和后置
    public Project getProjectById(String id){
        //在这里用了个匿名内部类,就不在外边创建新的Proxy了。
        //在这里可以更加直观的看,
        return new DataBaseProxyHandler(redisUtil).proxy(projectService, new ProxyInterface() {
            //在doBegin方法中是 getProjectById 前做的事情。
            //从 DataBaseProxyHandler 的invoke 方法,可以看到 如果返回的是null才会去运行 getProjectById
            //如果不为null  则直接返回了。
            @Override
            public Object doBegin(Object o, Object[] param) {
                RedisUtil redisUtil = (RedisUtil)o;

                return redisUtil.getProjectById(id);
            }

            //在doEnd方法中 是 getProjectById 后做的事情
            //在 getProjectById 中 doEnd是不用做任何事的。
            //但是 如果是 saveProject 呢? 我们可以在doBegin中不做任何事情,
            //在doEnd中可以 判断如果saveProject插入到 mysql/oracle 那么在doEnd可以插入到缓存中。
            @Override
            public Object doEnd(Object returnObj, Object o, Object[] param) {
                return null;
            }
        }).getProjectById(id);
    }
}

以上就是动态代理在实际开发中的使用。
github:https://github.com/zycisbg/ProxyDesignPattern

你可能感兴趣的:(代理模式和动态代理实战应用)