代理模式

代理提供了对目标对象的另外的访问方式,即通过代理对象访问目标对象,这样可以扩展目标对象的功能。

java有三种代理模式,分别为静态代理、动态代理、cglib代理

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象

1、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类

接口

public interface IUserDao {

    public void add();
}

目标对象

public class UserDao implements IUserDao {

    @Override
     public void add() {
         System.out.println("添加方法");
     }

}

代理对象

public class UserProxy implements IUserDao{

    private IUserDao userDao;
    
     public UserProxy(IUserDao userDao) {
         super();
         this.userDao = userDao;
     }

    @Override
     public void add() {
         System.out.println("开始事务");
         userDao.add();//执行目标对象的方法
         System.out.println("提交事务");
     }

}

测试类

public static void main(String[] args) {
         User user = new User();
         //目标对象
         UserDao userDao = new UserDao();
         //把目标对象传给代理对象
         UserProxy proxy = new UserProxy(userDao);
         //执行的是代理的方法
         proxy.add();
        
     }

静态代理优缺点:

优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.

缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护

要克服静态代理的缺点就需要用到动态代理


2、动态代理

接口和实现类同上静态代理

动态代理需要增加一个代理工厂ProxyFactory

public class ProxyFactory {
    
     private Object target;

    public ProxyFactory(Object target) {
         super();
         this.target = target;
     }
    
     public Object getProxyInstance() {
         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            
             @Override
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 //可以扩展方法
                 System.out.println("开始事务");
                 //执行目标对象方法
                 Object invoke = method.invoke(target, args);
                 System.out.println("提交事务");
                 return invoke;
             }
         });
                
     }
}

测试类

public static void main(String[] args) {
     UserDao userDao = new UserDao();
     //给目标对象生成代理对象,注意此处用的是接口而不是实现类,否则会报转换错误
     IUserDao proxy =(IUserDao) new ProxyFactory(userDao).getProxyInstance();
    
     User user = new User();
    
     //代理对象执行方法
     proxy.add();
    
}

动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等


3、cglib代理

cglib代理也叫子类代理,静态代理和动态代理都需要目标对象实现一个接口,但是实际情况可能是目标函数没有实现接口,此时就可以用cglib代理,它可以在内存中构建一个子类对象来对目标对象的函数来进行扩展

目标对象

public class CglibDao {

    public void save() {
         System.out.println("执行保存方法");
     }
}

创建一个代理工厂,需要引入一个cglib的依赖

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

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 ProxyFactory2 implements MethodInterceptor{

    private Object target;
    
     public ProxyFactory2(Object target) {
         super();
         this.target = target;
     }
     public Object getProxyInstance(){
         //工具类
         Enhancer en = new Enhancer();
         //设置父类
         en.setSuperclass(target.getClass());
         //设置回调函数
         en.setCallback(this);
         //创建子类(代理对象)
         return en.create();
     }
     @Override
     public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
        
         System.out.println("开始事务");
         //执行目标类方法,此处的的invoke的第一个参数为target而不是代理对象obj,否则会不断递归调用intercept方法而导致内存溢出
         Object invoke = method.invoke(target, arg2);
         System.out.println("提交事务");
         return invoke;
     }
}


测试类

public static void main(String[] args) {
     CglibDao cglibDao = new CglibDao();
     //给目标对象生成代理对象
     CglibDao proxy =(CglibDao) new ProxyFactory2(cglibDao).getProxyInstance();
     //代理对象执行方法
     proxy.save();
}

cglib也有局限,它也跳不出jdk的继承机制,对final类无能为力

posted @ 2018-02-02 14:46  夏夜弥光  阅读(250)  评论(0编辑  收藏  举报