我的第一篇博客《浅谈SSH框架原理,并模仿实现Spring中Aop框架》

来源:互联网 发布:知来者之可追什么意思 编辑:程序博客网 时间:2024/04/30 03:14

 这篇日志,是在我今天应聘JAVA软件工程师,那个招聘人员问我:“SSH框

架的核心原理是什么?”,由此引发的一些感想,虽然那时答的有点不好,SSH框架

的核心原理是什么?我回答是的反射技术。真是这样的吗?回到宿舍,网上看了些资

料,才知道了自己回答的确实不妥。
 SSH中struts框架它是MVC(Model-View-Contraller)的实现,它是用于

处理数据库里数据和界面(jsp)中的数据的传输。
Model层,是以一个或者多个JavaBean形式出现的。也就是我们平时写的实体类中的

Java类。它可以存储通过Contraller中取得的数据,并通过get和set方法来操作这些

数据。
View层,也就是我们平常所编写的JSP界面,当然Struts还为我们提供了丰富的JSP标

签库,S标签,Template等,这有利于分开表现逻辑和程序逻辑。
Contraller层中的角色采用的是一个Servlet,叫ActionServlet,也就是我们平时

使用SSH框架中所编写的Action类(需继承ActionSupport),它控制一些从Hibernate

中框架中查询出来的数据。
 SSH中的Spring框架的精髓是它的IOC(Inversion of Control,反向控制)

模式实现的BeanFactory(Bean工厂)和AOP(面向切面编程),它的底层是采用动态代

理技术和反射技术来实现的。它解释起来有点麻烦,假设,现在我们所用的类到Java

程序运行期才知道,我们怎么样做呢?如Collection collection = new

ArrayList();或者Collection collection = new HashSet();这里的ArrayList或

者HashSet的名字,我们编译期时还不知道要用那个,到执行期才知道用那个,又或

者我们要随时变化使用这两个ArrayList、HashSet中的一个,在只写了一份代码的情

况下,我们该怎么做呢?Spring就给我们解决了这样的问题,它在配置文件中配置我

们所需要用的ArrayList或者HashSet通过反射技术来得到所要使用的ArrayList或者

HashSet。由于Spring涉及的技术比较底层。这里先解释到这里,等下,讲解我

Hibernate的原理后,我在自己实现一个微型的spring框架,这时,我们也许就会明

了了。
 SSH框架中的Hibernate框架,它是干嘛的呢?简单的说,它是用来处理数据

库的数据的,例如连接数据库,对数据库进行增删改查都是它进行处理的。简单的就

是它把以前我们所用写的JDBC与一些数据库的操作都进行了封装,并提供给你接口使

用。当然它也提供了一些其他技术,如JNDI和JIA等。
 好了,简单解释了下SSH框架后,我们来实现我所谓的微型的spring框架。

这实现它之前,我需讲解下它所用的技术的是什么,它所用的技术也就是Java中的动

态代理和反射技术。现在先解释下什么叫动态代理:其实它很好理解,例如,我们生

活中我们要去买瓶可乐,我们不是直接去找生产商去买,我们而是去找代理商去买,

本来这个可乐只是生产商才能生产的,但是代理商去找生产商去批发,我们再去代理

商去买,就省了我们去生产商那里去买了。这里的代理商就是我要说的生活中的代理

。当然Java代码中的代理又是怎么样的呢?它是这样的,假如我们要去执行目标类,

我们这时不直接执行目标类,而是通过代理类来执行,不过代理类实现了目标类的相

同的接口。也许有人会问,这样有什么好处呢?当然有好处啊,这时我们可以再执行

代理类的同时可以去执行一些其他的系统功能啊,如日志功能,系统功能等。这就是

Java中所谓的代理技术。当然这里可能涉及到Spring中的Aop(面向切面编程,简单

来讲就是交叉业务的编程问题)技术,因为日志功能,系统功能等这些功能可能涉及

到多个业务层中。当然了Java中存在静态代理和动态代理之分:
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,

代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。当然我们要编写微型Spring

框架我们就需采用动态代理技术了。因为有些类,我们在运行期才知道,我们事先没

法知道用户要采用哪些类。
 解释完,代理技术后,现在来解释下反射技术,什么叫反射?反射就是把

JAVA类中的各个成员成分映射成相应的java类,当然这不是我讲的,这是那些编程高

手讲的。怎么理解呢?我的理解是Java类中的各个属性,字段,方法,构造函数,接

口等,都可以通过它的类的字节码来取得它们所对应的类的对象。如取得字段属性对

象:Field field = 类.class.getField("y");取得字段y的对象。这里就可以解决

上面,我们运行期才知道名字怎么取得它的对象的问题了。
Collection collection = (ArrayList)Class.forName

(className).getConstructor(ArrayList.class).newInstance(new ArrayList

());这里的className从配置文件中取得,我们通过反射技术取得所用的类的构造函

