基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】



动态代理 与AOP (Aspect Oriented Program)面向方面编程,OOP为面向对象



代理的结构

基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】

--------------------------------------------------------------


"动态代理类的设计原理与结构",框架结构


基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】

----------------------------

AOP核心--面向切面编程
需要解决的问题:

AOP核心--面向切面编程
"动态代理类的设计原理与结构"
	框架结构

需要解决的问题:
	如果目标类对象已经固化到代码中,没有办法由用户传入进行设置
	如果交叉业务(系统功能)也同样被固化到代码中,无法由用户自由配置。
解决方式:
	定义框架功能,将需要动态加载的对象以参数的形式传入
	将目标类对象移动到匿名局部类newInvocationHandler(){}的外部进行配置
	将系统功能 (交叉业务) 通过参数传入InvocationHandler的局部匿名类

【注意】局部类只能访问外部final变量,所以目标对象外置后要加 final 延长生命周期
//系统功能应该有四种执行位置,异常中,方法前,方法后,方法前后
	Spring将系统功能看做是用户对代码的建议,所以接口命名成Advice
	Spring规定封装业务代码的接口的方法应该传入目标类对象、目标类对象调用的方法和目标类对象调用的方法对应的参数

"分析动态代理的工作原理"
怎样将目标类传进去?
	直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
	为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
	让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
	将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时返回代理对象,
	让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
	将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
	把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,
	即等于执行了外界提供的代码!为bind方法增加一个Advice参数。

AOP应用:实现一个类似Spring的可配置框架

操作步骤

Spring 框架核心技术 BeanFactory & AOP
"实现AOP功能的封装与配置"
工厂类"BeanFactory"负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法
	根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,
	则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,
配置文件"config.properties)"格式如下:
	#xxx=java.util.ArrayList	//普通的bean
	xxx=cn.itcast.ProxyFactoryBean//target 和 advice 都是为 ProxyFactoryBean服务的,通过配置来指定
	xxx.target=java.util.ArrayList//后缀名代表对象类型,key = name+"target"
	xxx.advice=cn.itcast.MyAdvice//value 为MyAdvice类的路径
	框架是固定的,配置文件可以进行修改,以适应不同的需求
	配置文件的key 是框架中要用到的变量引用,value是要操作的类
"ProxyFacotryBean"代理类
	充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
	目标 target
	通知 advice
编写客户端应用"AopFrameworkTest"类:
	编写实现Advice接口的类和在配置文件中进行配置
	调用BeanFactory获取对象
	调用方法进行测试

代码实现:

编写BeanFactory

package aopframework;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import enhance_2.Advice;

public class BeanFactory {
	Properties props = new Properties();
	public BeanFactory(InputStream ips) throws IOException {
		
		props.load(ips);//加载
	}
	
	public Object getBean(String name) throws  Exception{
		Object bean = null;	
		String className = props.getProperty(name);
		Class clazz = Class.forName(className);
		bean = clazz.newInstance();//JavaBean的特性:必须有一个不带参数的构造方法
		
		if(bean instanceof ProxyFactoryBean){//如果是代理 (参见配置文件)
			Object proxy = null;
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;//转型
			Advice advice = (Advice) Class.forName(props.getProperty(name + ".advice")).newInstance() ;
			Object target = Class.forName(props.getProperty(name + ".target")).newInstance() ;
			proxyFactoryBean.setAdvice(advice);
			proxyFactoryBean.setTarget(target);
			proxy = proxyFactoryBean.getProxy();	
			return proxy;	//返回proxy
		}
		return bean;	//返回bean
}

编写ProxyFactoryBean代理类

package aopframework;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import enhance_2.Advice;

public class ProxyFactoryBean {
	
	private Advice advice;
	private Object target;
		
	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;
	}

//advice 和target两个对象不再作为参数而是作为成员变量存在
	public Object getProxy() {
		//使用Proxy.newProxyInstance 静态方法直接创建代理对象
		Object proxy = Proxy.newProxyInstance(
				target.getClass().getClassLoader(),	//参数1,类加载器	
				target.getClass().getInterfaces(),	//参数2,Class数组
				
				new InvocationHandler(){	//参数3,接口型内部类对象
					@Override	//三个参数分别代表:对象	方法	 参数
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						//----------------------
						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);//代理方法的返回值Object
						advice.afterMethod(method);
						//-----------------------
						return retVal;
					}
				});
		return proxy;
	}
}


编写客户端应用AopFrameworkTest类进行测试

package aopframework;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;

public class AopFrameworkTest {

	/**
	 * @param args
	 * @throws Exception 
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException, Exception {
		
		//通过配置文件来获取bean 和proxy
		InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
		Object bean = new BeanFactory(ips).getBean("xxx");
		System.out.println(bean.getClass().getName());
		((Collection)bean).clear();
	}
}


编写配置文件config.properties

基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】

【注意】:配置文件中的value必须是已经存在的的类

    配置文件的key 是框架中的变量引用,可以通过修改对应的value来对指定的类进行操作


运行结果:可以发现该框架按照配置文件进行了指定的操作

基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】

【总结】

通过学习动态代理与AOP,能够了解框架的实现原理,学习模块化设计的的思想,对于日后主流开源框架的学习也是大有裨益的

再次深化 房子(框架) 、(用户类)、(工具类)的概念

框架的本身建造起来并不容易,但是使用起来确实无比简单,只需修改配置文件即可达到目的,尤其是其开源性使初级的开发者也能够

站在巨人的肩膀上前行,避免了大量重复的操作