无线&移动互联网技术研发

换位思考·····
posts - 19, comments - 53, trackbacks - 0, articles - 283
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Spring IOC/DI/注解-理论与实例并存

Posted on 2010-05-27 00:04 Gavin.lee 阅读(2442) 评论(0)  编辑  收藏 所属分类: SSH2 --Spring

一、定义Spring 是一个开源的控制反转(Inversion of Control,IoC/DI)和面向切面(AOP)的容器框架,它的主要目的是简化企业开发

二、实例化Spring容器:

    方法一:在类路径下寻找配置文件来实例化容器

ApplicationContext ctx =

new ClassPathXmlApplicationContext(new String[]{“beans.xml”});

    方法二:在文件系统路径下寻找配置文件来实例化容器

ApplicationContext ctx =

new FileSystemXmlApplicationContext(new String[]{“d:""beans.xml”});

注:文件可指定多个

可建立单元测试来测试Spring容器的初始化

三、编写Spring 配置文件时,不能出现帮助信息的解决办法添加schema文件

由于Springschema文件位于网络上,如果机器不能连接到网络,那么在写配置文件的时候就无法出现提示信息,解决办法有两种:

1. 让机器上网,Eclipse会自动从网络上下载schema文件并缓存在硬盘上

2手动添加schema文件,方法如下:

windows->preferences->myeclipse->files and editors->xml->xmlcatalog

a. 我们可以选择将schema文件放到User Specified Entries然后点击‘add’,在出现的窗口中的Key Type中选择URL ‘Location’中选择‘File System’,然后在Spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd

b. 回到设置窗口时候不要急于关闭窗口,应把窗口中的Key Type改为schema location

c. key改为:

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

四、Spring 配置文件一个小插曲

<bean id=”” name=””></bean>,在这里,id name都是为这个bean取的名字,其实这两者意思相同,只是如果选择使用的是id,那么id命名则不能有特殊字符,如果使用的是name则可以使用特殊字符作为name。如

<bean id=”beanId” /> && <bean name=”bean-name”/>

五、Spring容器如何管理bean

首先读取beans.xml里的bean配置所有的idclass的值,作为String形式保存至对应一个Bean,形成bean列表

利用反射机制,将bean列表遍历,生成对象,如下:

package junit.test;

import java.net.URL;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.HashSet;

import java.util.List;

import java.util.Map;

import org.dom4j.Document;

import org.dom4j.Element;

import org.dom4j.XPath;

import org.dom4j.io.SAXReader;

/**

 *传智传客版容器

 *

 */

