---------- android培训、java培训、期待与您交流! ----------
Spring 是Java领域一个著名的开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架 。Spring有两大核心技术,一个是控制反转(ioc)也被称为注入(di),另外一个是面向切面的编程(aop),使用拥有者两大技术的sping创建应用程序变的很简单。
看了张孝祥老师的java高新视频教程,想自己写一个sping这样的框架,今天实现了sping核心技术中的注入。。。。。。
下面的这个类实现了sping中的注入功能,配置使用的是xml(beans.xml),解析xml文件使用的是dom4j。原理是扫描配置文件,把class属性的内容通过Class的forName这个静态方法得到一个对象,把放进map里面,key是配置文件中对应的id的值。若bean有property 这个属性,把property 上的ref对应的对象,注入到name的值表示的成员变量上,用法和spring差不多,看下配置文件格式就明白了。
xml的配置文件格式为:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="test" class="com.heima.Test">
<property name="myTest" ref="myTestImpl" />
</bean>
<bean id="myTestImpl" class="com.heima.MyTestImpl" />
</beans>
------------------------------------------------------
BeanFactory类:
public class BeanFactory {
privatestaticBeanFactory beanFactory=newBeanFactory();//使用了singleton模式
private Map<String, Object> beanMap = new HashMap<String, Object>();
privateBeanFactory(){
try{
init();
}catch(MyFrameworkException e){
e.printStackTrace();
}
}
publicstaticBeanFactory getBeanFactory(){
return beanFactory;
}
publicObject getBean(String id){
return beanMap.get(id);
}
//扫描xml配置文件
public void init() throws MyFrameworkException {
// 拿到配置文件的根元素
Element root = getRootElement();
// 拿到所有的bean元素
List<Element> beans = root.elements("bean");
// 遍历所有的bean元素,并装进map中,key为bean的id,value为这个bean对象
for (Element bean : beans) {
// 检查bean的格式
checkBeanData(bean);
// 获得bean这个对象
Object beanObj = getBeanObject(bean.attribute("class").getText());
// 装进对象
beanMap.put(bean.attributeValue("id"), beanObj);
}
// 开始注入
di(beans);
}
privatevoid di(List<Element> beans)throwsMyFrameworkException{
//重新读取下beans这个list,这样做可以使xml文件中bean的配置可以不用管顺序,代价是遍历了两次beans的list
for (Element bean : beans) {
Element property = getProperty(bean);
if (property != null) {
// 得到id为bean.attributeValue("id")的对象
Object beanObj = beanMap.get(bean.attributeValue("id"));
// 得到bean的参考bean
// id为property.attributeValue("ref"),并把这个参考bean设置到bean的字段上
Object refObj = beanMap.get(property.attributeValue("ref"));
if (refObj == null) {
// 如果被参考的bean为空,抛出异常
throw new MyFrameworkException("找不到id为"
+ property.attributeValue("ref") + "的Bean,请检查配置文件");
}
// 开始反射,使用了直接设置类中的私有字段的暴力反射方式(还可以使用javaBean,setXxx方法设置值)
Field field = null;
try {
field = beanObj.getClass().getDeclaredField(
property.attributeValue("name"));
field.setAccessible(true);
field.set(beanObj, refObj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//检查bean元素的格式是否正确
private void checkBeanData(Element bean) {
Attribute classAttr = bean.attribute("class");
Attribute idAttr = bean.attribute("id");
if (classAttr == null) {
try {
throw new MyFrameworkException("节点bean的属性class不能为空");
} catch (MyFrameworkException e) {
e.printStackTrace();
}
}
if (idAttr == null) {
try {
throw new MyFrameworkException("节点bean的属性id不能为空");
} catch (MyFrameworkException e) {
e.printStackTrace();
}
}
}
//拿到bean元素下的property元素
private Element getProperty(Element bean) {
Element property = bean.element("property");
if (property == null)
return null;
//如果属性name为空抛出异常
if (property.attributeValue("name") == null) {
try {
throw new MyFrameworkException("节点Property的name属性不能为空");
} catch (MyFrameworkException e) {
e.printStackTrace();
}
}
// 如果属性ref为空抛出异常
if (property.attributeValue("ref") == null) {
try {
throw new MyFrameworkException("节点Property的ref属性不能为空");
} catch (MyFrameworkException e) {
e.printStackTrace();
}
}
return property;
}
//通过bean的名字,获得一个对象
private Object getBeanObject(String name) {
Object obj = null;
try {
obj = (Object) Class.forName(name).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
//得到xml配置文件的根节点
private Element getRootElement() {
SAXReader reader = new SAXReader();
Element root = null;
try {
root = reader.read(
this.getClass().getClassLoader()
.getResourceAsStream("beans.xml")).getRootElement();
} catch (DocumentException e) {
System.out.println("读取beans.xml失败,你确定把它放在了ClassPath下了吗?");
}
return root;
}
//重新读取
public void reload() {
try {
init();
} catch (MyFrameworkException e) {
e.printStackTrace();
}
}
}
在配置文件值使用到的两个测试类还有一个接口如下:
定义MyTest这个接口
package com.itheima;
publicinterfaceMyTest{
void test();
}
写一个MyTest接口的实现MyTestImpl
package com.itheima;
publicclassMyTestImplimplementsMyTest{
@Override
publicvoid test(){
System.out.println("注入成功");
}
}
然后写配置文件中的被注入的Test类,也是main方法所在的类
package com.itheima;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
publicclassTest{
privateMyTest myTest;
publicstaticvoid main(String[] args)throwsException{
BeanFactory bf=BeanFactory.getBeanFactory();
Test test=(Test)bf.getBean("test");
test.getMyTest().test();
}
publicMyTest getMyTest(){
return myTest;
}
publicvoid setMyTest(MyTest myTest){
this.myTest= myTest;
}
}
若main方法中这句代码执行后test.getMyTest().test();打印出了注入成功,这句话则说明注入成功了
打印结果是:
注入成功
spring注入功能就完成了。。。。
这个程序中最主要的逻辑是这端代码
// 开始反射,使用了直接设置类中的私有字段的暴力反射方式(还可以使用javaBean,setXxx方法设置值)
Field field = null;
try {
field = beanObj.getClass().getDeclaredField(
property.attributeValue("name"));
field.setAccessible(true);
field.set(beanObj, refObj);
} catch (Exception e) {
e.printStackTrace();
}
使用的是暴力反射的方式进行注入的,除此之外还有一种方式也可以做到。通过反射的方式拿到,符合JavaBean标准的类(拥有一系类gets和sets方法)中的私有字段,有两种方式,一种为暴力反射,一种是使用,Java api对JavaBean操作的支持
通过代码,描述一下,现在有一个了目标类,获得他的私有字段x
package com.itheima;
publicclassTarget{
privateint x;
publicint getX(){
return x;
}
publicvoid setX(int x){
this.x= x;
}
}
通过反射拿到Target的x值
public static void main(String[] args) throws Exception {
Target t = new Target();
t.setX(5);
//通过暴力反射方式
Field f = Target.class.getDeclaredField("x");
f.setAccessible(true);
f.get(t);
System.out.println(f.get(t));
//通过JavaBean
PropertyDescriptor pd = new PropertyDescriptor("x", Target.class);
Method method = pd.getReadMethod();
System.out.println(method.invoke(t, null));
}
结果成功获得了x的值(都打印了5),使用PropertyDescriptor 这个类的原理,其实就是根据传进来的x,拼出getX这个方法,并通过反射进行调用。通常我们都使用符合JavaBean标准这种,因为使用暴力反射会破坏程序的封装性,像一些框架ssh只有很少数的情况下,才会使用暴力反射,比如Hibernate只有注解写在私有字段上时才会使用这种方式。
---------- android培训、java培训、期待与您交流! ----------
详细请查看:http://edu.csdn.net/