Java56项目网
我们一直用心在做

手动模拟JDK动态代理

admin阅读(11)

为哪些方法代理?

实现自己动态代理,首先需要关注的点就是,代理对象需要为哪些方法代理? 原生JDK的动态代理的实现是往上抽象出一层接口,让目标对象和代理对象都实现这个接口,怎么把接口的信息告诉jdk原生的动态代理呢? 如下代码所示,Proxy.newProxyInstance()方法的第二个参数将接口的信息传递了进去第一个参数的传递进去一个类加载器,在jdk的底层用它对比对象是否是同一个,标准就是相同对象的类加载器是同一个


ServiceInterface) Proxy.newProxyInstance(service.getClass().getClassLoader()
                , new Class[]{ServiceInterface.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("前置通知");
                method.invoke(finalService,args);
                System.out.println("后置通知");
                return proxy;
            }
        });

我们也效仿它的做法. 代码如下:


public class Test {
    public static void main(String[] args) {
        IndexDao indexDao = new IndexDao();
        Dao  dao =(Dao) ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao));
        assert dao != null;
        System.out.println(dao.say("changwu"));
    }
}

拿到了接口的Class对象后,通过反射就得知了接口中有哪些方法描述对象Method,获取到的所有的方法,这些方法就是我们需要增强的方法

如何将增强的逻辑动态的传递进来呢?

JDK的做法是通过InvocationHandler的第三个参数完成,他是个接口,里面只有一个抽象方法如下: 可以看到它里面有三个入参,分别是 代理对象,被代理对象的方法,被代理对象的方法的参数


public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

当我们使用jdk的动态代理时,就是通过这个重写这个钩子函数,将逻辑动态的传递进去,并且可以选择在适当的地方让目标方法执行

InvocationHandler接口必须存在必要性1:

为什么不传递进去Method,而是传递进去InvocationHandler对象呢? 很显然,我们的初衷是借助ProxyUtil工具类完成对代理对象的拼串封装,然后让这个代理对象去执行method.invoke(), 然而事与愿违,传递进来的Method对象的确可以被ProxyUtil使用,调用method.invoke()但是我们的代理对象不能使用它,因为代理对象在这个ProxyUtil还以一堆等待拼接字符串, ProxyUtil的作用只能是往代理对象上叠加字符串,却不能直接传递给它一个对象,所以只能传递一个对象进来,然后通过反射获取到这个对象的实例,继而有可能实现method.invoke()

InvocationHandler接口必须存在必要性2:

通过这个接口的规范,我们可以直接得知回调方法的名字就是invoke()所以说,在拼接字符串完成对代理对象的拼接时,可以直接写死它

思路

我们需要通过上面的ProxyUtil.newInstance(Dao.class,new MyInvocationHandlerImpl(indexDao))方法完成如下几件事

  • 根据入参位置的信息,提取我们需要的信息,如包名,方法名,等等
  • 根据我们提取的信息通过字符串的拼接完成一个全新的java的拼接
    • 这个java类就是我们的代理对象
  • 拼接好的java类是一个String字符串,我们将它写入磁盘取名XXX.java
  • 通过ProxyUtil使用类加载器,将XXX.java读取JVM中,形成Class对象
  • 通过Class对象反射出我们需要的代理对象

ProxyUtil的实现如下:

