大家知道,spring依赖注入可以通过xml和annotation两种方式实现,还提供了自动扫描类的功能,这样大大简化了开发。今天也闲着没事,也实现了类似的功能。废话少说,直接上码:
先说明下要使用到的jar包:dom4j.jar和jaxen.jar(读取配置文件),junit.jar(单位测试),log4j.jar和commons-logging.jar(日志记录)。
1,类似spring的@Service注解
/**
* 自动扫描类到容器中
* @author zcl
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
public String value() default "";
}
2,@Resource注解
/**
* 通过此注解实现注入的功能
* @author zcl
*
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {
public String name() default "";
}
3, 配置文件的格式类似这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="www.zcl.com.cn">
<bean id="logService" class="cn.zcl.spring.service.impl.LogServiceImpl" />
<bean id ="userService" class="cn.zcl.spring.service.impl.UserServiceImpl">
<property name="logService" ref="logService"/>
</bean>
<!-- 实现自动扫描特定包下类的功能,可以配置多个
<scan package="cn.zcl.spring"/>
-->
</beans>
4,封装配置文件中的<property name="" ref=""/>的类:
/**
* 封装<property name="" ref=""/>的对象
* @author zcl
*
*/
public class BeanProperty {
private String name;
private String ref;
public BeanProperty(String name, String ref) {
this.name = name;
this.ref = ref;
}
//省略setter与getter()方法,请自己补上(见源码)
5,封装<bean id="" class=""><property name="" ref=""/></bean的类
/**
* 存放形如:
* <bean id="xx" class="xx">
* <property name="xx" ref="xx"/>
* </bean>
* @author zcl
*
*/
public class BeanDefinition {
private String id;//存放id属性值
private String className;//存放class属性值
//存放对应的属性值
private List<BeanProperty> props = new ArrayList<BeanProperty>();
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
//省略setter与getter()方法,请自己补上(见源码)
6, 之后就是最重要的BeanFactory了
由于代码量比较多。我分步描述
1)定义成员变量:
public class BeanFactory {
private static final Log log = LogFactory.getLog(BeanFactory.class);
/** 存放从配置文件中读取的bean的配置信息 */
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
/** 存放初始化的bean对象,其中key为id属性值,value为对应的class属性值创建的对象 */
private Map<String, Object> beans = new HashMap<String, Object>();
/** 存放自动扫描包的配置信息,<scan package="xx"/> */
private List<String> packagePaths = new ArrayList<String>();
2),读取配置文件,通过dom4j,所以必需要导入dom4j的相关jar包
/**
* 通过dom4j读取配置信息,将读取的配置信息存放到beanDefines集合中
* @param fileName
*/
@SuppressWarnings("unchecked")
private void readXml(String fileName) {
SAXReader sReader = new SAXReader();
URL url = BeanFactory.class.getClassLoader().getResource(fileName);
Document document = null;
try {
document = sReader.read(url);
Map<String, String> map = new HashMap<String, String>();
map.put("ns", "www.zcl.com.cn");
XPath xPath = document.createXPath("//ns:beans/ns:bean");
xPath.setNamespaceURIs(map);//设置名字空间
List<Element> elements = xPath.selectNodes(document);
for (Element element : elements) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
if (id != null && !id.trim().equals("") && className != null && !className.trim().equals("")) {
BeanDefinition beanDefine = new BeanDefinition(id, className);
xPath = element.createXPath("ns:property");
xPath.setNamespaceURIs(map);
List<Element> propertyList = xPath.selectNodes(element);
for (Element prop : propertyList) {
String name = prop.attributeValue("name");
String ref = prop.attributeValue("ref");
if (name != null && !name.trim().equals("") && ref != null && !ref.trim().equals("")) {
BeanProperty beanProperty = new BeanProperty(name, ref);
beanDefine.addProps(beanProperty);
}
}
beanDefines.add(beanDefine);
}
}
xPath = document.createXPath("//ns:beans/ns:scan");
xPath.setNamespaceURIs(map);
elements = xPath.selectNodes(document);
for (Element e : elements) {
String packageName = e.attributeValue("package");
packagePaths.add(packageName);
}
} catch (DocumentException e) {
log.error("解析xml文件失败!");
throw new RuntimeException(e);
}
}
上面的代码将读取指定xml的文件,会将<property name="" ref=""/>的内容存放在BeanProperty对象中,会将
<bean id="" class="">的信息存放在BeanDefinition中,最后再放到成员变量beanDefines中。
3),通过xml初始化读取到beanDefines的对象
/**
* beanDefines集合中的信息初始化bean并存放到Map中
*/
private void initBeansByXml() {
for (BeanDefinition beanDefine : beanDefines) {
try {
/*
*将id作为key值,初始化的bean作为value存放到Map中
*/
beans.put(beanDefine.getId(), Class.forName(beanDefine.getClassName()).newInstance());
} catch (Exception e) {
log.error("初始化bean失败");
throw new RuntimeException(e);
}
}
}
4),通过注解初始化Bean
/**
* 通过注解初始化Bean
*/
private void initBeanByAnnotation() {
//得到经过utf-8编码的classpath路径
URL url = BeanFactoryTest.class.getClassLoader().getResource("");
String rootPath = null;
try {
//解码成标准形式的路径格式
rootPath = java.net.URLDecoder.decode(url.getPath(),"UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("解析项目路径时错误!");
throw new RuntimeException(e);
}
//遍历每个配置了<scan package=""/>的信息
for (String packagePath : packagePaths) {
File dir = new File(rootPath, saxReader(packagePath));
if (dir == null || !dir.isDirectory()) { //如果没有设置包,则出异常
log.error("配置的package不是目录或不存在!");
throw new RuntimeException("配置的package不是目录或不存在!");
}
handler(packagePath, dir);
}
}
5),通过xml注入各种bean
/**
* 通过xml注入各种bean
* 1,先遍历在xml文件中配置的所有的bean信息
* 2,然后通过id的值从map中得到当前遍历的Bean对象
* 3,再遍历当前bean对象中所有的属性
* 4,判断对象的属性名是否在配置文件中配置过
* 5,如果配置了则通过配置文件中的ref的值从map中取出对应的bean
* 6,通过setter()方法设置到bean中完成注入
*/
private void injectByXml() {
for (BeanDefinition beanDefine : beanDefines) { //遍历配置文件中所有的bean信息
Object obj = beans.get(beanDefine.getId()); //得到bean实体
try {
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor desc : propertyDescriptor) { //遍历bean实体里所有的属性
for (BeanProperty beanProperty : beanDefine.getProps()) {
//如果属性名与配置文件中的<property name="" ref/>的name的属性值相等,则注入
if (beanProperty.getName().equals(desc.getName())) {
Method method = desc.getWriteMethod();//得到Setter()方法
method.setAccessible(true);//暴力破解,防止用户将setter方法丢了public后,程序无法注入
Object val = beans.get(beanProperty.getRef());
if (val == null) {
log.error("找不到【" + beanProperty.getName() + "】对应的bean对象");
throw new RuntimeException("找不到【" + beanProperty.getName() + "】对应的bean对象");
}
method.invoke(obj, val);//注入
}
}
}
} catch (IntrospectionException e) {
log.error("得到beanInfo时发生异常");
throw new RuntimeException(e);
} catch (Exception e) {
log.error("调用invoke()方法时异常");
throw new RuntimeException(e);
}
}
}
6),通过注解注入各种bean
/**
* 通过注解注入各种bean
* 1,先检查bean的Setter()方法上有无Resource注解
* 2,再检查属性字段上有无Resource注解
* 在注入时:
* 1,先通过Resource(name="")的name注入,若没有设置name则使用属性名
* 2,若没匹配的属性名,则通过类型注入
*/
private void injectByAnnotation() {
for (Entry<String, Object> entry : beans.entrySet()) { //遍历每个bean对象
Object obj = entry.getValue();
try {
//先检查setter()方法上有无设置Resource注解
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor desc : propertyDescriptor) { //遍历每个属性
Method method = desc.getWriteMethod();//得到Setter()方法
//如果setter()方法上标识有Resource注解
if (method != null && method.isAnnotationPresent(Resource.class)) {
Resource resource = method.getAnnotation(Resource.class);//得到此注解
String name = resource.name(); //得到name
if (name == null || name.trim().equals("")) {
name = desc.getName();//如果没有使用name,形如:@Resource, 则将属性的名字作为name
}
Object val = beans.get(name); //从Map中得到此bean对象
if (val == null) { //若为空,则通过类型注入
for (Object o : beans.values()) { //再次遍历Bean
//如果需要注入的bean是Map中某个bean的类型相同或者是其超类,则注入
if (desc.getPropertyType().isAssignableFrom(o.getClass())) {
val = o;
break;
}
}
}
method.invoke(obj, val);
}
}
//通过配置在属性字段上的Resource注入
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
//检查属性字段上有无Resource注解
if (field.isAnnotationPresent(Resource.class)) {
Resource resource = field.getAnnotation(Resource.class);
String name = resource.name();
if (name == null) {
name = field.getName();
}
Object val = beans.get(name);
if (val == null) { //通过属性名注解时找不到匹配的bean,则通过类型注入
for (Object o : beans.values()) {
if (field.getType().isAssignableFrom(o.getClass())) {
val = o;
break;
}
}
}
field.setAccessible(true);
field.set(obj, val);
}
}
} catch (IntrospectionException e) {
log.error("得到beanInfo时发生异常");
e.printStackTrace();
} catch (Exception e) {
log.error("调用invoke()方法时异常");
e.printStackTrace();
}
}
}
7,编写getBean()方法得到容器中的bean
/**
* 从环境中得到Bean对象
* @param <T>
* @param id
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <T> T getBean(String id, Class<T> clazz) {
return (T) beans.get(id);
}
/**
* 得到代理的对象
* @param <T>
* @param id
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <T> T getBeanProxy(String id, Class<T> clazz) {
final T realObj = getBean(id, clazz);
return (T)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return method.invoke(realObj, args);
}
});
}
8, 最后通过构造方法来调用各个方法
/**
* 创建此对象时会依次执行下列方法
* */
public BeanFactory() {
readXml("applicationContext.xml"); //读取配置文件
initBeansByXml(); //通过xml配置文件初始化各种对象
initBeanByAnnotation(); //通过annotation初始化各种对象
injectByXml(); //通过xml注入各种对象
injectByAnnotation(); //通过annatation注入各种对象
}
再写个测试程序:
@Test
public void testBeanFactory() {
BeanFactory factory = new BeanFactory();
UserService userService = factory.getBean("userService", UserServiceImpl.class);
userService.add();
}
用法有两种方式:
xml方式:
完全和spring的配置一样
Annotation方式:
可以在配置文件中写上<scan package="xx"/>,其中xx表示根路径下的一个包,程序可以通过此配置的xx遍历基下所有的标注有@Service注解的类,并实例化。在使用Service注解时可以带上Service("userService"),若不指定默认会使用类名作为标识。
在实现注入对象的功能只需在要注入的对象的属性字段或者setter()方法标注@Resource,当然也可以这样:
@Resourc(name="xx"),当不指定xx时会以属性字段的名称去查找容器中的bean对象,若没有匹配的,则按类型匹配,其实这和spring实现的一样。
最后通过getBean()【得到真实的对象】或getBeanProxy()【得到代理对象】来找某个bean.注意通过getBeanProxyt得到的对象必须用接口去接收(我想大家也知道原因吧)
本人是第一次正经地发贴,若有什么不好的或值得改进的,请大家指点。谢谢。
注:附件是源代码(包括jar包)。
分享到:
相关推荐
Spring依赖注入(Dependency Injection,简称DI)是Java应用开发中常用的设计模式,它极大地提高了代码的可测试性和可维护性。在Spring框架中,依赖注入是核心特性之一,通过控制反转(Inversion of Control,IoC)...
### 详解Spring 3.0基于Annotation的依赖注入实现 #### 概述 Spring框架作为一个广泛使用的Java开发框架,提供了强大的依赖注入(Dependency Injection, DI)能力,帮助开发者轻松管理和组织复杂的Java应用。随着...
总结一下,Spring的注解自动匹配注入IoC是通过注解来简化bean的管理和依赖注入。通过`@Autowired`、`@Qualifier`等注解,可以方便地完成bean的自动装配。结合`@Component`系列注解进行组件扫描,以及`@Configuration...
本文旨在深入探讨Spring框架中基于注解的依赖注入机制,特别是`@Repository`、`@Service`、`@Controller`和`@Component`等核心注解的使用方法,以及如何利用`<context:component-scan>`自动扫描功能,实现类级别的...
Spring Annotation通过在类、方法或字段上添加特定的注解,可以实现自动配置,从而实现依赖注入和组件扫描等功能。 1. **依赖注入(Dependency Injection, DI)**:Spring Annotation中最常用的注解之一是`@...
在Spring框架中,自动装配(Auto-Wiring)是一种简化依赖注入(Dependency Injection,DI)配置的方式,它允许Spring容器自动管理Bean之间的依赖关系。本文将深入探讨如何通过注解(Annotation)和`@Resource`来实现...
本文将深入探讨Spring 3.0中依赖注入的新特性,特别是如何使用`@Repository`、`@Service`、`@Controller`和`@Component`注解来标记类为Bean,以及如何利用`<context:component-scan/>`元素自动扫描和注册这些Bean。...
除了自动扫描,Spring还提供了注解来管理Bean的生命周期和依赖注入。例如: - **@Autowired**:用于自动装配Bean的依赖。Spring会尝试根据类型或名称找到合适的Bean进行注入。 - **@Qualifier**:配合@Autowired...
在Spring框架中,注解(Annotation)的使用极大地简化了传统XML配置的复杂性,使得开发者可以更加专注于业务逻辑的实现。本篇文章将深入探讨如何通过注解将配置资源注入到Bean中,以此来理解Spring的注解驱动开发。 ...
Spring是一个全面的后端开发框架,提供了依赖注入、AOP(面向切面编程)、事务管理等功能;而MyBatis则是一个轻量级的持久层框架,它将SQL与Java代码分离,使数据库操作更加灵活。本文将详细介绍如何将Spring和...
Spring框架是Java开发中不可或缺的一部分,它以其强大的依赖注入(Dependency Injection,简称DI)特性而闻名,其中自动装配(Auto-Wiring)是DI的一种实现方式。自动装配允许开发者减少手动配置bean之间的依赖关系...
Spring框架是Java开发中不可或缺的一部分,它通过提供丰富的注解简化了依赖注入、配置管理和AOP(面向切面编程)等任务。本文将深入探讨Spring注解及其在实际开发中的应用。 1. **依赖注入(Dependency Injection, ...
在Java开发领域,Spring框架是应用最广泛的轻量级框架之一,它提供了强大的依赖注入(Dependency Injection,简称DI)功能,极大地简化了企业级应用的开发。本篇将重点讲解Spring的@Autowired自动装配机制,包括XML...
这项功能允许开发者无需在XML配置文件中显式声明bean,而是通过在类上添加特定注解(如@Service、@Repository、@Controller等)来让Spring容器自动发现并管理这些bean。这一特性极大地简化了Spring应用的配置,提高...
Spring框架是Java开发中不可或缺的一部分,它通过提供依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP)等核心功能,极大地简化了企业级应用的开发工作。...