欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring基于xml配置与注解驱动开发(一)

程序员文章站 2024-03-19 16:33:46
...

1. Spring IoC容器和beans的简介

Spring基于xml配置与注解驱动开发(一)

IoC也被称为依赖注入(DI)。这是一个过程,对象通过构造函数参数,工厂方法的参数或在工厂方法构造或返回后在对象实例上设置的属性来定义它们的依赖关系,即它们所处理的其他对象。然后容器 在创建bean时注入这些依赖关系。这个过程基本上是相反的,因此名为控制反转(IoC),通过使用类的直接构造或诸如Service Locator模式的机制来控制bean本身的依赖关系的实例化或位置。

在org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基础。该 BeanFactory 接口提供了一种能够管理任何类型对象的高级配置机制。 ApplicationContext 是BeanFactory的一个子接口。它增加了与Spring的AOP功能更易集成、 消息资源处理(用于国际化)、事件发布以及应用层特定的上下文,例如WebApplicationContext 在Web应用程序中使用。

总之,BeanFactory提供了配置框架和基本的功能,并且ApplicationContext增加了更多的企业特定功能。ApplicationContext是BeanFactory的一个完整的扩展。

在Spring中,构成应用程序的骨干并由Spring IoC 容器管理的对象称为bean。bean是一个被实例化、组装并由Spring IoC容器管理的对象。bean只是应用程序中众多对象中的一个普通对象。bean之间的依赖关系反映在配置元数据中被容器使用。

2. spring应用上下文配置

2.1 基于xml配置

web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

spring-context.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.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd ">

</beans>

2.2 基于注解的配置

web.xml

<context-param>  
    <param-name>contextClass</param-name>  
    <param-value>  
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext  
    </param-value>  
</context-param>  
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>com.yt.MainConfig</param-value>  
</context-param>
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>

MainConfig.java

@Configuration
public class MainConfig {

}

3. bean定义

基于XML的配置,你可以在<bean/>元素通过class属性来指定对象的类型。这个class属性,实际上是BeanDefinition实例中的一个Class属性。IOC容器会通过反射调用构造方法或工厂方法来创建bean

3.1 基于xml配置

3.1.1 bean创建

spring-context.xml

<!--通过构造器创建bean-->
<bean id="person" class="com.yt.model.Person"/>
<!--静态工厂方法创建bean-->
<bean id="person1" class="com.yt.factory.PersonFactory" factory-method="newInstance"/>
<!--工厂方法创建bean-->
<bean id="personFactory" class="com.yt.factory.PersonFactory"/>
<bean id="person2" factory-bean="personFactory" factory-method="createInstance"/>

工厂类:

public class PersonFactory {

    public static Person newInstance(){
        return new Person();
    }
    public  Person createInstance() {
        return new Person();
    }
}

3.1.2 依赖注入DI

构造器注入

 <!--如果constructor-arg的顺序与构造器参数的顺序不同则需要用注释的三种方式之一来显式的指定构造器参数-->
 <bean id="human" class="com.yt.model.Human">
            <constructor-arg >
                <value>yt</value>
            </constructor-arg>
            <constructor-arg value="24"/>
            <constructor-arg>
                <ref bean="person1"/>
            </constructor-arg>
           <!-- <constructor-arg index="0" value="yt"/>
            <constructor-arg index="1" value="24"/>
            <constructor-arg index="2" ref="person1"/>-->

            <!--<constructor-arg name="name" value="yt"/>
            <constructor-arg name="age" value="24"/>
            <constructor-arg name="person" ref="person1"/>-->

            <!--<constructor-arg type="java.lang.String" value="yt"/>
            <constructor-arg type="int" value="24"/>
            <constructor-arg type="com.yt.model.Person" ref="person1"/>-->
        </bean>

setter注入(必须要有无参构造器和setter方法)

 <bean id="human1" class="com.yt.model.Human">
     <property name="name" value="yt"/>
      <property name="age" >
          <value>24</value>
      </property>
      <property name="person">
          <ref bean="person1"/>
      </property>
  </bean>

工厂方法注入

工厂类:

 public static Human newHuman(String name,int age,Person person){
        return new Human(name,age,person);
    }
 public Human createHuman(String name,int age,Person person) {
      return new Human(name,age,person);
  }
