java高新技术-代理
发布日期:2021-07-12 08:49:33 浏览次数:4 分类:技术文章

本文共 13640 字,大约阅读时间需要 45 分钟。

程序中代理的概念与作用

  要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等。

  代理架构图:

  

 

   如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是使用代理类,

  这样以后很容易切换,例如:想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易。

 

1.分析代理类的作用与原理及AOP概念

  代理是实现AOP功能的核心和关键技术。

  系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。

  安全,事务,日志等功能要贯穿到好多个模块中,所以,他们就是交叉业务

  用具体的程序代码描述交叉业务:

  

  交叉业务的编程问题即为面向方面的变成(Aspect oriented program,简称AOP,AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围。这与直接在方法中编写切面代码的运行效果是一样的。因为不可能去修改用户的代码。如下所示:

      ---------------------------------------------------- 切面

                func1                   func2                         func3

      {                          {                                {

 

      }                           }                                }

       ---------------------------------------------------- 切面

2.动态代理技术

  > JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类

    JVM动态生成的类不是代理,而是可以作为其他类的代理。

  > JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理.

  *如果目标类没有实现接口,要想生成代理使用 CGLIB

  > CGLIB 库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的的类生成动态代理类,那么可以使用CGLIB库。

  > 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

    > 1.在调用目标方法之前

    > 2.在调用目标方法之后

    > 3.在调用目标方法前后

    > 4.在处理目标方法异常的catch块中

    

void sayHello(){       ..............            try{             target.sayHello();       }catch(Exception e){                 ...............       }       .................  }

 

  

创建动态类及查看其方法列表信息

  分析JVM动态生成的类

    创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数    

 

static <?> getProxyClass( loader, <?>... interfaces)
Returns the java.lang.Class object for a proxy class given a class loader and an array of interfaces.

    编码列出动态类中的所有构造方法和参数签名

    编码列出动态类中的所有方法和签名

    创建动态类的实例对象

      > 用反射获得构造方法

      > 编写一个最简单的InvocationHandler

      > 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

      > 打印创建的对象和调用对象的没有返回值得方法和getClass方法,调用其他有返回值的方法会抛异常。

 

public class ProxyTest {    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {        //这里指定的classLoader 为任意一个ClassLoader,通常用与接口相同的        Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);        System.out.println("-------------" + clazzProxy1.getName());  //-------------com.sun.proxy.$Proxy0        System.out.println("-------------Begin constructor list-----------");        Constructor[] constructors = clazzProxy1.getConstructors(); //返回所有的构造方法        for(Constructor constructor : constructors){            String name = constructor.getName();            StringBuilder sBuilder = new StringBuilder(name);            sBuilder.append('(');            Class[] clazzs = constructor.getParameterTypes();  //返回所有的参数列表类型            for(Class clazz: clazzs){                sBuilder.append(clazz.getName());                sBuilder.append(",");            }            if(clazzs != null && clazzs.length != 0){                sBuilder.deleteCharAt(sBuilder.length() - 1);  //去掉最后一个 ','            }            sBuilder.append(')');            System.out.println(sBuilder.toString());        }                System.out.println("-------------Begin method list-----------");        Method[] methods = clazzProxy1.getMethods();   //返回所有的方法        for(Method method : methods){            String name = method.getName();            StringBuilder sBuilder = new StringBuilder(name);            sBuilder.append('(');            Class[] clazzs = method.getParameterTypes();  //返回所有的参数列表类型            for(Class clazz: clazzs){                sBuilder.append(clazz.getName());                sBuilder.append(",");            }            if(clazzs != null && clazzs.length != 0){                sBuilder.deleteCharAt(sBuilder.length() - 1);  //去掉最后一个            }            sBuilder.append(')');            System.out.println(sBuilder.toString());        }                System.out.println("--------------begin create instance----------------");        //以下为创建代理对象        Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);        class MyInvocationHandler implements InvocationHandler{            @Override            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable {                return null;            }                    }        Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHandler());        System.out.println(proxy1); //null 这里为null是因为toString()返回为null        System.out.println(proxy1.toString());//null 这里为null 没有报错是因为调用了handler的toString(),handler的返回为null        proxy1.clear();   //调用没有返回值的就不会报错,无返回值为void invoke返回的是null 正确//        proxy1.size();  //如果collection为null, 调用有返回值的方法时会抛出异常 是由于调用handler的invoke方法,返回值为null,                //以上为一种方式,还可稍微简化下            Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){            @Override            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable {                // TODO Auto-generated method stub                return null;            }        });                            }}

 

 

  

    > InvocationHandler 对象的内部功能

System.out.println("--------------begin create instance----------------");        //以下为创建代理对象        Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);        class MyInvocationHandler implements InvocationHandler{            @Override            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable {                return null;            }                    }        Collection proxy1 = (Collection) constructor.newInstance(new MyInvocationHandler());        System.out.println(proxy1); //null 这里为null是因为toString()返回为null        System.out.println(proxy1.toString());//null 这里为null 没有报错是因为调用了handler的toString(),handler的返回为null        proxy1.clear();   //调用没有返回值的就不会报错,无返回值为void invoke返回的是null 正确//        proxy1.size();  //如果collection为null, 调用有返回值的方法时会抛出异常 是由于调用handler的invoke方法,返回值为null,                //以上为一种方式,还可稍微简化下            Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){            @Override            public Object invoke(Object proxy, Method method, Object[] args)                    throws Throwable {                // TODO Auto-generated method stub                return null;            }        });                //可以使用Proxy.newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) 更为简单 Collection proxy3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() { ArrayList target = new ArrayList
(); //为代理类挂上目标类,代理类需为前边接口的实现类 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 如果目标类出现在这,则proxy每次调用方法时都会调用invoke() 每次都会是一个全新的ArrayList对象 * 即后边的size() 总是0 */// ArrayList target = new ArrayList
(); //为代理类挂上目标类,代理类需为前边接口的实现类 long startTime = System.currentTimeMillis(); Object obj = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time " + (endTime-startTime)); return obj; } }); proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size()); /** * 在执行proxy3.getClass().getName() * 返回的是com.sun.proxy.$Proxy0,调用的是代理的不是目标的getClass() * 是由于 getClass() 是继承的Object的方法,其中Object中只有 hashCode,equals,toString这三个方法会委托给handler去调用 * 其余的Object中的其他方法 proxy自己有实现,不交给handler * java API: * If a proxy interface contains a method with the same name and parameter signature * as the hashCode, equals, or toString methods of java.lang.Object, when such a method is invoked on a proxy instance, * the Method object passed to the invocation handler will have java.lang.Object as its declaring class. * In other words, the public, non-final methods of java.lang.Object logically precede all of the proxy interfaces for the determination of which Method object to pass to the invocation handler. */ System.out.println(proxy3.getClass().getName());//com.sun.proxy.$Proxy0

 

 

 总结分许动态代理类的设计原理与结构

  > InvocationHandler 对象的运行原理

    Client程序调用objProxy.add("abc")方法时,涉及三个要素:objProxy对象、add方法、"abc"参数

    Class Proxy${

      add(Object object){

        return handler.invoke(Object proxy, Method method, Object[] args);

      }

    }

  动态代理的工作原理图

    

   这里的log() 相当于一个系统功能,这个不应该一直是一个功能,而应该配置成为动态变化的,即可动态设置的系统功能。

 

   > 面向切面编程:

    把切面的代码,以对象的形式进行封装,以对象的形式传递给你,你只要执行这个对象,就等于执行了切面的代码

   

   > 编写可生成代理和插入通告的通用方法

    

Collection proxy3 = (Collection)getProxy(new ArrayList<>(),new MyAdvice());        //每调用一次,都会调用 一次InvocationHandler 的 invoke方法        proxy3.add("zxx");          proxy3.add("lhm");        proxy3.add("bxd");        System.out.println(proxy3.size());
getProxy方法
private static Object getProxy(final Object target, final MyAdvice advice) {        Object proxy3 =  Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(), new InvocationHandler() {                    @Override                    public Object invoke(Object proxy, Method method,                            Object[] args) throws Throwable {                        advice.beforeMethod(method);                        Object object = method.invoke(target, args);                        advice.afterMethod(method);                        return object;                    }                });        return proxy3;    }

 

MyAdvice类:

public class MyAdvice implements Advice {    long startTime = 0;    @Override    public void beforeMethod(Method method) {        startTime = System.currentTimeMillis();            }    @Override    public void afterMethod(Method method) {        long endTime = System.currentTimeMillis();        System.out.println(method.getName()                + " running time of " + (endTime - startTime));    }}

 

  目的是把 代理 和系统功能都转义成了外边的 对象

  完成Spring 只完成一件事:写MyAdvice, target是在配置文件中配置的

 

实现类似Spring的可配置的AOP框架

  第一个类 FactoryBean bean工厂用于产生bean

public class BeanFactory {    Properties properties = new Properties();    public BeanFactory(InputStream is) {        try {            properties.load(is);        } catch (IOException e) {            e.printStackTrace();        }    }    public Object getBean(String name) throws Exception { //根据传入的名字判断为javaBean还是代理        String className = properties.getProperty(name);        Object bean = null;        Class clazz = Class.forName(className);        bean = clazz.newInstance();        if (bean instanceof ProxyFactoryBean) {  //如果为一个代理对象,产生响应的代理bean            ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;            Advice advice = (Advice) Class.forName(properties.getProperty(name+".advice")).newInstance();            Object target = Class.forName(properties.getProperty(name+".target")).newInstance();            proxyFactoryBean.setAdvice(advice);            proxyFactoryBean.setTarget(target);            Object proxy = proxyFactoryBean.getProxy();            return proxy;        }        return bean;  //直接返回普通javaBean    }}

 

  ProxyFactoryBean.java 产生代理的类 要求有两个参数 目标类target 以及Advice

public class ProxyFactoryBean {    private Object target;    private Advice advice;    /**     * @return the target     */    public Object getTarget() {        return target;    }    /**     * @param target     *            the target to set     */    public void setTarget(Object target) {        this.target = target;    }    /**     * @return the advice     */    public Advice getAdvice() {        return advice;    }    /**     * @param advice     *            the advice to set     */    public void setAdvice(Advice advice) {        this.advice = advice;    }    public Object getProxy() {        Object proxy3 = Proxy.newProxyInstance(target.getClass()                .getClassLoader(), target.getClass().getInterfaces(),                new InvocationHandler() {                    @Override                    public Object invoke(Object proxy, Method method,                            Object[] args) throws Throwable {                        /**                         * 如果目标类出现在这,则proxy每次调用方法时都会调用invoke()                         * 每次都会是一个全新的ArrayList对象 即后边的size() 总是0                         */                        // ArrayList target = new ArrayList
(); // //为代理类挂上目标类,代理类需为前边接口的实现类 advice.beforeMethod(method); Object obj = method.invoke(target, args); advice.afterMethod(method); return obj; } }); return proxy3; }} 

  存放配置的配置文件 config.properties

xxx=java.util.ArrayList#xxx=com.java.javaenhance1.aopFrameWork.ProxyFactoryBeanxxx.advice=com.java.javaenhance1.MyAdivcexxx.target=java.util.ArrayList

 

  测试类:

  

public class AopFrameWorkTest {    public static void main(String[] args) throws Exception {        InputStream is = AopFrameWorkTest.class.getResourceAsStream("config.properties");        Object bean = new BeanFactory(is).getBean("xxx");        System.out.println(bean.getClass().getName());    }}

 

以上的例子实现了Spring的两大核心 IOC(Bean工厂) 及AOP(动态代理)

转载于:https://www.cnblogs.com/wq3435/p/6014586.html

转载地址:https://blog.csdn.net/ankan7400/article/details/102088964 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:java高新技术-类加载器
下一篇:了解和入门注解的应用

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年03月15日 20时37分47秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

html生成jsessionid,H5 APP 使用 JSESSIONID 保持会话登录 2019-04-21
大数据可视化陈为智慧树_知到智慧树大数据可视化网课答案 2019-04-21
前端背景图放置_web前端入门到实战:css 中的背景图片小技巧和存在的坑 2019-04-21
wordpress账号无法登陆_苦闷两个月,wordpress后台不能登陆的问题终于解决了! 2019-04-21
java option作用_java – 类Option [T]的意义是什么? 2019-04-21
php 整形 字符串排序,php-通过特定的字符串值进行排序 2019-04-21
每个java程序都至少有一个线程给主线程,java程序在主线程中判断各个子线程状态的操作,该如何解决... 2019-04-21
lotus php,LotusPhp框架目录_PHP教程 2019-04-21
java倒计时自动关闭弹窗,打开页面弹出窗口子窗口定时自动关闭 2019-04-21
mysql 常见存储过程,MYSQL存储过程 2019-04-21
php+jq+添加css,jquery如何添加css样式? 2019-04-21
matlab 函数 向量参数,Scipy integrate(quad,quadration,nquad)不能集成向量参数化函数?等效函数(MATLAB works)... 2019-04-21
arduino如何调用mysql,【 实测可用 】Arduino 直接访问 mysql 2019-04-21
php数据库结构对比 微擎,禾匠数据库对比–微擎通用各类数据库结构对比教程... 2019-04-21
mitproxy php,orion-c 2019-04-21
oracle外部表ora29913,从外部表中选择sqlplus错误:ORA-29913:执行ODCIEXTTABLEOPEN标注时出错... 2019-04-21
oracle负载均衡方案,Oracle负载均衡配置代码 2019-04-21
html语言放到数据库中,怎样把输入的文本转换成html代码存入数据库啊 2019-04-21
html描述列表在线实例,HTML的列表标签 2019-04-21
mysql 允许网络连接_MySQL 权限问题 允许所有网络的连接 2019-04-21