public static Object newInstance(Class targetInf, MyInvocationHandler invocationHandler) {

    Method methods[] = targetInf.getDeclaredMethods();
    String line = "\n";
    String tab = "\t";
    String infName = targetInf.getSimpleName();
    String content = "";
    String packageContent = "package com.myproxy;" + line;
    //   导包,全部导入接口层面,换成具体的实现类就会报错
    //   
    String importContent = "import " + targetInf.getName() + ";" + line
                           + "import com.changwu.代理技术.模拟jdk实现动态代理.MyInvocationHandler;" + line
                           + "import java.lang.reflect.Method;" + line
                           + "import java.lang.Exception;" + line;

    String clazzFirstLineContent = "public class $Proxy implements " + infName +"{"+ line;
    String filedContent = tab + "private MyInvocationHandler handler;"+ line;
    String constructorContent = tab + "public $Proxy (MyInvocationHandler  handler){" + line
            + tab + tab + "this.handler =handler;"
            + line + tab + "}" + line;
    String methodContent = "";
    // 遍历它的全部方法,接口出现的全部方法进行增强
    for (Method method : methods) {
        String returnTypeName = method.getReturnType().getSimpleName();         method.getReturnType().getSimpleName());

        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();

        // 参数的.class
        String paramsClass = "";
        for (Class<?> parameterType : parameterTypes) {
            paramsClass+= parameterType.getName()+",";
        }

        String[] split = paramsClass.split(",");

        //方法参数的类型数组 Sting.class String.class
        String argsContent = "";
        String paramsContent = "";
        int flag = 0;
        for (Class arg : parameterTypes) {
            // 获取方法名
            String temp = arg.getSimpleName();
            argsContent += temp + " p" + flag + ",";
            paramsContent += "p" + flag + ",";
            flag++;
        }
        // 去掉方法参数中最后面多出来的,
        if (argsContent.length() > 0) {
            argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);
            paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);
        }
        methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line
                + tab + tab+"Method method = null;"+line
                + tab + tab+"String [] args0 = null;"+line
                + tab + tab+"Class<?> [] args1= null;"+line

                // invoke入参是Method对象,而不是上面的字符串,所以的得通过反射创建出Method对象
                + tab + tab+"try{"+line
                // 反射得到参数的类型数组
                 + tab + tab + tab + "args0 = \""+paramsClass+"\".split(\",\");"+line
                 + tab + tab + tab + "args1 = new Class[args0.length];"+line
                 + tab + tab + tab + "for (int i=0;i<args0.length;i++) {"+line
                 + tab + tab + tab + "   args1[i]=Class.forName(args0[i]);"+line
                 + tab + tab + tab + "}"+line
                // 反射目标方法
                + tab + tab + tab + "method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\",args1);"+line
                + tab + tab+"}catch (Exception e){"+line
                + tab + tab+ tab+"e.printStackTrace();"+line
                + tab + tab+"}"+line
                + tab + tab + "return ("+returnTypeName+") this.handler.invoke(method,\"暂时不知道的方法\");" + line; //
                 methodContent+= tab + "}"+line;
    }

    content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";

    File file = new File("d:\\com\\myproxy\\$Proxy.java");
    try {
        if (!file.exists()) {
            file.createNewFile();
        }

        FileWriter fw = new FileWriter(file);
        fw.write(content);
        fw.flush();
        fw.close();

        // 将生成的.java的文件编译成 .class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(file);
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();

        // 使用类加载器将.class文件加载进jvm
        // 因为产生的.class不在我们的工程当中
        URL[] urls = new URL[]{new URL("file:D:\\\\")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class clazz = urlClassLoader.loadClass("com.myproxy.$Proxy");
        return clazz.getConstructor(MyInvocationHandler.class).newInstance(invocationHandler);
    } catch (Exception e) {
        e.printStackTrace();
    }
       return null;
}
}

运行的效果:

package com.myproxy;
import com.changwu.myproxy.pro.Dao;
import com.changwu.myproxy.pro.MyInvocationHandler;
import java.lang.reflect.Method;
import java.lang.Exception;
public class $Proxy implements Dao{
    private MyInvocationHandler handler;
    public $Proxy (MyInvocationHandler  handler){
        this.handler =handler;
    }
    public String say(String p) {
        Method method = null;
        String [] args0 = null;
        Class<?> [] args1= null;
        try{
            args0 = "java.lang.String,".split(",");
            args1 = new Class[args0.length];
            for (int i=0;i<args0.length;i++) {
               args1[i]=Class.forName(args0[i]);
            }
            method = Class.forName("com.changwu.myproxy.pro.Dao").getDeclaredMethod("say",args1);
        }catch (Exception e){
            e.printStackTrace();
        }
        return (String) this.handler.invoke(method,"暂时不知道的方法");
    }
}

解读

通过newInstance()用户获取到的代理对象就像上面的代理一样,这个过程是在java代码运行时生成的,但是直接看他的结果和静态代理差不错,这时用户再去调用代理对象的say(), 实际上就是在执行用户传递进去的InvocationHandeler里面的invoke方法, 但是亮点是我们把目标方法的描述对象Method同时给他传递进去了,让用户可以执行目标方法+增强的逻辑

当通过反射区执行Method对象的invoke()方法时,指定的哪个对象的当前方法呢? 这个参数其实是我们手动传递进去的代理对象代码如下