<!--静态工厂方法-->
 <bean id="human2" class="com.yt.factory.PersonFactory" factory-method="newHuman">
      <constructor-arg value="yt"/>
      <constructor-arg value="24"/>
      <constructor-arg ref="person1"/>
  </bean>
  <!--工厂方法-->
  <bean id="human3" factory-bean="personFactory" factory-method="createHuman">
       <constructor-arg value="yt"/>
       <constructor-arg value="24"/>
       <constructor-arg ref="person1"/>
   </bean>

3.1.3 补充内容

<idref>元素

idref元素用来将容器内其他bean的id(值是字符串-不是引用)传给元素<constructor-arg/> 或者<property/>,如:

      property name="name" >
          <idref bean="person"/>
      </property>

      等同于

      property name="name" value="person">

通过这种方式可以检验容器中bean是否存在

内部bean

 <bean id="human" class="com.yt.model.Human">
      <constructor-arg >
           <value>yt</value>
       </constructor-arg>
       <constructor-arg value="24"/>
       <constructor-arg>
           <bean class="com.yt.model.Person"></bean>
       </constructor-arg>
 </bean>

内部bean不可以被外部bean引用,同时内部bean是匿名的,不需要id或name属性,IOC容器会忽略这些属性。

NULL

spring会把 “” 作为空字符串处理,对于null spring提供了<null>元素

<property name="email">
       <null/>
</property>

p命名空间

 <bean id="human4" class="com.yt.model.Human" p:name="yt" p:age="24" p:person-ref="person1"/>

c命名空间

 <bean id="human5" class="com.yt.model.Human" c:name="yt" c:age="24" c:person-ref="person1"/>
 <bean id="human6" class="com.yt.model.Human" c:_0="yt" c:_1="24" c:_2-ref="person1"/>

depends-on

bean初始化之前显式地强制一个或多个bean被初始化

 <bean id="human6" class="com.yt.model.Human" c:_0="yt" c:_1="24" c:_2-ref="person1" depends-on="person1,person2"/>

延迟初始化bean

<bean id="person" class="com.yt.model.Person" lazy-init="true"/>

ApplicationContext容器在启动时会实例化所有的单例bean,lazy-init 属性默认是false,设置lazy-init=true将不会在ApplicationContext启动时提前实例化,当person是其他单例bean的依赖时,即使lazy-init=true,person依然会被强制加载。

//可以设置全局的单例为lazy
<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

自动装配

mode 描述
no (默认)不自动装配。Bean的引用必须用ref元素定义。对于较大的部署不建议改变默认设置,因为明确指定协作者能更好控制和维护系统。 在某种程度上,它记录了系统的结构。
byName 通过属性名称自动装配。Spring会寻找相同名称的bean并将其与属性自动装配。譬如,如果bean的定义设置了根据名称自动装配, 并且包含了一个master 属性(换句话说,它有setMaster(..)方法),Spring会寻找名为master的bean的定义,并用它来装配属性
byType 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,将会抛出异常,并指出 不能使用byType自动装配这个bean。如果没有找到相同类型的,什么也不会发生。属性不会被设置。
constructor 和byType类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,就会抛出异常。
<bean id="human7" class="com.yt.model.Human" autowire="byName"/>

同样可以设置全局的bean自动装配

<beans default-autowire="byName">
    <!-- no beans will be pre-instantiated... -->
</beans>

bean作用域

scope 描述
singleton (默认的) 每个 String IoC 容器作用域中一个 bean 定义只对应一个对象实例。
prototype 一个 bean 定义对应多个对象实例。
request 一个 bean 定义作用于 HTTP request 生命周期;是指每个 HTTP request 拥有自己的通过一个 bean 定义创建的实例。仅在基于 web 的 Spring ApplicationContext 中有效。
session 一个 bean 定义作用于 HTTP session 生命周期。仅在基于 web 的 Spring ApplicationContext 中有效。
global session 一个 bean 定义作用于全局的 HTTP session 生命周期。仅在 portlet context 中使用才有效。仅在基于 web 的 Spring ApplicationContext 中有效。
application 一个 bean 定义作用于整个 ServletContext 生命周期。仅在基于 web 的 Spring ApplicationContext 中有效。
<bean id="human7" class="com.yt.model.Human" autowire="byName" scope="prototype"/>

