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

深入学习Spring框架(二)- 注解配置

程序员文章站 2024-01-29 12:02:10
1.为什么要学习Spring的注解配置? 基于注解配置的方式也已经逐渐代替xml。所以我们必须要掌握使用注解的方式配置Spring。 关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式都需要掌握。 学习基于注解的IoC配置,首先得有一个认知,即注解配置和xml配置 ......

1.为什么要学习spring的注解配置?  

  基于注解配置的方式也已经逐渐代替xml。所以我们必须要掌握使用注解的方式配置spring。
  关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式都需要掌握。
  学习基于注解的ioc配置,首先得有一个认知,即注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

2.入门示例

步骤:
  1.导入jar包,相对于之前的,在基于注解的配置中,我们还要多拷贝一个aop的jar包。

  深入学习Spring框架(二)- 注解配置

  2.在classpath下创建一个配置文件applicationcontext.xml,并导入约束,基于注解整合时,配置文件导入约束时需要多导入一个context名称空间下的约束

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    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>

  3.创建一个用于测试的类,并且加入使用@component注解,声明该类允许注入到spring容器

import org.springframework.stereotype.component;
/*
 * @component 组件注解,spring在启动的时候扫描对应的包下面的所有类型
 * 如果哪一个类上只要有 @component 注解,说明这个就需要被spring管理
 * spring在容器就创建这个类的对象
 * 
 * @component 属性介绍
 *     @component(value="id值")
 *  value :指定 bean 的 id值
 *    可以不写,默认bean的id就是当前类名的 首字母小写
 *    如果写,“value=”可以省略,直接"id值"
 * 
 */
@component("service")
public class service {
    
    public void say() {
        system.out.println("你好!spring");
    }
}

  4.往配置文件加入扫描组件配置

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    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
">
    <!-- 配置spring要进行扫描的组件注解的包(默认包含子包)的位置 -->
    <context:component-scan base-package="com.gjs.service"/>
</beans>

  5.测试代码

import org.junit.test;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;

import com.gjs.service.service;

public class testspring {
    @test
    public void testname() throws exception {
        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");
        service service = context.getbean("service",service.class);
        service.say();
        
    }
}

3.常用注解说明

  3.1 ioc相关注解

  用于被扫描创建对象的注解,统称为组件注解。组件包括:@component,@controller,@service,@repository。它们的作用是标识类为注解的组件类,启动spring框架的程序时,声明将这些组件类注入到spring容器里面。功能类似原来配置文件的<bean>标签。
其他它们的功能是一样的并没有本质上的区别,哪为什么会有4个呢?
  spring第一版注解的实现(spring 2.5),就是使用一个@component。从3.0以后,作者认为根据分层的需要,把它拆成了四个。为了可以让开发人员,可见即可得,一看到注解,立即知道类的性质。所以分成了四个。

规范:

@controller:用于声明表示层的组件注解
@service:用于声明服务层的组件注解
@repository:用于声明持久层的组件注解
@component:用于声明三层以外的组件注解
除了@controller在springmvc里面有强制的要求,springmvc的表示层必须使用@controller组件注解。其他情况不按规范使用也不会有问题,但既然是规范就要遵守。

 

  @scope:指定作用范围,等同于xml配置<bean>标签中的scope

@component("service")
@scope("prototype")
public class service {
    
    public void say() {
        system.out.println("你好!spring");
    }
}

  @postconstruct:初始化方法注解,等同于xml配置<bean>标签中的init-method

@postconstruct 
public void init() {
    system.out.println("初始化方法执行了");
}

 

  @predestroy:销毁方法注解,等同于xml配置<bean>标签中的destroy-method

@predestroy
public void destroy() {
     system.out.println("销毁方法执行了");
}

 

  3.2 依赖注入的注解

  spring提供了两套用注解依赖注入的解决方案
    1.@autowired +@qualifier():是spring定义的标签
    2.@resouce:是j2ee的规范

  

@autowired +@qualifier()

@autowired +@qualifier()有三种注入的方式:
  1.在字段上面注入
  2.在方法上面注入
  3.在构造方法上面注入

示例:

整体结构:

深入学习Spring框架(二)- 注解配置

  customeservice接口:

package com.gjs.service;

public interface customeservice {
    public void say();
}

  customserviceimpl1:

package com.gjs.service.impl;

import org.springframework.stereotype.service;

import com.gjs.service.customeservice;
@service("service1")
public class customserviceimpl1 implements customeservice {