public class MyInvocationHandlerImpl implements MyInvocationHandler {
    private Object obj;
    public MyInvocationHandlerImpl(Object obj) {
        this.obj = obj;
    }
    @Override
    public Object invoke(Method method, Object[] args) {
        System.out.println("前置通知");
        try {
            method.invoke(obj,args);
        } catch (Exception e) {
            e.printStackTrace();
        }  
        System.out.println("后置通知");
        return null;
    }
}

作者:赐我白日梦

https://www.cnblogs.com/ZhuChangwu/p/11648911.html

 

关注微信公众号,非常感谢;

技术大牛如何找到靠谱的创业想法?

admin阅读(11)

对于不少有志创业的技术大牛,技术能力并非瓶颈,怎么找到靠谱的创业想法却让他们踩坑。

技术创业的门槛越来越高,商汤、旷世、寒武纪等AI独角兽聚集了众多技术人才,凭借着技术和人才的优势成为赛道领导者,这让很多技术大牛跃跃欲试。

懂商业的CEO+技术强的CTO仍是很好的创始团队组合,但很多成功技术创业公司的创始人不仅技术强,而且懂商业,甚至会在创业初期同时身兼CEO+CTO的职责,这是技术创业对创业者提出的高要求。

对于不少有志创业的技术大牛,技术能力并非瓶颈,怎么找到靠谱的创业想法却让他们踩坑:

第一个坑:拿着锤子找钉子

在最能发挥自己技术优势的领域创业是一个正常的逻辑,但这里隐藏的坑是,一些技术大牛太过于重视技术,以技术而非用户(客户)为中心去开发产品,最后满足的是一个没有人买单的伪需求。

例如一家技术创业公司,创始人背景很好,技术领先,凭借技术优势在初期融资顺利,但产品开发出来后落地困难,没有足够的收入,又因为商业模式没有被验证而后续融资困难,最后因为资金枯竭而创业失败。

第二个坑:只想做下一个谷歌、Facebook

创业要Dream Big,要找到体量巨大的蓝海市场,但这并不意味着每一个创业者都要创立下一个谷歌或Facebook。创业十分讲究Timing,这些巨头看似是从无到有开创了一个新市场,但当它们进入的时机,都是需求和技术成熟到恰到好处的时候。

进入市场太早,很容易成为别人的垫脚石。在一个过于前沿的赛道奔跑,成功的概率并不高,可能需求确实存在,但若其他的条件不成熟,也会失败。例如闪送的创始人于建红在2009年就做过类似菜鸟裹裹的项目,结果因为各方面的(主要是人员分工专业化和信息化)条件不成熟,导致项目失败。积累了这次失败的经验后,他在2013年抓准时机重新开始做闪送,但直到2017年和2018年闪送才真正进入大众视野。

明确了哪些坑应该避开,技术大牛们要用什么方法找到自己的创业想法?

方法一:聚焦

其实无论是否技术大牛,创业者都应该用聚焦的方式寻找属于自己的创业想法。我们假设有两种创业的领域,一个领域的用户量非常大,但用户的需求并不强烈;另一个领域用户量较少,但用户的需求非常迫切。几乎所有良好的创业想法都诞生在第二个领域。

第一个领域看起来拥有庞大的用户量,“市场天花板”很高,但需求不迫切代表用户可用可不用,可代替性高,用户和客户也就不会有强烈的付费意愿。第二个领域用户需求迫切,就更容易建立护城河,客户(用户)用了就离不开,付费意愿强,创业公司会拥有自我造血的能力。

当然,选择聚焦并不代表选择一个小的市场,产品在一个聚焦的市场把模式跑通后,还要能拥有在同类市场复制的能力。例如Facebook,最初在哈佛的校园跑通了模式,随后扩展到所有大学校园,乃至全世界。Uber,最初是在旧金山一个城市试点,现在其服务已经覆盖了全球的上百个国家。

方法二:从自身出发

从自身出发包括两个部分,第一是从自身的需求出发,第二是从自身的热爱出发。教育领域的创业公司Remind就是从自身需求出发的典型,该公司的联合创始人之一Brett Kopf上学期间被诊断患有注意力缺陷症,他的兄弟David Kopf为Brett建立了一个工具,能提醒他不要错过各类测试,这真的提升了Brett的学校表现。之后,兄弟俩决定把这个工具扩充成创业项目,解决学生、家长和老师之间的沟通效率问题。目前Remind在美国的月活用户超过2000万,覆盖美国50%以上的公立学校。

