基础加强____【动态代理 & AOP】【实现类Spring的AOP框架】
动态代理 与AOP (Aspect Oriented Program)面向方面编程,OOP为面向对象
代理的结构
--------------------------------------------------------------
"动态代理类的设计原理与结构",框架结构
----------------------------
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
【注意】:配置文件中的value必须是已经存在的的类配置文件的key 是框架中的变量引用,可以通过修改对应的value来对指定的类进行操作
运行结果:可以发现该框架按照配置文件进行了指定的操作
【总结】
通过学习动态代理与AOP,能够了解框架的实现原理,学习模块化设计的的思想,对于日后主流开源框架的学习也是大有裨益的
再次深化 房子(框架) 、门(用户类)、锁(工具类)的概念
框架的本身建造起来并不容易,但是使用起来确实无比简单,只需修改配置文件即可达到目的,尤其是其开源性使初级的开发者也能够
站在巨人的肩膀上前行,避免了大量重复的操作