zoukankan      html  css  js  c++  java
  • 代理模式

    1,代理模式

     通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理

    例如:A和B是朋友,B和C是朋友,A想通过B 认识C,并且买了花,买了礼物,委托B转送给C,这里B就是A的代理,A就是B代理对象

    A和B都得完成送花,送礼物的动作,A 是实际的发起者,B是代理完成者

    2,代理运用场景

    SpringAOP:面向切面编程,在不改变原先代码的情况下,添加新的功能

    日志打印:面向切面编程里面也有这部分功能的实现,添加日志的打印,方法前,方法后,之前的代码里若没有这方面功能

    事务功能:方法调用前,方法调用后

    3,静态代理

    由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

    public interface IUserDao {
        
        void addUser();
        
        void delete();
    
    }
    public class UserDaoImpl implements IUserDao {
    
        @Override
        public void addUser() {
            System.out.println("add。。。");
        }
    
        @Override
        public void delete() {
            System.out.println("delete。。。");
        }
    
    }
    public class UserDaoProxy implements IUserDao{
        
        //定义代理对象
        private IUserDao target;
        
        //构造方法传入代理对象
        
        public UserDaoProxy(IUserDao userDaoImpl){
            this.target = userDaoImpl;
        }
        
        //代理替代理对象完成的动作
        @Override
        public void addUser() {
            System.out.println("开启事务");
            target.addUser(); //还是之前的实现
            System.out.println("关闭事务");
        }
    
        @Override
        public void delete() {
            System.out.println("开启事务");
            target.delete(); //还是之前的实现
            System.out.println("关闭事务");
        }
    
    }
    public class Client {
        public static void main(String[] args) {
            IUserDao userDaoImpl = new UserDaoImpl();
            IUserDao userDao = new UserDaoProxy(userDaoImpl);
            userDao.addUser();
            userDao.delete();
        }
    
    }

    在 UserDaoImpl 的方法里面没有事务的功能,或者日志的功能,现在在不改变原先代码的情况下,添加一个代理类UserDaoProxy,并且将UserDaoImpl 委托给UserDaoProxy,在UserDaoProxy里面添加日志的功能,实际方法的

    调用还是之前的UserDaoImpl 的实现,UserDaoProxy 只是添加了额外的一些需求(日志,事务...)

    静态代理的弊端:

    1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

    2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserDaoImpl 类的访问提供了代理,但是如果还要为其他类如LoginImpl类提供代理的话,就需要我们再次添加代理LoginImpl的代理类

    4,jdk 动态代理

    静态代理,代理类是程序员自己写的,程序运行前代理类的字节码文件已经存在了(编译之后)

    动态代理,动态生成的,程序运行过程中动态的生成的代理类

    JDK 动态代理,代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

    1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)

    2)实现方式:

    1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);

    2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

    3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口

    public interface IUserDao {
        
        void addUser();
        
        void delete();
    
    }
    public class UserDaoImpl implements IUserDao {
    
        @Override
        public void addUser() {
            System.out.println("add。。。");
        }
    
        @Override
        public void delete() {
            System.out.println("delete。。。");
        }
    
    }

    实现InvocationHandler 接口

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    //动态代理类
    public class InvocationHandlerImpl implements InvocationHandler {
    
        private Object target;
    
        public InvocationHandlerImpl(Object target) { // 传入的代理对象
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object result = null;
    
            System.out.println("开始调用");
    
            result = method.invoke(target, args);
    
            System.out.println("结束调用");
    
            return result;
        }
    
    }

    java.lang.reflect 下的Proxy 对象,通过newInstance()生成代理类对象,传入目标代理的对象的classLoader,interfaces,已经实现JDK invocationHandler的实现类invocationHandlerImpl(里面有日志等额外信息。。。)

    import java.lang.reflect.Proxy;
    
    public class Client {
        public static void main(String[] args) {
            
             IUserDao userDaoImpl = new UserDaoImpl();
             InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDaoImpl);
             ClassLoader classLoader = userDaoImpl.getClass().getClassLoader();
             Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces();
            // 主要装载器、一组接口及调用处理动态代理实例
             IUserDao newProxyInstance = (IUserDao)Proxy.newProxyInstance(classLoader, interfaces, invocationHandlerImpl);
             newProxyInstance.addUser();
             newProxyInstance.delete();
        }
    
    }

    5,cglib 动态代理

    使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码

    maven:

    <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib</artifactId>
       <version>3.1</version>
    </dependency>

    asm的jar 包 依赖cglib

    没有接口,类里面有两个方法

    public class UserDaoImpl{
    
        public void addUser() {
            System.out.println("add。。。");
        }
    
        public void delete() {
            System.out.println("delete。。。");
        }
    
    }

    cglib 代理类

    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    // 动态的代理类
    public class CglibProxy implements MethodInterceptor {
    
        private Object targetObj;
    
        // 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理 绑定了代理类和目标代理对象之间的关系
        public Object getInstance(Object target) {
            // 设置需要创建子类的类
            this.targetObj = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());//代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
    //这个重写的方法就是代理的具体是实现 @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开启事物"); Object result = proxy.invoke(targetObj, args); System.out.println("关闭事物"); // 返回代理对象 return result; } }

    Client

    public class Client {
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
            UserDaoImpl instance = (UserDaoImpl)cglibProxy.getInstance(new UserDaoImpl());
            instance.addUser();
            instance.delete();
    
        }
    
    }

    CGLIB动态代理与JDK动态区别

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    Spring中。

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

    2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
    因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

  • 相关阅读:
    POJ 1703 Find them, Catch them
    POJ 2236 Wireless Network
    POJ 2010 Moo University
    POJ 2184 Cow Exhibition
    POJ 3280 Cheapest Palindrome
    POJ 3009 Curling 2.0
    POJ 3669 Meteor Shower
    POJ 2718 Smallest Difference
    POJ 3187 Backward Digit Sums
    POJ 3050 Hopscotch
  • 原文地址:https://www.cnblogs.com/pickKnow/p/11096461.html
Copyright © 2011-2022 走看看