JAVA动态代理解析

主要目标理解JDK动态代理
1、为什么要使用代理
   实际程序中,有许多程序的共同操作和实际业务没有关系,但是我们有不得不每次把这些相同的代码给他加上去
   显然我们可以把这样的操作当成一个切面,在执行方法之前和之后去调用这样一段特殊的代码,从而实现需要的
   功能。
   *日志处理:比如操作日志可能只是需要保存执行的方法、时间等信息,所以这样的功能应当被当做成一个切面
            在执行方法的时候或者执行完之后加上对该方法的执行日志
   *权限处理:只执行某个方法的时候,可能需要检查当前用户是否有权限执行改操作,可以把权限检测当当成一个
             切面,在需要的时候执行权限检测
   *特俗情况等处理:在执行一些方法的时候,可能需要做某些特定的测试操作,而且经常改动,当时实际业务确
                 是固定的,这时候把特定操作当成一个切面,从而当需要修改的时候,也仅仅是只需要修改一
               个地方而已,不是每个方法都去做改变
2、JDK的动态代理实现及原理(实际例子:操作日志记录)
   2.1:正对一个业务类UserServiceImpl创建代理对象,并且生成操作日志
   UserService 类如下:
   public class UserServiceImpl implements UserService{
       public void add() {
           System.out.println("add user");
       }
      
       public void delete() {
           System.out.println("deleteuser");
       }
      
       public void update() {
           System.out.println("update user");
       }
      
       public void find() {
           System.out.println("find user");
       }
   }
  
   public interface UserService {
public abstract void add();

public abstract void delete();

public abstract void update();

public abstract void find();
}
2.2、从上面可以看到要生成代理对象使用JDK自带的功能,必须实现接口,然而在spring这样的框架里面
     可以发现,即时不实现接口也能生成代理对象,那样不实现接口生成代理对象的方式和JDK这样的生成
     实现方式是不一样的,spring是借助了cglib去生成,jdk自带和cglib生成区别稍后在分析
    2.3、JDK自带的实现方式,主要是利用java.lang.reflect.Proxy类
                                 java.lang.reflect.InvocationHandler接口实现
         2.3.1、Proxy类
           构造方法一个
           Proxy(InvocationHandler h):可见要创建Proxy类去创建代理对象,必须给Proxy传递实现InvocationHandler接口的类
          方法
           getInvocationHandler(Object proxy):返回某个代理类所关联的实现InvocationHandler类的对象
           getProxyClass(ClassLoader loader, Class<?>... interfaces) :返回要代理类的class对象。
                   获取了代理对象的class实例,就可以获取代理对象的构造方法,new出一个实例,当然想通过这样的方式new,也是需要给他传递一个InvocationHandler接口的实现类的
           isProxyClass(Class<?> cl):cl类是否是代理类,是返回true,不是返回false
           newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) :直接返回一个代理类的对象,通过上面方法getProxyClass能获取class
              也可以生成代理对象,可见newProxyInstance只是getProxyClass的封装版,提供给我们一个更加容易操作的方法。
    2.4、代理工厂,代理使用的地方可能有很多,这里把他封装起来,做成单例模式的实现
         public class ProxyFactory implements InvocationHandler{
             private static ProxyFactory proxyFactory;
             private Object target;
            
             private ProxyFactory() {};
             public static ProxyFactory getInstance() {
                 if(proxyFactory == null) {
                     return new ProxyFactory();
                 }else {
                     return proxyFactory;
                 }
             }
            
             public Object getProxy(Object target) {
                 this.target = target;
         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
             }
            
             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//记录操作日志
System.out.println("执行方法:" + method.getName());
System.out.println("执行时间:" + new Date());
return method.invoke(target, args);
}
         }
    2.5、测试
        public class Test {
            public static void main(String[] args) {
            UserService service = (UserService)ProxyFactory.getInstance().getProxy(new UserServiceImpl());
service.add();
System.out.println(Proxy.isProxyClass(service.getClass()));
            }
        }
        打印结果:
         执行时间:Fri May 13 11:50:55 CST 2011
add user
true
    2.6、执行过程分析
         首先执行Test类的main方法
        ProxyFactory.getInstance()获取代理工厂ProxyFactory对象
        getProxy():方法传递实际需要代理的对象过去,ProxyFactory保存实际代理对象用来后面实际调用
        Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)
         生成代理对象,在调用的时候,代理对象调用方法的时候,它和实际对象拥有一样的方法和功能,当代理对象调用方法的时候,会去执行
        invoke方法,执行完之后,Invoke方法同时也有真实调用的方法和参数,我们就利用代理保存的真实对象,去调用返回结果
3、JDK代理为什么一定得实现接口和原理
   首先JDK里面实现是利用传入的接口去生成对应的代理类,所生成的代理类是和需要被代理的类实现的接口一样,从而他们也就
   拥有相同的行为,如果传入的接口为空,直接是不进行生成代理对象的,装载器,生成的代理对象也是利用被代理类的装载器
   去加载进来的。
  
   在getProxyClass:首先会把所以传递过来的接口集合分析,加载,如果加载过程中出现错误,抛出错误,如果没有错误
                   把接口的名称组成集合,把接口的class保存起来,已经被生成的代理,会直接返回,没有被生成的就
                   先生成在缓存起来.
   最后通过获取记载传入的接口获取其字节码文件,进行加载,生成出的代理类是如下结构
   public class $Proxy extends Proxy implements 传入的接口
4、CGLIB是通过字节码的方式生成一个代理类的子类,所以在应用的时候
   可以直接把代理类强制转化为具体的实现子类。而JDK动态代理则不可以,
   他不是具体的子类,它只是实现接口的类,所以只能转化为指向父引用
   的接口对象。
       
       
       
       
          
          
          
          
  

你可能感兴趣的:(java,spring,jdk,框架)