    @override
    public void say() {
        system.out.println("customerserviceimpl1.say()");
    }
}

  customserviceimpl2:

package com.gjs.service.impl;

import org.springframework.stereotype.service;

import com.gjs.service.customeservice;

@service("service2")
public class customserviceimpl2 implements customeservice {

    @override
    public void say() {
        system.out.println("customerserviceimpl2.say()");
    }
}

  customcontroller:

package com.gjs.client;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.stereotype.controller;

import com.gjs.service.customeservice;


@controller("client")
public class customcontroller {
    /*
     * 方式一(推荐) : 在字段(成员变量)上注入
     * @autowired :
     *     默认会从spring容器找对应类型的对象注入进来
     *  使用@autowired 必须保证spring容器中最少一个类型对应bean ,如果没有就会抛异常
     *   org.springframework.beans.factory.nosuchbeandefinitionexception 
     *     可以使用 注解的 required属性(除特殊情况,一般不使用)
     *  required = true/false 是否是必须有对应的对象,true 是必须有(默认),false 不是必须有
     * 
     *  如果spring容器有多个相同类型的对象,默认无法注入也会抛异常
     *  org.springframework.beans.factory.nouniquebeandefinitionexception 不是唯一的bean异常
     *  这时就需要配合使用 @qualifier() 注解了
     *  @qualifier(value="对应bean的id值")可以在多个相同类型的对象中筛选指定唯一id的对象,“value=”可以省略
     */
    //@autowired(required=false)
    //@qualifier("service1")
    private customeservice customeservice;
    
    /*
     * 方式二 :使用setter方法(属性)注入
     * 将@autowired直接贴在set方法上面即可,程序运行,会执行set方法
     * 将spring容器对应的类型的参数赋值给 set方法的参数,类型不存在或存在多个,处理方式与方式一一样
     */
    //@autowired()
    //@qualifier("service1")
    public void setcustomeservice(customeservice customeservice) {
        this.customeservice = customeservice;
    }
    
    /*
     * 方式三 : 构造器注入
     * 使用注解的ioc创建bean的情况下
     * 默认bean中有什么样的构造器,spring就调用那个构造器去创建对应的bean对象
     * 并且会自动注入 构造器中对应类型参数的对象,无须@autowired()
     * 
     * 如果构造函数的参数类型对应的bean有多个就在 在参数前面 使用 @qualifier()注解,指定 对应的bean的id
     */

    public customcontroller(@qualifier("service1")customeservice customeservice) {
        this.customeservice = customeservice;
    }

    public void say() {
        customeservice.say();
    }
    
}

  applicationcontext.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    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
">
    <!-- 配置spring要进行扫描的组件注解的包(默认包含子包)的位置 -->
    <context:component-scan base-package="com.gjs"/>
    
</beans>

  测试类testspring:

package com.gjs.test;

import org.junit.test;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;

import com.gjs.client.customcontroller;

public class testspring {
    @test
    public void testname() throws exception {
        //1.读取配置文件,创建spring容器
        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");
        //获取调用方 customclient对象
        customcontroller client = context.getbean("client", customcontroller.class);
        //调用customclient对象的say()方法
        client.say();
    }
}

  @resouce

  @resource 功能等同 @autowired + @qualifier
  @resource只能注入字段和setter方法,不能注入构造方法

  customcontroller类,其他参考上面的

package com.gjs.client;

import javax.annotation.resource;

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.stereotype.controller;

import com.gjs.service.customeservice;


@controller("client")
public class customcontroller {
    /*
     * 方式一: 字段注入
     *  也是默认会从spring容器找对应类型的对象注入进来
     *  有多个相同类型时,可以使用@resource(name="对应bean的id")指定注入哪个对象
     *  @resource 必须保证需要注入的类型在spring容器中最少有一个对象,没有直接抛异常
     */
    //@resource(name="service1")
    private customeservice customeservice;
    
    /*
     * 方式二: set方法(属性)注入
     */
    @resource(name="service1")
    public void setcustomeservice(customeservice customeservice) {
        this.customeservice = customeservice;
    }
    

    public void say() {
        customeservice.say();
    }
    
}

 

  @value注解

  @value注解:注入基本数据类型以及它们的包装类和string类型数据的,支持${}注入properties文件的键值对,等同 <proprty name=”...” value=”${key}”>。

@repository
public class userdaoimpl implements userdao {
    
    /**
     * @value(value="")
     * 可以从spring容器读取 .properties 配置文件内容
     * value :配置文件的对应的key -->使用 ${key} 获取
     * 程序运行中自动将 properties 对应key的获取出来设置给字段
     * 
     */
    