3.2 基于注解的配置

3.2.1 注册组件

包扫描@ComponentScan

/**
 * FilterType.ANNOTATION:按照注解
 * FilterType.ASSIGNABLE_TYPE:按照指定的类型
 * FilterType.ASPECTJ:使用ASPECTJ表达式
 * FilterType.REGEX:使用正则表达式
 * FilterType.CUSTOM:自定义FilterType,实现TypeFilter接口
 */
 //ConponentScan可定义多个
@ComponentScan(value = "com.yt.*",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Person.class),
                @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Component.class,Service.class}),
        },
        excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = User.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)},
        lazyInit = false,
        useDefaultFilters = false)//但useDefaultFilters=true时,默认会扫描所有组件@Controller、@Srevice、@Component、@Respository
public class MainConfig {
}

自定义TypeFilter

public class MyTypeFilter implements TypeFilter{
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前正在扫描的类注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前正在扫描的类的资源路径
        Resource resource = metadataReader.getResource();
        //获取当前正在扫描的类的类名
        String className = classMetadata.getClassName();
        //返回true时该类匹配
        if(className.contains("Controller")) {
            return true;
        }
        return false;
    }
}

等同于spring-context.xml中配置:

 <context:component-scan base-package="com.yt.*" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:include-filter type="assignable" expression="com.yt.model.User"/>
        <context:exclude-filter type="assignable" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="custom" expression="com.yt.conf.MyTypeFilter"/>
    </context:component-scan>

@Bean

用于导入第三方的bean

public class MainConfig {
   /**
     * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_PROTOTYPE
     * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_SINGLETON
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
     */
    @Lazy //懒加载,仅对singleton作用,因为prototype原本就是懒加载
    @Bean(name = "person") //默认是方法名作为id
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)//prototypeIOC容器启动时不会加载,singleton在IOC容器启动时会被加载
    public Person person(){
        System.out.println("init");
        return new Person("lisi" ,20);
    }
 }

@Import

/**
 * @{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
 * or regular component classes to import.
 */
 @Import({MyImportBeanDefinitionRegistrar.class,Person.class, MyImportSelector.class})
 public class MainConfig {
 }

自定义ImportSelector

public class MyImportSelector implements ImportSelector {
    //返回值不允许null,否则会抛出NullPointException
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.yt.model.Person","com.yt.model.Human"};
    }
}

自定义ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    //importingClassMetadata包含了所有bean的注解元数据,register包含了bean定义信息和注册bean方法
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean definition = registry.containsBeanDefinition("user");
        if(!definition) {
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class);
            registry.registerBeanDefinition("user",rootBeanDefinition);
        }
    }
}

bean工厂

实现Spring提供的FactoryBean,默认获取的是工厂Bean调用的getObject创建的对象,获取工厂Bean本身,需要在id前面加上&

自定义bean工厂

public class UserFactoryBean implements FactoryBean{
    /**
     * 获取对象
     * @throws Exception
     */
    public Object getObject() throws Exception {
        return new User();
    }
    /**
     * 返回对象的类型
     */
    public Class<?> getObjectType() {
        return User.class;
    }
    /**
     * 是否创建一个单例对象
     */
    public boolean isSingleton() {
        return false;
    }
}
@Configuration
public class MainConfig {
     /**
      * 返回的是getObject方法id为userFoctoryBean的实例
      */
     @Bean
     public UserFactoryBean userFactoryBean() {
         return new UserFactoryBean();
     }
}

UserFactoryBean实例的id是“&userFactoryBean

3.2.2 @Conditional

@Bean(name = "linux")
@Conditional(value = {LinuxCondition.class})
public Person linuxPerson() {
    System.out.println("Linus Torvalds");
    return new Person("Linus Torvalds",48);
}
@Bean(name = "windows")
@Conditional(value = {WindowsCondition.class})
public Person windowsPerson() {
    System.out.println("Bill Gates");
    return new Person("Bill Gates",65);
}

实现Condition接口可条件注册组件

public class WindowsCondition implements Condition{
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取到IOC容器中使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //获取当前环境变量
        Environment environment = context.getEnvironment();
        //获取的bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        //获取源文件加载器
        ResourceLoader resourceLoader = context.getResourceLoader();
        //获取当前操作系统
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")) {
            return true;
        }
        return false;
    }
}