黑马程序员_张孝祥_Java基础加强_代理

----------- android培训、java培训、期待与您交流! ------------

什么是代理:

 

从类关系上来讲,代理是组合与继承的中庸之道。

 

组合是将另一个类的对象作为成员属性存放在本类中,然后通过调用这个成员属性对象的方法来获得此类的功能。

继承是通过扩展一个类,复制其全部功能接口,让本类看起来is a 另一个类。

而代理的本质是,通过组合来达到部分继承的效果,即复制接口功能,这些接口功能是通过调用所代理的类的功能来实现的。

事实上,代理和继承的内部实现方式非常相像。

 

代理类与被代理类拥有某些共同的接口,代理类中的接口方法调用被代理类中的接口方法,除了可以达到和被代理类一样的功能外,还可以在方法前后添加其他系统代码,修改传递的参数,甚至修改返回值。

 

代理的好处:

1.为被代理类的方法添加额外的功能代码。

通过为一个类设置不同的代理,可以为此类中的方法添加不同的包围代码,以此来产生稍稍不同的效果。

 

2.让同一个代理类代理不同的实现类,动态修改实现方式。

因为可以为代理类指定不同的代理目标,而代理目标的实现方法是不同的。Client程序调用代理类的方法,并可以通过修改代理目标来可以让这个被调用的方法切换不同的实现方式甚至行为。

  

不同对象的代理类看起来都大同小异:

1.他们都包含了一个被代理目标类(Target)

2.他们与被代理类拥有者交叉功能

3.他们都可以在交叉功能的前、后、包围方式、catch块中添加额外代码。

 

需要被代理的类可能有很多,而代理又都遵从与这一固定的模式,这样可以通过设置框架来简化生成代理的操作。

 

动态代理:

 

事实上,JVM已经为我们提供了一个可以在运行期生成动态代理类的方式。

所用到的两个类:

 java.lang.reflect.Proxy

java.lang.InvocationHandler

 

//通过Proxy类来获取动态代理类的Class对象,他的类名为$Proxy0-N
Class proxyCls =Proxy.getProxyClass(AInterface.class.getClassLoader(), AInterface.class);
//  创建一个代理类的调用处理程序,这个程序需要实现InvocationHandler接口,并覆盖invoke方法。
// invoke方法用于接收代理方法的Method对象和参数列表数组,并在第一个参数指定使用的代理类对象。
//  于此同时,还要指定代理类的几大要素,被代理目标Target,添加代码Advice。
class MyInvocationHandler implementsInvocationHandler{
         privateAInterface target = ...;
         privateAdvice advice = ...;
         publicObject invoke(Object proxy, Method method, Object[] args)throws Throwable{
                   advice.beforeMethod(method,args);
                   ObjectreturnVal = method.invoke(target, args);
                   advice.afterMethod(method,args);
                   if(returnVal== null)
                            returnnull;
                   returnreturnVal;
         }
}
//获取代理类的构造函数,创建代理对象
AInterface proxy =(AInterface)proxyCls.getConstructor(InvocationHandler.class).newInstance(newMyInvocationHandler());

  

另一种简化创建代理对象的方法:

AInterface proxy = Proxy.newProxyInstance(
         AInterface.class.getClassLoader(),
         newClass[]{AInterface.class},
         newInvocationHandler(){
                   privateAInterface target = ...;
                   privateAdvice advice = ...;
                   publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable{
                            advice.beforeMethod(method,args);
                            ObjectreturnVal = method.invoke(target, args);
                            advice.afterMethod(method,args);
                            if(returnVal== null)
                                     returnnull;
                            returnreturnVal;
                   }
         }
);
interface Advice{
         voidbeforeMethod(Method method, Object[] args);
         voidafterMethod(Method method, Object[] args);
}

 

动态代理类的实现原理:

 

普通的代理关系是将被代理目标用组合方式作为成员属性存储在代理类中,然后调用方法时调用此成员的对应方法。