从自身热爱出发的典型是GoPro,它由Nick Woodman创立。Woodman热爱冲浪,在前两次的创业失败后,他与女友进行了一次冲浪之旅,在旅行的过程中,他萌生了一个想法:要是有一个可以安装手腕上的摄像头,能拍摄高质量的冲浪动作镜头就好了。最初他实现这个想法的方式是开发了一个定制的腕带,并与其他小型相机配合,而GoPro的第一个大订单是来自日本体育节目的100台相机。之后,运动相机这个市场逐渐成熟,更多的竞争者进来,但因为对需求的把握最精准,GoPro在很长时间内占据着市场的领导地位。

在有了几个初步创业想法后,应该通过以下的步骤筛除不靠谱的想法,并让那个相对靠谱的想法更加完善。

步骤一:建立列表

这个列表包括创业想法应该考虑的各类因素,包括:

 

客户(用户)是谁,他的需求是什么?

我们的解决方案是什么(概要)?

市场规模有多大?

为什么我们的方案比现有解决方案更好?(更快?更便宜?更可靠?)

商业模式。(谁付钱,单次付费金额,付费频次多少?)

商业化。(可至少分三个阶段:如果是To C的想法,阶段可分为100个用户,10000个用户,1000000个用户。如果是To B想法,阶段可分为1个客户,10个客户,100个客户。)

未来的增长。(如果成功解决了现有问题,还有什么相关的问题可以解决?)

 

步骤二:剔除小市场

首先应该剔除的就是市场规模不够大的想法。做以下几个动作效率会更高:

1.找出客户群的大小

可以参考人口数据,各类研究机构的报告,也可以做比较分析,研究相似产品的客群。

2.判断预期使用频率

使用频率非常重要,因为它将直接影响获客策略,客户生命周期价值,客户流失预测及定价模型等。一个明显的例子:一个用户每年使用几次的产品肯定不适合用月度订阅的形式收费。

3.判断“痛点”的疼痛程度

正如前文所说,如果瞄准的需求和“痛点”不够疼痛,那就不够聚焦,也就不容易建立起护城河。

4.判断客户的支付意愿

免费模式对于获客确实有帮助,但公司要活下去需要持续的收入和现金流。对于初创公司,“通过广告赚钱”并不是一个很好的模式,因为只有规模够大的平台级公司才能用广告模式获取足够的收入和利润,例如谷歌或百度。

步骤三:剔除薄弱的商业模式

很多新颖的创业想法之所以胎死腹中,就是因为无法找到合适的商业模式。靠谱的商业模式,建立在对客户(用户)足够了解的基础上,这不仅意味着大量的市场调查,用户的体验测试,在现在的时代也包括数据分析。

在确定商业模式时,固定成本、客户增长速度、客户获取成本、定价、客户生命周期价值等都需要考虑。

在筛除时,可以把商业模式建立保守、合理和乐观三个版本。在保守模式中,将获客成本设得偏高,客户生命周期价值舍得偏低,客户增速设得较慢,反之则以此类推。

如果一个想法的商业模式在乐观版本下可行,那并不“性感”,但若在保守版本下也能Work,则相对靠谱。

步骤四:验证技术可行性

技术大牛们在验证技术可行性方面经验丰富,本文对具体的过程不再赘述,只强调与创业相关的几个要点:

第一、找出技术可行性的过程同时也是验证需求是否强烈、是否“真实”的过程,以需求为中心构建产品,是在创业的任何阶段都应该坚持的原则。

第二、在创业初期就应该搭建良好的技术架构,当创业进入扩张期,大量的新增用户和新增数据会带来很大的冲击,而架构不够好时,在升级时也会浪费很多成本。

第三、应该预想好每一个产品阶段需要哪些类型和多少数量的相关技术人才,这关乎人才成本以及团队的建设规划。

最后,创业是一个长期的战役,它可能会花费创业者至少4-5年的时间,所以找到靠谱的创业想法不能靠拍脑袋,也不应该在半个月或一个月的时间里做决定。靠谱的创业想法对于创业成功与否起到的作用是决定性的,它值得创业者们花时间慎重对待。

关注微信公众号,非常感谢;

如何从程序员走向技术管理岗位?