数,并且通过实例化对象来取得类对象。我们编写这个微型Spring框架这采用JVM中

提供的Proxy来生成的动态代理类。所以这里也把Proxy所涉及的类和接口说下。
JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws

Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
 
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉

ProxySubject。
 
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现

类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[]

interfaces,
InvocationHandler h)
                               throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
这里又出现个新词类加载器:类加载器其实也是一个类的对象
java为我们提供了三类类加载器分别为

BootStrap,ExtClassLoader,AppClassLoader
它们分别加载jdk/jre/rt.jar,jdk/jre/ext/*.jar,classpath下的class文件
它们也是祖,父,子的关系。它们中存在委托的机制,当一个类要使用类加载器时,

这时AppClassLoader类加载器会委托给ExtClassLoader类加载器去找,

ExtClassLoader会委托给BootStrap去找BootStrap找到了就使用,没有找到,让

ExtClassLoader去找,ExtClassLoader也一样,没有找到让AppClassLoader去找,

AppClassLoader没有找到则抛出异常(除非你自定义类加载器)。

BeanFactory.java
/**
 * Bean工厂,用来根据配置文件取得其Bean
 * @author Administrator
 *
 */
public class BeanFactory {
 Properties properties = new Properties();
 /**
  * 根据输入流取得Properties对象
  * @param ips
  */
 public BeanFactory(InputStream ips){
  try {
   properties.load(ips);
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
 }
 public Object getBean(String name) throws Exception{
  String className = properties.getProperty(name);//根据

名字取得配置文件中所对应的类的名字
  Object bean = Class.forName(className).newInstance();//

根据类的名字取得配置文件中所对应的类
  if(bean instanceof ProxyFactoryBean){//判断配置文件是否

要求生成代理
   ProxyFactoryBean proxyBean =

(ProxyFactoryBean)bean;
   Advice advice = (Advice)Class.forName

(properties.getProperty(name+".advice")).newInstance();
   Object target = Class.forName

(properties.getProperty(name+".target")).newInstance();
   proxyBean.setAdvice(advice);
   proxyBean.setTarget(target);
   Object proxy = proxyBean.getProxy();//得到代理
   return proxy;
  }
  return bean;
 }
 

}

ProxyFactoryBean.java
/**
 * 根据类对象生成代理
 * @author Administrator
 *
 */
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;
 }

 public Object getProxy() {
  Object proxy = (Object)Proxy.newProxyInstance(
    target.getClass().getClassLoader(), //

类加载器
    target.getClass().getInterfaces(),//得

到全部的接口
    new InvocationHandler() {
     
     @Override
     public Object invoke(Object

proxy, Method method, Object[] args)
       throws

Throwable {
      // TODO Auto-generated

method stub
      advice.beforeMethod

(method);//执行目标类方法前后可以执行系统功能
      Object retValue =

method.invoke(target, args);
      advice.endMethod

(method);
      return retValue;
     }
    });
   return proxy;
 }
 

}
Advice.java
/**
 * 系统功能的接口,我们要实现的系统功能方法都放在这
 * @author Administrator
 *
 */
public interface Advice {
 public void beforeMethod(Method method);
 public void endMethod(Method method);

}
MyAdvice.java
/**
 * 系统功能实现类
 * @author Administrator
 *
 */
public class MyAdvice implements Advice {
 //这里只是简单的模拟,只写了统计开始和结束时间的方法
 long begeinTime;
 @Override
 public void beforeMethod(Method method) {
  // TODO Auto-generated method stub
  begeinTime = System.currentTimeMillis();
 }

 @Override
 public void endMethod(Method method) {
  // TODO Auto-generated method stub
  long endTime = System.currentTimeMillis();
  System.out.println(method.getName()+"running time

is"+(endTime - begeinTime));
 }

}
测试类:
AopFrameWorkTest.java
public class AopFrameWorkTest {

 /**
  * @param args
  */
 public static void main(String[] args) throws Exception {
  // TODO Auto-generated method stub
  InputStream ips =

AopFrameWorkTest.class.getResourceAsStream("config.properties");//取得

配置文件的输入流
  Object bean = new BeanFactory(ips).getBean("csk");//根

据名字取得配置文件所对应的类;
  System.out.println(bean.getClass().getName());//打印对

象名字
 }

}
配置文件:
config.properties
csk=java.util.ArrayList
#csk=com.kun.day3.aopframework.ProxyFactoryBean
csk.advice=com.kun.day3.aopframework.MyAdvice
csk.target=java.util.HashSet
 我们这时就可以根据配置文件来配置我们要加载来写类,如我们要使用集合

ArrayList的时候只需注释掉HashSet的配置,我们要用HashSet的使用只需在配置文

件中注释掉ArrayList的配置即可。这就是实现了模仿spring中aop的微型框架。
 ps:以上讲述纯粹记事本书写的,可能存在很多不足之处和错误之处,不足之处请指出并轻碰。

 

0 0