黑马程序员--java之动态代理

来源:互联网 发布:淘宝男朋友 编辑:程序博客网 时间:2024/05/08 13:54

-------android培训,java培训期待与您交流--------

java除了面向对象编程,还有一种新的编程思想,就叫面向切面编程。也就是说,在编程中有一些相同的代码需要在很多类或者方法里面,而这时候为了增加程序的松耦合,我们就将这些公用代码抽取出来,作为一个类,在每个需要的地方加入就可以了。也可以理解为这些抽取出来的代码为一个切面,需要它加入的地方就是一个切入点。这样做有利于我们对程序的维护,简化了代码。

    动态代理技术Proxy正是利用了这一技术,来实现了多个具有相同接口的类为其每个方法加入一些公共功能,如,日志,异常处理等。下面来看一段这样的代码,来更直观的理解这个动态代理。

    这是自己做的一个小框架,实现了一个在方法调用前后打印通知的功能

首先,我们来看看

//代理工厂
public class ProxyBeanFactory {

    public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
//通知接口
private Advice advice;
//目标对象
    private Object target;
    
    //获取代理对象
    public Object getProxy(){
    //根据target对象的类加载器,接口集合,以及实现一个InvocationHandler接口的内部类
    Object proxy = 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 obj = method.invoke(target, args);
       advice.afterMethod(method);
return obj;
}
});
    //返回得到的代理对象
    return proxy;
    }
}

有了代理工厂,再创建一个BeanFactory我们就可以实现类似Spring那样的模式,将对象一层层依赖注入的模式配置然后通过代码的工厂类实现调用,下面是BeanFactory的代码

//Bean工厂
public class BeanFactory {
//属性配置文件
    private Properties pro =null;


public Properties getPro() {
return pro;
}


public void setPro(Properties pro) {
this.pro = pro;
}
//初始化构造函数并传入输入流对象加载配置文件
    public BeanFactory(InputStream input){
    try {
pro = new Properties();
pro.load(input);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
    //根据传入的名称,获得对应的代理类对象
    public Object getBean(String name){
    String className = pro.getProperty(name);
    Object bean = null;
    try {
    //获取配置文件配置的对象并实例化
bean = Class.forName(className).newInstance();
//判断bean对象是否是代理工厂的实例化对象否则直接返回bean对象
if(bean instanceof ProxyBeanFactory){
//获取配置文件的通知和目标属性,并实例化其对象
ProxyBeanFactory proBean = (ProxyBeanFactory) bean;
Advice advice = (Advice)Class.forName(pro.getProperty(name+".advice")).newInstance();
Object target = Class.forName(pro.getProperty(name+".target")).newInstance();
//将目标对象和通知对象设置进入代理工厂
proBean.setAdvice(advice);
proBean.setTarget(target);
//调用代理工厂的获取代理对象方法获得代理对象
Object proxy = proBean.getProxy();
return proxy;
}
} catch (Exception e) {
e.printStackTrace();
}
return bean;
    }
}

下面是配置文件的信息

test=test.ProxyBeanFactory
test.advice=test.MyAdvice
test.target=java.util.ArrayList

这样一个小小的代理就做好了。

public static void main(String[] args) throws SecurityException, NoSuchMethodException {
      InputStream input = TestMyFrame.class.getClassLoader().getResourceAsStream("config.properties");
      Object bean = new BeanFactory(input).getBean("test");
      ((Collection)bean).clear();
    } 


输出结果

clear方法开始执行开始。
//clear()方法真正的运行
clear方法开始执行完毕。

这样就实现了对Collection集合的子类在每个方法前添加一个通知的代理功能了。当然不光是集合,只要是具有相同接口的类,都可以通过动态代理的方式来实现对同一段需要加的代码实现一个封装并在切入点切入,这就是面向切面编程了(AOP),类似于Spring的声明式事务就是这样一个实现方式,当然,框架还有很多通过这样的方式实现的功能,基本上就是通过反射,与一个InvocationHandler代理实例的调用处理程序实现的接口。 



原创粉丝点击