admin阅读(10)

    
华丽转身是华而不实的假面具,我作为一名技术管理人员,建议大家不要轻易的转向管理岗位,坚持自己的技术才是根本。因为只有10%的技术专业人士具备相应的管理岗位所需要的特质,而更少的这样的人能够走到最后,管理岗位所做的工作不是技术人员所认为的非黑即白这样的事情,管理人员整个发展轨迹不仅仅需要自身能力、理念的培养,更需要伯乐的支持,而伯乐的支持是可遇不可求的,与其等待不如在自己可控制的领域一直前进,有机会自己也能够以更好的状态接受挑战。

        想接受挑战首先要认识自己,看自己是否适合做管理不妨考虑以下几点:对待问题是否公平公正,是否能够牺牲自我的利益顾全大局,是否有洞察力,是否能够了解组员的想法,是否能够做出正确的决策在没有条件许可的前提下……另外在该职责里面有两个重要的事情要做,一件事情是帮着老板干活,另一件事是替组员说话帮助他们争取相应的权益,所以夹心层的处理方式是处理好承担与托付,即责任的承担和信任的建立。技术管理者的技术不是最资深的,但应该具备专业的能力,并且是某个领域的技术和业务专家,因为一个人的能力是有限的,管理上有所建树便注定了技术道路上深度有限,但他有自己的技术见解并能够在需求把控、产品设计、架构设计中给予建设性意见并作出正确的决策,也只有这样才不会变成空中楼阁的大领导,拥有良好的技术基础是赢得你的团队信任的前提,也是自己能够建设一个可信任的团队的前提。作为管理者只有得到大家的认可,大家的信任,才能进而营造一个可信任的团队,使得各个部门之间互信,协调一致。而得到大家的信任作为管理者必须要有一定技术基础,也只有基于该基础才能做出正确的判断力,才能够确保团队整体方向的正确性。

一定走上管理岗位就很有成就感吗,我个人不敢苟同,这在于自己对自身的认知和个人经历的不同而不同,不能强求,但我建议每个技术人员能够经历项目经理的角色,尝试一下管理团队,看一下有哪些问题要解决,换一种角度思考你所参与的项目,培养自己的大局观,大局观重要性在于能够让个体将一个事物分析的更清晰,只有站得高才能看得远,大局观对个人而言很重要,拥有大局观会使个体在把握处理事情上更公正清晰,并更有利于作出正确的决策使得工作向良性方向发展。对于技术学习本身也需要融会贯通、举一反三,这也是一种意义上的大局观,所以思考角度决定着自己在其发展方向前进的远近,不要只是将自己局限于技术细节当中。

认识自己很重要,因为只有自己做了擅长的事情才能够事半功倍,游刃有余,改变一个人太难,与其效果甚微的改掉缺点不如发挥自己的优势。国外有一种创新的思维,认为缺点和优势其实是相辅相成的,你的缺点正好反映出了你拥有它对应的优点,换一个角度扬长避短,真的就事半功倍了。想一想对于组员又何尝不是呢,与其说服改变一个人不如知人善用,扬长避短。

时间管理上,处理问题上要有优先级,因为时间是有限的,资源也是有限的,管理者的决策影响着你的团队的方向,只有将更多地精力放在重要不紧急的事情上,才能产生更大的效能。做不重要的事情,再辛苦也是徒劳的。

如果说了这些你还是想走这条路不如先从思维习惯上转变一下角度,以下是管理培训中经常会提到的,我们也不妨再思考一下:

1.强将手下无弱兵

由专业技术人员转型为管理人员的管理者,常常因为自身的技术能力强,而永远以一个强者的姿态出现在下属面前,下属的一切问题都代为解决,认为强将手下无弱兵,这样就能够领导好团队。实际上这种做法却在无形中阻碍了下属的健康成长,使他们不能在实际工作中独当一面,因而管理学中有句名言:强将手下必弱兵。

2.不打无准备之仗

由于专业技术人员崇尚科学,所以在很多情况下,如果认为还没有准备充分,就很难做出科学、正确的决定;而作为管理人才,有时候就需要在准备尚未充足的情况下做出决定。

3.亲力亲为

专业转型的管理人员喜欢凡事亲力亲为,总是替下属解决本属于他们自己应该解决的问题,结果往往在这方面花费过多的精力,而没有更好地通过有效管理措施,提高整个团队的绩效。

4.追求技术完美

专业转型的管理人员往往过度追求技术上的完美,而牺牲了工作速度,甚至舍本逐末,忽略了对最终结果的认定。

关注微信公众号,非常感谢;

 

Java56项目网 更专业 更方便

联系我们