Spring新手指南(上)

本文是Spring新手入门教程,详细介绍了IOC(控制反转)和DI(依赖注入)的概念及其在Spring中的实现。通过配置文件创建Bean,演示了Person类的生命周期,包括单例和多例模式,以及通过setter方法、构造函数、p名称空间等方式进行属性赋值。此外,还讨论了bean的作用域、工厂方法创建bean、生命周期方法、SpEL表达式以及注解驱动的自动装配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring新手指南(上)

框架:高度抽取可重用代码的一种设计,是多个可重用模块的集合

IOC(Inversion[反转] of Control)

控制

  • 主动式:(要什么资源自己创建即可)
  • 被动式:(资源的获取不是我们创建,而是交给一个容器来创建和设置)
    容器:管理所有的组件(有功能的类)

DI:依赖注入,容器能知道哪个组件(类)运行的时候需要哪个组件(类),通过注入(反射的方式)进行赋值

初体验

  • 编写Person类
  public class pers.lele.domain.Person {
      private String name;
      private String gender;
      private int age;
      private String email;
  
      public pers.lele.domain.Person(){
          System.out.println("创建了。。。");
      }
      public String getName() {
          System.out.println("setName方法被调用了。。。");
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      //其他属性的getter和setter方法省略
      //toString方法省略
  }
  • 创建配置文件并配置
  <!--
      一个bean标签注册一个组件
      class:所要注册组件的全类名
      id:此组件的唯一标识
  -->
  <bean id="person" class="pers.lele.domain.pers.lele.domain.Person">
      <!--
          为组件的属性赋值
          name:指定属性名
          value:指定属性值
      -->
      <property name="name" value="张三"/>
      <property name="gender" value=""/>
      <property name="age" value="18"/>
      <property name="email" value="zhangsan@qq.com"/>
  </bean>
  • 单元测试
   /**
    * ClassPathXmlApplicationContext:类路径
    * FileSystemXmlApplicationContext:文件路径
    */
   @Test
   public void test01(){
       ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
       pers.lele.domain.Person person = context.getBean("person", pers.lele.domain.Person.class);
       System.out.println(person);
   }

总结

  • Person对象是何时创建的?

    Person对象并不是使用时才创建,而是当容器初始化的时候就创建了

        @Test
        public void test02(){
            //输出person对象创建了
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
    
  • 同一个组件在IOC容器中是单例的

        @Test
        public void test03() {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            pers.lele.domain.Person person01 = context.getBean("person", pers.lele.domain.Person.class);
            pers.lele.domain.Person person02 = context.getBean("person", pers.lele.domain.Person.class);
            System.out.println(person01 == person02);
        }
    
  • 若容器中不存在指定的组件,则报错

  • IOC容器在创建对象的时候会使用setter方法进行赋值

  • JavaBean的属性名是由getter和setter方法决定的

DI

通过构造函数创建组件

      <bean id="person01" class="pers.lele.domain.Person">
          <!--
              使用constructor-arg调用Person的有参构造器
              注意
                  1.不区分前后顺序
                  2.name属性值要与构造函数的形参名一致
          -->
          <constructor-arg name="name" value="老王"/>
          <constructor-arg name="age" value="22"></constructor-arg>
          <constructor-arg name="email" value="laowang@qq.com"></constructor-arg>
          <constructor-arg name="gender" value=""></constructor-arg>
      </bean>

若省略name属性,则constructor-arg需要严格按照构造参数的顺序

   <!-- 省略name属性 -->
   <bean id="person02" class="pers.lele.domain.Person">
       <constructor-arg value="老七"/>
       <constructor-arg value=""/>
       <constructor-arg value="88"/>
       <constructor-arg value="laoqi@qq.com"/>
   </bean>
  • 可以通过index属性来调整顺序
  • 当发生构造函数重载而导致赋值错误时,可以使用type属性来确定具体的类型

使用p名称空间进行赋值

  <bean id="person03" class="pers.lele.domain.Person" p:age="38" p:name="kobe" p:gender=""
        p:email="kobe@qq.com"></bean>

为各种类型赋值

  • 简单类型(基本类型)属性的赋值,直接使用value属性进行赋值

  • 赋null值

      <!--
          赋null值
              默认情况下就是null
              若属性在有初始值的情况下需要赋值为null时,则需要在property标签内部进行赋值
      -->
      <bean id="person01" class="pers.lele.domain.Person">
          <property name="name"><null/></property>
      </bean>
    
  • 为自定义类型赋值

    • 使用ref引用赋值(引用外部bean)
       <bean id="car01" class="pers.lele.domain.Car">
              <property name="carName" value="奥迪双转"/>
              <property name="price" value="150"/>
              <property name="color" value="紫罗兰"/>
          </bean>
       <bean id="person02" class="pers.lele.domain.Person">
              <property name="car" ref="car01"/>
       </bean>
    

    注意:person02.getCar与car01是同一个引用

    • 在标签体中创建对象(引用内部bean)
       <bean id="person03" class="pers.lele.domain.Person">
              <property name="car">
                   <bean class="pers.lele.domain.Car">
                       <property name="carName" value="宝马"/>
                       <property name="price" value="150"/>
                       <property name="color" value="大红大紫"/>
                   </bean>
              </property>
       </bean>
    
  • list类型属性赋值

    <!--list类型赋值-->
        <bean id="book01" class="pers.lele.domain.Book">
            <property name="bookName" value="叽叽叽"></property>
            <property name="author" value="那个最好的她"></property>
        </bean>
        <bean id="person04" class="pers.lele.domain.Person">
            <property name="books">
                <list>
                    <bean class="pers.lele.domain.Book" p:bookName="水浒" p:author="我自己"></bean>
                    <ref bean="book01"/>
                </list>
            </property>
        </bean>
    
  • map类型属性赋值

        <bean id="person05" class="pers.lele.domain.Person">
            <property name="maps">
                <map>
                    <entry key="key01" value="张三"></entry>
                    <entry key="key02" value="18"></entry>
                    <entry key="key03" value-ref="book01"></entry>
                    <entry key="key04">
                        <bean class="pers.lele.domain.Car">
                            <property name="carName" value="小行星"></property>
                            <property name="price" value="52"></property>
                            <property name="color" value=""></property>
                        </bean>
                    </entry>
                </map>
            </property>
        </bean>
    
  • 为properties类型赋值

      <bean id="person06" class="pers.lele.domain.Person">
              <property name="properties">
                  <props>
                      <prop key="key01">哈哈</prop>
                      <prop key="key02">呵呵</prop>
                      <prop key="key03">嘿嘿</prop>
                      <prop key="key04">嘻嘻</prop>
                  </props>
              </property>
          </bean>
    
  • utils名称空间的使用

      <utils:map id="myMap">
              <entry key="a" value="A"/>
              <entry key="b" value="B"/>
              <entry key="c" value="C"/>
          </utils:map>
          <bean id="person07" class="pers.lele.domain.Person">
              <property name="maps" ref="myMap"></property>
          </bean>
    
  • 级联属性的使用
    注意:当修改car.price时,原来的car也会发生变化

          <bean id="person08" class="pers.lele.domain.Person">
              <property name="car" ref="car01"/>
              <property name="car.price" value="88"/>
          </bean>
    

通过继承实现bean配置信息的重用

注意:此处的继承只是配置信息的继承,不存在类继承现象,且book01与book02不是同一个对象

  <bean id="book01" class="pers.lele.domain.Book">
      <property name="bookName" value="吃瓜群众" ></property>
      <property name="author" value="张三"></property>
  </bean>

  <bean id="book02" class="pers.lele.domain.Book" parent="book01">
      <property name="author" value="李四"/>
  </bean>

通过abstract户属性创建一个模板bean

  <bean id="book03" class="pers.lele.domain.Book" abstract="true">
      <property name="bookName" value="八卦G" ></property>
      <property name="author" value="张三"></property>
  </bean>
  <bean id="book04" class="pers.lele.domain.Book" parent="book03">
      <property name="author" value="李四"/>
  </bean>

通过bean的depends-on属性来改变创建顺序

  <!--创建顺序: car01->book05->person04-->
  <bean id="person04" class="pers.lele.domain.Person" depends-on="car01,book05"></bean>
  <bean id="car01" class="pers.lele.domain.Car"></bean>
  <bean id="book05" class="pers.lele.domain.Book"></bean>

测试bean的作用域

  <!--测试bean的作用域-->
  <!--
      singleton : 单实例(默认)
        在容器启动完成时就创建好了,保存在容器中
        单例模式
  -->
  <!--prototype : 多实例
        在容器启动完成时不会创建,获取的时候才创建,每次获取都创建一个新的bean
  -->
  <bean id="person05" class="pers.lele.domain.Person" scope="prototype">
      <property name="name" value="张三"/>
  </bean>

通过静态工厂和动态工厂创建bean

  • 静态工厂
      <!--静态工厂-->
      <!-- factory-method 指定通过工厂的哪个方法来创建对象-->
      <bean id="apple01" class="pers.lele.factory.AppleStaticFactory" factory-method="getInstance">
          <!-- 使用constructor-arg设置方法参数 -->
          <constructor-arg value="红富士"></constructor-arg>
      </bean>
  • 实例工厂
      <!--实例工厂-->
      <!--先创建工厂的bean-->
      <bean id="appleInstanceFactory" class="pers.lele.factory.AppleInstanceFactory"/>
      <!--
          factory-bean:使用哪个工厂
          factory-method:使用工厂的哪个方法
      -->
      <bean id="apple02" class="pers.lele.domain.Apple" factory-bean="appleInstanceFactory" factory-method="getInstance">
          <constructor-arg value="富士康"></constructor-arg>
      </bean>

通过实现FactoryBean接口来创建对象

  public class MyFactoryBeanImpl implements FactoryBean<Apple> {
  
      public Apple getObject() throws Exception {
          return new Apple("红果果", 88, "绿");
      }
  
      public Class<?> getObjectType() {
          return Apple.class;
      }
  
      //设置是否是单例
      public boolean isSingleton() {
          return false;
      }
  }
  <!--实现FactoryBean接口的工厂-->
  <bean id="myFactoryBeanImpl" class="pers.lele.factory.MyFactoryBeanImpl"></bean>

创建带有生命周期方法的bean

  public class Book {
      private String bookName;
      private String author;
  
      public Book() {
          System.out.println("Book被创建了");
      }
  
      public void myInit() throws Exception {
          System.out.println("Book对象被初始化了");
      }
  
      public void myDestroy() {
          System.out.println("Book对象被销毁了");
      }
      
      //省略
  }
    <!--创建带有生命周期方法的bean-->
    <!--
        注意:
            init-method和destroy-method方法不能有参数,可以抛出异常
            创建的顺序:
                单例模式:
                       因为是单例的,当不用容器中的bean时,也会被创建同时执行init防范,当容器关闭后,bean对象会执行相应的destroy方法
                       构造器 -> 初始化方法 -> 销毁方法
                多例模式:
                       而对于多例,在使用时才创建对象并执行初始化方法,当容器关闭后,bean对象会执行相应的destroy方法
    -->
<!--    <bean id="book01" class="pers.lele.domain.Book" init-method="myInit" destroy-method="myDestroy" scope="singleton"></bean>-->

    <bean id="book02" class="pers.lele.domain.Book" init-method="myInit" destroy-method="myDestroy" scope="prototype"></bean>

bean的后置处理器

    <!--
        实现BeanPostProcessor接口
        初始化顺序
            构造函数 初始化前置处理器 初始化方法 初始化后置处理器 容器销毁 销毁办法
        注意:若没有初始化方法,则也会执行响应的初始化前置处理器  初始化后置处理器
    -->
    <bean class="pers.lele.processor.MyBeanPostProcessor"/>
    <bean id="book03" class="pers.lele.domain.Book" init-method="myInit" destroy-method="myDestroy">
        <property name="bookName" value="雪山来客"/>
        <property name="author" value="admin"/>

Spring管理连接池

导入druid数据库连接池以及mysql驱动

  • 引用命名空间context来引入外部配置文件
  • 注意在引用properties文件的时候,数据库的用户名不能和username属性名冲突
  <!--引入外部配置文件-->
  <context:property-placeholder location="classpath:dbConfig.properties"></context:property-placeholder>
  <!--直接使用配置文件的配置信息-->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
  <!-- 注意${jdbc.username}空格-->
      <property name="username" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
      <property name="url" value="${jdbc.url}"></property>
      <property name="driverClassName" value="${jdbc.driverClassName}"></property>
  </bean>
  <!--username默认获取的是电脑用户名-->
  <bean id="car" class="pers.lele.domain.Car">
      <property name="carName" value="${username}"></property>
  </bean>

基于XML的自动装配


    <bean id="car01" class="pers.lele.domain.Car">
        <property name="carName" value="ofo"></property>
        <property name="price" value="88888888"></property>
        <property name="color" value=""></property>
    </bean>

    <bean id="car02" class="pers.lele.domain.Car">
        <property name="carName" value="青桔单车"></property>
        <property name="price" value="88888888"></property>
        <property name="color" value=""></property>
    </bean>

    <!--
        autowire="default" , autowire="no" 不会自动装配,不自动为car属性赋值
        autowire="byName":
            以属性名(属性名为setter方法)作为id去容器中找到某个组件,然后给他赋值,若不存在此id组件,则赋值为null
        autowire="byType"
            根据类型去容器中查找,若找不到则赋值为null,若存在多个,则报错:org.springframework.beans.factory.NoUniqueBeanDefinitionException
        autowire="constructor"
            按照构造器进行赋值,先根据类型进行装配,若没有赋值为null,若有多个,将参数的名作为id继续匹配,若没有,则赋值为null
    -->
    <bean id="person" class="pers.lele.domain.Person" autowire="constructor">
        <!--手动装配-->
        <!-- <property name="car" ref="car"></property> -->
    </bean>

SpEL(Spring Expression Language)测试

<bean id="car" class="pers.lele.domain.Car">
        <property name="carName" value="巴啦啦"></property>
        <property name="color" value="乐乐"></property>
    </bean>
    <!--Spring Expression Language-->
    <bean id="person" class="pers.lele.domain.Person">
        <!--使用字面常量-->
        <property name="salary" value="#{88.88*10}"></property>
        <!--引用其他的bean-->
        <property name="car" value="#{car}"></property>
        <!--引用其他bean的某个属性-->
        <property name="name" value="#{car.carName}"></property>
        <!--调用非静态方法-->
        <property name="gender" value="#{car.getColor()}"></property>
        <!--调用静态方法-->
        <property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>
    </bean>

使用注解创建DAO、Service、Controller

  • @Controller
  • @Service
  • @Repository
  • @Component

如何使用

  1. 给要添加的组件标上四个注解的任何一个
  2. 告诉Spring,自动扫描加了注解的组件,依赖context名称空间 <context:component-scan base-package=""/>
  3. 使用:bean的id是类名首字母小写(注意:要支持注解,要使用到aop包),组件默认是单例的

注意:

  • 修改bean的id值,@Controller(“pServlet”)
  • 修改单实例与多实例 @Scope(“prototype”)

只扫描与不扫描

    <context:component-scan base-package="pers.lele" use-default-filters="false">
        <!--
            annotation:使用注解进行排除
            assignable:使用全类名进行排除
        -->
        <!--指定哪些不要-->
        <!--<context:exclude-filter type="annotation" expression=""/>-->

        <!--指定只扫描哪些注解-->
        <!--use-default-filters="false" 可以禁用扫描默认规则-->
        <!--<context:include-filter type="annotation" expression=""/>-->
    </context:component-scan>

AutoWired实现自动装配

以前装配时需要在bean中指定,且还需要getter和setter方法,使用@AutoWired注解可自动赋值装配,装配规则与属性名无关(猜想与类名有关)

装配原理:

  1. 先按照类型去容器中找对应的组件,若找到一个,则直接赋值,若没找到则报错
  2. 若找到多个,则根据变量名作为id继续查找,若匹配上直接赋值,否则报错

@Autowired在装配时找不到会报错,可使用required=false来解决不报错,那是很能会出现nullPointerException

Qualifier:使用指定名字作为id进行装配,<@Qualifier(“bookService”)>,否则报错

AutoWired和Qualifier注解作用在方法上

    /**
     * 被Autowired注解修饰的类,在容器运行时会自动执行且参数会自动装配,
     * 装配原则与Autowired一致(现根据classType,若存在多个根据属性名作为id)
     * 也可以使用Qualifier注解进行制定
     * @param service
     */
    @Autowired
    public void printInfo(@Qualifier("service")PersonService  service) {
        System.out.println("printInfo...");
        service.printInfo();
    }

Autowired注解、Resource注解与@Inject(都是自动装配)

  • Autowired :强大的,Spring自己的注解
  • Resource: j2ee,java的标准,拓展性强

使用Spring的单元测试

注意
1. 在maven中test目录下进行单元测试时,若使用自动注入则会出现空指针异常,因为它不和源码在同一路径下,
包扫描扫描不到
2. 若在类路径下进行单元测试,可能会出现重复的执行,因为容易的创建发生了递归,改为static即可

如何使用

 /**
  * 使用Spring的junit的步骤
  * 1. 导入jar包 spring-test
  * 2. 使用@ContextConfiguration指定配置文件的位置
  * 3. @@RunWith指定使用哪种驱动进行单元测试
  */
 @ContextConfiguration(locations = "classpath:IOC_06.xml")
 @RunWith(SpringJUnit4ClassRunner.class)
 public class SpringJunitTest {
 
     @Autowired
     private BookServlet servlet;
 
     @Test
     public void test01(){
         System.out.println(servlet.hashCode());
     }
 }

主要用途:可以在maven的test目录下进行单元测试

泛型依赖注入

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小雷日记!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值