    //等价 <property name="driverclassname" value="${jdbc.driverclassname}">
    @value("${jdbc.driverclassname}") 
    private string driverclassname;
    
    @value("${jdbc.url}")
    private string url;
    
    @value("${jdbc.username}")
    private string username;
    
    @value("${jdbc.password}")
    private string password;
    
    //@value("${jdbc.maxactive}")
    @value("10") //开发者也手动赋值
    private string maxactive;
    

    @override
    public void insert(user user) {
        system.out.println(driverclassname);
        system.out.println(url);
        system.out.println(username);
        system.out.println(password);
        system.out.println(maxactive);

    }

}

  

4.纯注解配置

  虽然使用注解的方式,但我们还是离不开xml文件,因为我们还有配置组件扫描位置,如果这也能用注解配置,那么我们就可以脱离xml文件了。
  替换xml配置文件的注解:

  深入学习Spring框架(二)- 注解配置

package com.gjs.config;

import javax.sql.datasource;

import org.springframework.beans.factory.annotation.value;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.componentscan;
import org.springframework.context.annotation.configuration;
import org.springframework.context.annotation.propertysource;

import com.alibaba.druid.pool.druiddatasource;

/*
 * @configuration 
 * 说明把当前类当做成spring框架的配置文件
 * @componentscan 
 *  配置注解包扫描的位置
 * @propertysource("classpath:db.properties")
 *  读取.peroperties 后缀的配置文件
 */

@configuration
@componentscan("com.gjs")
@propertysource("classpath:db.properties")
public class springconfig {
    
    
    /**
     * @value(value="")
     * 可以从spring容器读取 .properties 配置文件内容
     * value :配置文件的对应的key -->使用 ${key} 获取
     * 程序运行中自动将 properties 对应key的获取出来设置给字段
     * 
     */
    
    //等价 <property name="driverclassname" value="${jdbc.driverclassname}">
    @value("${jdbc.driverclassname}") 
    private string driverclassname;
    
    @value("${jdbc.url}")
    private string url;
    
    @value("${jdbc.username}")
    private string username;
    
    @value("${jdbc.password}")
    private string password;
    
    @value("${jdbc.maxactive}")
    private integer maxactive;
    
    
    //<bean id="datasource" class="com.alibaba.druid.pool.druiddatasource" 
      //init-method="init" destroy-method="close">
    @bean(name="datasource",initmethod="init",destroymethod="close")
    public datasource getdatasource() {
        druiddatasource datasource = new druiddatasource();
        datasource.setdriverclassname(driverclassname);
        datasource.seturl(url);
        datasource.setusername(username);
        datasource.setpassword(password);
        datasource.setmaxactive(maxactive);
        return datasource;
    }

}

5. spring的测试

  5.1.传统的单元测试

  存在的问题:
    1,每个测试都要重新启动spring容器,启动容器的开销大,测试效率低下。
    2,不应该是测试代码管理spring容器,应该是spring容器在管理测试代码。

  深入学习Spring框架(二)- 注解配置

  5.2 正确的spring的测试

  深入学习Spring框架(二)- 注解配置

  5.3 如何使用spring测试

  spring测试必须保证eclipse的单元测试的最低版本是 4.12版本,如果使用的eclipse版本很低,那么单元测试版本可能低于4.12,那么需要开发者手动导入单元测试的jar包

  要使用spring测试就要先导入test的jar包

  深入学习Spring框架(二)- 注解配置

 

 

package com.gjs.test;

import org.junit.test;
import org.junit.runner.runwith;
import org.springframework.context.applicationcontext;
import org.springframework.context.support.classpathxmlapplicationcontext;
import org.springframework.test.context.contextconfiguration;
import org.springframework.test.context.junit4.springjunit4classrunner;

import com.gjs.client.customcontroller;

//表示先启动spring容器,把junit运行在spring容器中
@runwith(springjunit4classrunner.class)
//表示从哪里加载资源文件,默认从src(源目录)下面加载
@contextconfiguration("classpath:applicationcontext.xml")
public class testspring {
    @test
    public void testname() throws exception {
        //1.读取配置文件,创建spring容器
        applicationcontext context = new classpathxmlapplicationcontext("applicationcontext.xml");
        //获取调用方 customclient对象
        customcontroller client = context.getbean("client", customcontroller.class);
        //调用customclient对象的say()方法
        client.say();
    }
}