publicclass ItcastClassPathXMLApplicationContext {

    private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();

    private Map<String, Object> sigletons = new HashMap<String, Object>();

   

    public ItcastClassPathXMLApplicationContext(String filename){

        this.readXML(filename);

        this.instanceBeans();

    }

    /**

     *完成bean的实例化

     */

    privatevoid instanceBeans() {

        for(BeanDefinition beanDefinition : beanDefines){

            try {

                if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))

                    sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

    }

    /**

     *读取xml配置文件

     *@paramfilename

     */

    privatevoid readXML(String filename) {

        SAXReader saxReader = new SAXReader();

        Document document = null;

        try {

            URL xmlpath = this.getClass().getClassLoader().getResource(filename);

            document = saxReader.read(xmlpath);

            Map<String, String> nsMap = new HashMap<String, String>();

            nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空间

            XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径

            xsub.setNamespaceURIs(nsMap);// 设置命名空间

            List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点

            for (Element element : beans) {

                String id = element.attributeValue("id");// 获取id属性值

                String clazz = element.attributeValue("class"); // 获取class属性值

                BeanDefinition beanDefine = new BeanDefinition(id, clazz);

                beanDefines.add(beanDefine);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    /**

     *获取bean实例

     *@parambeanName

     *@return

     */

    public Object getBean(String beanName){

        returnthis.sigletons.get(beanName);

    }

}

六、Spring的三种实例化Bean方式

1使用构造器实例化

<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>

    这种就是利用默认的构造器进行的实例化

2. 静态工厂方法实例化

<bean id="personService2" class="cn.itcast.service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"/>

3. 使用实例工厂方法实例化

<bean id="personServiceFactory" class="cn.itcast.service.impl.PersonServiceBeanFactory"/>

<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"/>

七、bean的作用域-属性scope

singleton:(默认)在每个Spring IOC容器中一个bean定义只有一个对象实例,默认情况下会在容器启动时初始化bean。但我们可以指定bean节点lazy-init=”true” 则表示不需要在容器初始化时候对bean进行初始化,只有在第一次getBean时候进行初始化,如果需要所有的bean都应用延迟初始化,可以在根节点<beans>设置default-lazy-init=”true”(不推荐,不利于观察bean初始化情况)

prototype:每次从容器中获取的bean都是新的对象

request

session

global session

七、Spring管理的bean的生命周期

默认情况下容器初始化的时候对bean进行实例化

如果scopeprototype时,在调用getBean方法时候对bean进行实例化

如果lazy-inittrue时,容器初始化时候不会对bean进行实例化

综上所述,Spring管理的bean初始化点根据设定的条件不同而不同

init-method:在bean被实例化的后即会执行的方法

destroy-method:在bean被销毁的时候执行的方法(如果没有手动的销毁该bean,则只有在容器关闭的时候才会销毁)

正常的关闭Spring容器

AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

ctx.close();

八、控制反转IoC 定义:

Public class PersonServiceBean {

Private PersonDao personDao = new PersonDaoBean();

Public void save (Person person) {

personDao.save(person);

}

}

PersonDaoBean是在应用内部创建和维护的,所谓的控制反转就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由外部容器负责的,这样控制权就由应用转移到了外部容器,控制权的转移就是所谓的反转。

九、依赖注入(Dependency Injection)的定义:

当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean类可以改成如下:

public class PersonServiceBean{

private PersonDao personDao;

//通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入

public PersonServiceBean(PersonDao personDao) {

this.personDao = personDao;

}

Public void save(Person person) {

personDao.save(person);

}

}

所谓的依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中

十、依赖注入:

1.使用构造器注入

private PersonDao personDao;

    private String name;   

    public PersonServiceBean(PersonDao personDao, String name) {

        this.personDao = personDao;

        this.name = name;

    }

beans.xml配置:

<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">

<constructor-arg index="0" type="cn.itcast.dao.PersonDao" ref=""></constructor-arg>

    <constructor-arg index="1" type="paramValue"></constructor-arg>

</bean>

2.使用setter方法注入

ref形式注入:

    <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">

        <property name="personDao" ref="personDaoBean" />

    </bean>

可以采用内部bean方式进行注入,不同过ref方式的是,这种内部bean注入对于bean的重用性效果不好:

    <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">

        <property name="personDao">

            <bean class="cn.itcast.dao.impl.PersonDaoBean" />

        </property>

    </bean>

bean基本类型的注入:

    <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">

        <property name="personDao">

            <bean class="cn.itcast.dao.impl.PersonDaoBean" />

        </property>

        <property name="name" value="itcast" />

        <property name="id" value="100" />

    </bean>

集合类的注入:

<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" >

        <property name="sets">

            <set>

                <value>第一个</value>

                <value>第二个</value>

                <value>第三个</value>

            </set>

        </property>

        <property name="lists">

            <list>

                <value>第一个list元素</value>

                <value>第二个list元素</value>

                <value>第三个list元素</value>

            </list>

        </property>

        <property name="properties">

            <props>

                <prop key="key1">value1</prop>

                <prop key="key2">value2</prop>

                <prop key="key3">value3</prop>

            </props>

        </property>

        <property name="maps">

            <map>

                <entry key="key-1" value="value-1" />

                <entry key="key-2" value="value-2" />

                <entry key="key-3" value="value-3" />

            </map>

        </property>

</bean>

3.使用Field注入(用于注解方式)

注入依赖对象,可以采用手工装配或者自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终装配的结果

1.     手工装配依赖对象

a)       xml配置文件中,通过在bean节点下配置

b)       java代码中使用@Autowired@Resource注解方式进行装配。但我们需要在xml配置以下信息

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"      

       xsi:schemaLocation="http://www.springframework.org/schema/beans         

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!-- 相当于启用解析注解的处理器 -->

<context:annotation-config/>

</beans>

这个配置隐式注册了多个注释进行解析处理的处理器:

注解本身不做任何事情,只是利用这些处理器来达到配置一样的效果

AutowiredAnnotationBeanPostProcessor,

CommonAnnotationBeanPostProcesor,

PersistenceAnnotationBeanPostProcessor,

RquiredAnnotationBeanPostProcessor

注:@Resource注解在spring安装目录的lib/j2ee/common-annotations.jar

jar文件:

dist/spring.jar

lib/Jakarta-commons/commons-logging.jar

lib/aspectjaspectjweaver.jaraspectjrt.jar//aop

lib/cglib/cglib-nodep-2.1_3.jar

lib/j2ee/common-annotations.jar

java代码中使用@Autowired@Resource注解方式进行装配,这两个注解的区别是:

@Autowired默认按类型装配,@Resource默认按照名称装配,当找不到与名称匹配的bean才会匹配的bean才会按类型装配

@Autowired

private PersonDao personDao;    //用于字段上

@Autowired

public void setOrderDao(OrderDao orderDao) {    //用于属性的setter方法上

    this.orderDao = orderDao;

}

@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false(默认是true),如果我们想按名称装配,可以结合@Qualifier注解一起使用,如下:

@Autowired @Qualifier(“personDaoBean”)

private PersonDao personDao;

如上面的注解,则@Autowired本身是按照类型装配,现在将会按名称装配

@Resource注解和@Actowired一样,也可以标注在字段或者属性的setter方法上,但他默认按照名称装配。名称可以通过@Resourcename属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。

@Resource(name=”personDaoBean”)

private PersonDao personDao;

注意:如果没有指定name属性,并且暗号默认的名称仍然找不到依赖对象时,“Resource注解会回退到按类型装配,但一旦指定了name属性,就只能按名称装配了

2.自动装配依赖对象

对于自动装配,作为了解

byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null

byName:按名称装配,可以根据属性的名称在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null

constructorbyType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常

autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式

十一、依赖注入的原理

Spring容器管理bean原理的实例中进行改造,该例子涉及到两个实体类:BeanDefinition{id,class}PropertyDefinition{name,ref}

package junit.test;

import java.beans.IntrospectionException;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import java.net.URL;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import org.dom4j.Document;

import org.dom4j.Element;

import org.dom4j.XPath;

import org.dom4j.io.SAXReader;

/**

 *传智传客版容器

 *

 */

publicclass ItcastClassPathXMLApplicationContext {

    private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();

    private Map<String, Object> sigletons = new HashMap<String, Object>();

   

    public ItcastClassPathXMLApplicationContext(String filename){

        this.readXML(filename);

        this.instanceBeans();

        this.injectObject();

    }

    /**

     *bean对象的属性注入值

     */

    privatevoid injectObject() {

        for(BeanDefinition beanDefinition : beanDefines){

            Object bean = sigletons.get(beanDefinition.getId());

            if(bean!=null){

                try {

                    PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();

                    for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){

                        for(PropertyDescriptor properdesc : ps){

                            if(propertyDefinition.getName().equals(properdesc.getName())){

                                Method setter = properdesc.getWriteMethod();//获取属性的setter方法 ,private

                                if(setter!=null){

                                    Object value = sigletons.get(propertyDefinition.getRef());

                                    setter.setAccessible(true);

                                    setter.invoke(bean, value);//把引用对象注入到属性

                                }

                                break;

                            }

                        }

                    }

                } catch (Exception e) {

                }

            }

        }

    }

    /**

     *完成bean的实例化

     */

    privatevoid instanceBeans() {

        for(BeanDefinition beanDefinition : beanDefines){

            try {

                if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))

                    sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());

            } catch (Exception e) {

                e.printStackTrace();

            }

        }

       

    }

    /**

     *读取xml配置文件

     *@paramfilename

     */

    privatevoid readXML(String filename) {

           SAXReader saxReader = new SAXReader();  

            Document document=null;  

            try{

             URL xmlpath = this.getClass().getClassLoader().getResource(filename);

             document = saxReader.read(xmlpath);

             Map<String,String> nsMap = new HashMap<String,String>();

             nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间

             XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径

             xsub.setNamespaceURIs(nsMap);//设置命名空间

             List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点

             for(Element element: beans){

                String id = element.attributeValue("id");//获取id属性值

                String clazz = element.attributeValue("class"); //获取class属性值       

                BeanDefinition beanDefine = new BeanDefinition(id, clazz);

                XPath propertysub = element.createXPath("ns:property");

                propertysub.setNamespaceURIs(nsMap);//设置命名空间

                List<Element> propertys = propertysub.selectNodes(element);

                for(Element property : propertys){                 

                 String propertyName = property.attributeValue("name");

                 String propertyref = property.attributeValue("ref");

                 PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref);

                 beanDefine.getPropertys().add(propertyDefinition);

                }

                beanDefines.add(beanDefine);

             }

            }catch(Exception e){  

                e.printStackTrace();

            }

    }

    /**

     *获取bean实例

     *@parambeanName

     *@return

     */

    public Object getBean(String beanName){

        returnthis.sigletons.get(beanName);

    }

}