而JVM自动生成的动态代理类是将一个调用处理程序InvocationHandler组合进动态代理类中,将代理目标组合进InvocationHandler中,在调用方法时调用InvocationHandler中的invoke方法,然后由invoke方法调用被代理类中的对应方法,invoke方法还可能对调用过程进行处理,Advice对象也是组合进InvocationHandler对象中,而非代理类中。

 

实现代码大致如下:

class MyProxy implementsCollection{
         InvocationHandlerhandler;
         MyProxy(InvocationHandlerhandler){
                   this.handler= handler;
         }
         publicboolean add(E e){
                   return(boolean)handler.invoke(this, this.getClass().getMethod("add",T.getClass()), T.getClass());
         }
         ...
}

 

注意:

对于继承自Object类的方法,只有hashCode、equals、toString进行派发给被代理类,而其他的Object方法使用代理类自身的方法。

因此proxy.getClass().getName()返回的是代理类的名称而非被代理类的名称。

 

特别注意:

事实上,InvocationHandler中的invoke方法的返回值是Object,但是通过实验发现方法调用的实际返回值类型是正确的,这就说明代理类在返回方法的返回值时,进行了复杂的转换过程,这可能是通过反射来实现的。(我猜)

  

AOP(Aspect oriented program),面向方面编程。

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

 

交叉业务的编程问题即为面向方面的编程,AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的效果是一样的。

  

为各种接口的类编写代理类是一项非常枯燥且麻烦得事情,因此JVM为我们实现了这样的功能。

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

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

 

如果一个类没有实现接口,那么JVM就无法为其生成动态代理类。这是可以使用第三方类库CGLIB,它可以动态生成一个类的子类,而子类自然和父类拥有相同的接口。

  

实现类似spring的可配置的AOP框架:

import java.io.*;
import java.util.*;
import java.lang.reflect.*;
 
class AopFrameworkTest
{
         publicstatic void main(String[] args) throws Exception
         {
                   InputStreamin = AopFrameworkTest.class.getResourceAsStream("config.properties");
                   Speakablebean = (Speakable)new BeanFactory(in).getBean("xxx");
                   System.out.println(bean.speak());
         }
}
 
 
class ProxyFactoryBean
{
         privateObject target;
         privateAdvice advice;
         publicvoid setTarget(Object tar){
                   target= tar;
         }
         publicvoid setAdvice(Advice adv){
                   advice= adv;
         }
 
         publicObject getProxy()throws Exception{
                   returnProxy.newProxyInstance(
                            target.getClass().getClassLoader(),
                            target.getClass().getInterfaces(),
                            newInvocationHandler(){
                                     publicObject invoke(Object proxy, Method method, Object[] args)throws Throwable{
                                               advice.beforeMethod(method,args);
                                               ObjectreturnVal = method.invoke(target, args);
                                               advice.afterMethod(method,args);
                                               if(returnVal== null)
                                                        returnnull;
                                               returnreturnVal;
                                     }
                            });
         }
}
 
class BeanFactory
{
         privateProperties prop = new Properties();
         publicBeanFactory(InputStream in){
                   try
                   {
                            prop.load(in);
                   }
                   catch(IOException e)
                   {
                            thrownew RuntimeException("配置文件读取失败");
                  }
         }
         publicObject getBean(String name)throws Exception{
                   StringclsName = prop.getProperty(name);
                   Objectobj = Class.forName(clsName).newInstance();
                   if(clsName.equals("ProxyFactoryBean")){
                            ProxyFactoryBeanproxyFactory = (ProxyFactoryBean)obj;
                            proxyFactory.setTarget(Class.forName(prop.getProperty("target")).newInstance());
                            proxyFactory.setAdvice((Advice)Class.forName(prop.getProperty("advice")).newInstance());
                            returnproxyFactory.getProxy();
                   }
                   returnobj;
         }
}


----------- android培训、java培训、期待与您交流! ------------

你可能感兴趣的:(黑马自学课程)