• 通过实例理解Spring的Bean工厂和AOP框架


    一、需求设想      

           现在我有一个配置文件,里面配置了Bean的相关信息,如bean的类名(包括包名)、代理工厂(主要负责产生代理类)、目标类(被代理的类)、业务织入接口(Advice)。然后通过BeanFactory来产生Bean的实例,如果配置文件中配置的Bean是ProxyFactoryBean的实例,我们则产生这个Bean一个代理类的实例,还可以通过此配置文件进行切换,是使用代理类还是使用目标类来完成相应的业务功能,该配置文件的格式如下:


                                                                                                                        图1-1

    图1-1中所示,beanName就是我们要通过Bean工厂动态产生的实例或代理类实例,如果配置文件中的beanName指定的是ProxyFactoryBean,则获得的实例则是根据beanName.target对应的类名,由ProxyFactoryBean产生一个代理类的实例,并由beanName.advice对应的Advice织入相应的业务功能到该目标类中。

    二、功能实现

    1、步聚:
           BeanFactory即然是专门用来产生Bean的,那就必须得知道产生这些个Bean的配置文件在哪里?这个配置文件事是先由程序员配置好的。所以在初始化BeanFactory的时候,就必要要得到产生这些Bean的配置文件。
                1)、创建BeanFactory的一个唯一的构造方法,接收一个InputStream参数,这个参数用于获得Bean的配置文件。初始化的时候,通过Properties对象加载这个配置文件。
                2)、创建一个getBean方法,接收一个参数(类名),用于根据beanName动态创建该类的一个实例。该beanName就是配置文件中的beanName。
                3)、使用Class.forName方法根据beanName产生一个Class字节码对象,并产生一个实例对象
                4)、如果该实例对象是ProxyFactoryBean的实例,则返回由ProxyFactoryBean产生的一个代理类实例,并注入配置文件中beanName.advice对应的Advice,否则直接返回该实例。

    2、代码实例
    1)、BeanFactory类
    package proxy.aopframework;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    import proxy.Advice;
    
    /**
     * Bean工厂,负责产生代理类或目标类的实例
     */
    public class BeanFactory<T> {
    	
    	private Properties props = new Properties();
    	
    	/**
    	 * 初始化bean工厂,加载bean的配置文件,该配置文件是一个标准的Properties文件,该文件由Key=Value的格式组成
    	 * @param inStream bean的配置文件
    	 */
    	public BeanFactory(InputStream inStream) {
    		try {
    			props.load(inStream);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 获取一个Bean的实例
    	 * @param beanName bean javabean的名称
    	 * @return 根据配置文件,返回目标类或代理类的实例
    	 * @throws ClassNotFoundException
    	 */
    	public T getBean(String beanName) throws ClassNotFoundException {
    		String className = props.getProperty(beanName);
    		Class clazz = Class.forName(className);
    		T bean = null;
    		try {
    			bean = (T)clazz.newInstance();
    			if (bean instanceof ProxyFactoryBean) {
    				ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
    				Advice advice = (Advice) Class.forName(props.getProperty(beanName + ".advice")).newInstance();
    				Object target = Class.forName(props.getProperty(beanName + ".target")).newInstance();
    				proxyFactoryBean.setAdvice(advice);
    				proxyFactoryBean.setTarget(target);
    				bean = (T)proxyFactoryBean.getProxy();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		} 
    		return bean;
    	}
    }
    2)、ProxyFactoryBean类
    package proxy.aopframework;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import proxy.Advice;
    
    /**
     * 代理工厂,负责产生目标对象的代理类
     */
    public class ProxyFactoryBean {
    	
    	/**
    	 * 被代理的目标对象
    	 */
    	private Object target;
    	
    	/**
    	 * 目标对象要插入的业务逻辑
    	 */
    	private Advice advice;
    
    	public Object getTarget() {
    		return target;
    	}
    
    	public void setTarget(Object target) {
    		this.target = target;
    	}
    
    	public Advice getAdvice() {
    		return advice;
    	}
    
    	public void setAdvice(Advice advice) {
    		this.advice = advice;
    	}
    
    	/**
    	 * 获得一个代理类对象
    	 * @return
    	 */
    	public Object getProxy() {
    		return Proxy.newProxyInstance(
    				target.getClass().getClassLoader(), 
    				target.getClass().getInterfaces(),
    				new InvocationHandler() {
    					@Override
    					public Object invoke(Object proxy, Method method,
    							Object[] args) throws Throwable {
    						Object retVal = null;
    						try {
    							advice.doBefore(target, method, args);
    							retVal = method.invoke(target, args);
    							advice.doAfter(target, method, args, retVal);
    						} catch (Exception e) {
    							advice.doThrow(target, method, args, e);
    						} finally {
    							advice.doFinally(target, method, args);
    						}
    						return retVal;
    					}}
    				);
    	}
    }
    3)、Advice接口和LogAdvice实现类
    package proxy;
    
    import java.lang.reflect.Method;
    
    /**
     * aop接口,提供方法运行前、方法运行后、方法运行中产生Exception、方法最终运行代码
     *
     */
    public interface Advice {
    	
    	/**
    	 * 方法运行前
    	 * @param target 被代理的目标对象
    	 * @param method 被调用的方法
    	 * @param args 方法的参数
    	 */
    	public void doBefore(Object target, Method method, Object[] args);
    	
    	/**
    	 * 方法运行后
    	 * @param target 被代理的目标对象
    	 * @param method 被调用的方法对象
    	 * @param args 方法的参数
    	 * @param retVal 方法的返回值
    	 */
    	public void doAfter(Object target, Method method, Object[] args, Object retVal);
    	
    	/**
    	 * 方法运行时产生的异常
    	 * @param target 被代理的目标对象
    	 * @param method 被调用的方法
    	 * @param args 方法参数
    	 * @param e 运行时的异常对象
    	 */
    	public void doThrow(Object target, Method method, Object[] args, Exception e);
    	
    	/**
    	 * 最终要执行的功能(如释放数据库连接的资源、关闭IO流等)
    	 * @param target 被代理的目标对象
    	 * @param method 被调用的方法
    	 * @param args 方法参数
    	 */
    	public void doFinally(Object target, Method method, Object[] args);
    }
    
    
    
    package proxy;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    /**
     * 日志功能切入类
     * @author 杨信
     *
     */
    public class LogAdvice implements Advice {
    
    	long beginTime = System.currentTimeMillis();
    	
    	@Override
    	public void doBefore(Object target, Method method, Object[] args) {
    		System.out.println(target.getClass().getSimpleName() +
    				"." + method.getName() + "方法被调用,参数值:" + Arrays.toString(args));
    
    	}
    	
    	@Override
    	public void doAfter(Object target, Method method, Object[] args, Object retVal) {
    		long endTime = System.currentTimeMillis();
    		System.out.println(target.getClass().getSimpleName() +
    				"." + method.getName() + "方法运行结束,返回值:" + retVal + ",耗时" + (endTime - beginTime) + "毫秒。");
    
    	}
    
    	@Override
    	public void doThrow(Object target, Method method, Object[] args,
    			Exception e) {
    		System.out.println("调用" + target.getClass().getSimpleName() +
    				"." + method.getName() + "方法发生异常,异常消息:");
    		e.printStackTrace();
    	}
    
    	@Override
    	public void doFinally(Object target, Method method, Object[] args) {
    		System.out.println("doFinally...");
    		
    	}
    
    }
    4)、config.properties文件
    #beanName=java.util.ArrayList
    beanName=proxy.aopframework.ProxyFactoryBean
    beanName.advice=proxy.LogAdvice
    beanName.target=java.util.ArrayList
    
    #hashMap=java.util.HashMap
    hashMap=proxy.aopframework.ProxyFactoryBean
    hashMap.advice=proxy.LogAdvice
    hashMap.target=java.util.HashMap
    5)、测试类AopFrameworkTest
    package proxy.aopframework;
    
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    
    public class AopFrameworkTest {
    
    	public static void main(String[] args) throws ClassNotFoundException {
    		InputStream inStream = AopFrameworkTest.class.getResourceAsStream("config.properties");
    		/*BeanFactory<ArrayList> beanFactory = new BeanFactory<ArrayList>(inStream);
    		List list = beanFactory.getBean("ArrayList");
    		System.out.println(list.getClass().getName());
    		list.add("zhangsan");*/
    		
    		BeanFactory<HashMap> beanFactory = new BeanFactory<HashMap>(inStream);
    		Map map = beanFactory.getBean("hashMap");
    		System.out.println(map.getClass().getName());
    		map.put("name", "zhangsan");
    		System.out.println(map.size());
    	}
    
    }
    6)、测试结果

  • 相关阅读:
    BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第13章节--使用业务连接服务创建业务线解决方式 创建启用BCS的业务解决方式
    POI 导入excel数据自己主动封装成model对象--代码分析
    四旋翼飞行器Quadrotor飞控之 PID调节(參考APM程序)
    Detours改动段属性漏洞
    C++中父类的虚函数必需要实现吗?
    深入理解JavaScript系列(12):变量对象(Variable Object)
    CSS 类、伪类和伪元素差别具体解释
    Qt Quick 之 PathView 具体解释
    读《一年一度屈原祭,端午时节话公知》有感
    Volley简单学习使用五—— 源代码分析三
  • 原文地址:https://www.cnblogs.com/xyang0917/p/4172534.html
Copyright © 2020-2023  润新知