Spring 开发的好处

a)       降低组件之间的耦合度,实现软件各层之间的解耦

 Controller -- Service -- Dao

b) 可是使用容器提供的众多服务

可使用容器提供的众多服务:事务管理服务(事务传播,无需手工控制事务)、JMS服务、Spring core核心服务、持久化服务、其他……

c) 容器提供单例模式支持,开发人员不再需要自己编写实现代码

d) 容器提供了AOP技术,利用它狠容易实现如权限拦截,运行期监控等功能

e) 容器提供了众多的辅助类,使用这些类,能够加快应用的开发,如:Jdbc TemplateHibernate Template

f) Spring 对于主流的应用框架提供了集成支持,如:集成HibernateJPAStruts等,这样更便于应用的开发

十三、 Spring DI配置文件减肥(Spring2.0 –Spring2.5的升级)

1@Resource注解

2.注解使Spring 自动扫描和管理bean

扫描路径:<context:component-scan base-package=”cn.itcast” />,将会扫描该包下包括其子包的类

@Component:泛指组件、当组件不好归类的时候,我们可以使用这个注解进行标注

@Service:用于标注业务层组件

@Controller:用于标注控制层组件(如struts中的action

@Repository:用于标注数据访问组件,即DAO组件

此时bean默认的名称为类全名首写字母小写,也可指定bean名称,如

@Service(“personService”)

下面有几个例子:

service组件的注解

package cn.itcast.service.impl;

import javax.annotation.PostConstruct;

import javax.annotation.PreDestroy;

import org.springframework.stereotype.Service;

import cn.itcast.dao.PersonDao;

import cn.itcast.service.PersonService;

@Service("personService") @Scope("prototype")    //这样可以修改bean作用域

public class PersonServiceBean implements PersonService {

    //@Autowired(required=false) @Qualifier("personDaoxxxx")

    private PersonDao personDao;

   

    //初始化bean时会执行该方法的注解(ejb3中同样应用)

    @PostConstruct

    public void init(){

        System.out.println("初始化");

    }

   

    //销毁bean之前会执行该方法的注解(ejb3中同样应用)

    @PreDestroy

    public void destory(){

        System.out.println("开闭资源");

    }

   

    public void setPersonDao(PersonDao personDao) {

        this.personDao = personDao;

    }

    public void save(){

        personDao.add();

    }

}

dao组件的注解

package cn.itcast.dao.impl;

import org.springframework.stereotype.Repository;

import cn.itcast.dao.PersonDao;

@Repository

public class PersonDaoBean implements PersonDao {

    public void add(){

        System.out.println("执行PersonDaoBean中的add()方法");

    }

}

小结:@PostConstruct @PreDestroy的补充

Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法

使用时只需要在方法前标注 @PostConstruct @PreDestroy,这些方法就会在 Bean 初始化后或销毁之前被 Spring容器执行了,我们知道,不管是通过实现 InitializingBean/DisposableBean 接口,还是通过 <bean> 元素的 init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct @PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct @PreDestroy 注释的方法都会在初始化 / 销毁时被执行。


只有注册用户登录后才能发表评论。


网站导航: