Spring入门(2)—— IoC与DI使用、纯注解配置

IoC基于XML的使用

一、创建工程

环境

maven3.6

jdk1.8

spring5.0.7

ide的话随便,创建maven项目即可

工程搭建

1.pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>spring-demo1</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <!-- spring ioc组件需要的依赖包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>

        <!-- 单元测试Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <!-- 配置Maven的JDK编译级别 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2. 创建spring配置文件(resources路径下) -- applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>

二、实现

演示

1. 思路

编写UserService接口的实现类

将UserService实现类交给Spring IoC容器管理

从Spring IoC容器中获取UserService实现类

2. UserService

public interface UserService {

	void saveUser();
}

3. UserServiceImpl

public class UserServiceImpl implements UserService {
	@Override
	public void saveUser() {
		System.out.println("IoC 演示 之UserService");
	}

}

4. applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring IoC基于XML配置,主要是通过bean标签进行Bean的管理的 -->
    <bean id="userService" class="com.kkb.spring.service.UserServiceImpl"></bean>

</beans>

5. 测试类TestSpringIoC.java

public class TestSpringIoC {
	@Test
	public void test1() {
		// 创建ApplicationContext容器
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 方式1:根据Bean的类型,从容器中获取实例
		UserService service1 = context.getBean(UserService.class);
		// 方式2:根据bean的id从容器中获取bean的实例
		UserService service2 = (UserService) context.getBean("userService");
		
		service1.saveUser();
		service2.saveUser();
	}
}

bean标签详解

bean标签作用:
    用于配置对象让 spring 来创建的。
    默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
bean标签属性:
    id:给对象在容器中提供一个唯一标识。用于获取对象。
    class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
    scope:指定对象的作用范围。
        *    singleton :默认值,单例的(在整个容器中只有一个对象).
        *    prototype :多例的.
        *    request    :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
        *    session    :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
        *    global session    :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
    init-method:指定类中的初始化方法名称。
    destroy-method:指定类中销毁方法名称。比如DataSource的配置中一般需要指定destroy-method=“close”。
bean的作用范围:
    单例对象:scope="singleton"
        一个应用只有一个对象的实例。它的作用范围就是整个引用。
        生命周期:
            *对象出生:当应用加载,创建容器时,对象就被创建了。
            *对象活着:只要容器在,对象一直活着。
            *对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
    多例对象:scope="prototype"
        每次访问对象时,都会重新创建对象实例。
        生命周期:
            *对象出生:当使用对象时,创建新的对象实例。
            *对象活着:只要对象在使用中,就一直活着。
            *对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

实例化bean的三种方式

1. 使用默认无参构造函数(重点)

在默认情况下:它会根据默认无参构造函数来创建类对象。
如果 bean 中没有默认无参构造函数,将会创建失败。

<bean id="userService" class="com.spring.service.UserServiceImpl"/>

2. 静态工厂(了解)

/**
* 模拟一个静态工厂,创建业务层实现类
*/
public class StaticFactory {
    public static UserService createUserService(){
        return new UserServiceImpl();
    }
}
<!-- 
    此种方式是:
    使用 StaticFactory 类中的静态方法 createUserService 创建对象,并存入 spring 容器
    id 属性:指定 bean 的 id,用于从容器中获取
    class 属性:指定静态工厂的全限定类名
    factory-method 属性:指定生产对象的静态方法
-->
<bean id="userService" class="com.spring.factory.StaticFactory" factory-method="createUserService"></bean>

3. 实例工厂(了解)

/**
* 模拟一个实例工厂,创建业务层实现类
* 此工厂创建对象,必须现有工厂实例对象,再调用方法
*/
public class InstanceFactory {
    public UserService createUserService(){
        return new UserServiceImpl();
    }
}
<!-- 
    此种方式是:
    先把工厂的创建交给 spring 来管理。
    然后在使用工厂的 bean 来调用里面的方法
    factory-bean 属性:用于指定实例工厂 bean 的 id。
    factory-method 属性:用于指定实例工厂中创建对象的方法。
-->
<bean id="instancFactory" class="com.factory.InstanceFactory"></bean>
<bean id="userService" factory-bean="instancFactory" factory-method="createUserService"></bean>

DI(依赖注入)介绍

一、介绍

什么是依赖?
    依赖指的就是Bean实例中的属性
    属性分为:简单类型(8种基本类型和String类型)的属性、POJO类型的属性、集合数组类型的属性。
什么是依赖注入?
    依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现
为什么要进行依赖注入?
    我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。
    ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。
    简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
 

二、依赖注入的方式(基于XML)

1. 构造函数注入

顾名思义,就是使用类中的构造函数,给成员变量赋值。
注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。
代码如下

public class UserServiceImpl implements UserService {
	private int id;
	private String name;

	public UserServiceImpl(int id, String name) {
		this.id = id;
		this.name = name;
	}

	@Override
	public void saveUser() {
		System.out.println("保存用户:id为" + id + ",name为" + name + "   Service实现");
	}
}

使用构造函数的方式,给 service 中的属性传值要求:类中需要提供一个对应参数列表的构造函数。
    涉及的标签:constructor-arg

  • index:指定参数在构造函数参数列表的索引位置
  • name:指定参数在构造函数中的名称
  • value:它能赋的值是基本数据类型和 String 类型
  • ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
<bean id="userService" class="com.spring.service.UserServiceImpl">
    <constructor-arg name="id" value="1"></constructor-arg>
    <constructor-arg name="name" value="zhangsan"></constructor-arg>
</bean>

2. set方法注入(重点)

set方法注入又分为手动装配方式注入和自动装配方式注入。

  • 手动装配方式(XML方式):bean标签的子标签property,需要在类中指定set方法。
  • 自动装配方式(注解方式,后面讲解):@Autowired注解、@Resource注解。

@Autowired:一部分功能是查找实例,从spring容器中根据类型(java类)获取对应的实例。另一部分功能就是赋值,将找到的实例,装配给另一个实例的属性值。(注意事项:一个java类型在同一个spring容器中,只能有一个实例)

@Resource:一部分功能是查找实例,从spring容器中根据Bean的名称(bean标签的名称)获取对应的实例。另一部分功能就是赋值,将找到的实例,装配给另一个实例的属性值。
 

3. 使用p名称空间注入数据(本质上还是调用set方法,自行了解)

1. 步骤一:需要先引入 p 名称空间
    * 在schema的名称空间中加入该行:xmlns:p="http://www.springframework.org/schema/p"

2. 步骤二:使用p名称空间的语法
    * p:属性名 = ""
    * p:属性名-ref = ""

3. 步骤三:测试
<bean id="person" class="com.spring.demo.Person" p:pname="老王" p:car2-ref="car2"/>
<bean id="car2" class="com.spring.demo.Car2" />

三、依赖注入不同类型的属性(基于XML)

简单类型(value)

<bean id="userService" class="com.spring.service.UserServiceImpl">
    <property name="id" value="1"></property>
    <property name="name" value="zhangsan"></property>
</bean>

引用类型(ref)

ref就是reference的缩写,是引用的意思。

<bean id="userService" class="com.spring.service.UserServiceImpl">
    <property name="userDao" ref="userDao"></constructor-arg>
</bean>
<bean id="userDao" class="com.spring.dao.UserDaoImpl"></bean>

集合类型(数组)

1. 如果是数组或者List集合,注入配置文件的方式是一样的

<!--CollectionBean要加set哦-->
<bean id="collectionBean" class="com.demo.CollectionBean">
    <property name="arrs">
        <list>
            //如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签
            <value>美美</value>
            <value>小风</value>
            <bean></bean>
        </list>
    </property>
</bean>

2. 如果是Set集合,注入的配置文件方式如下

<property name="sets">
    <set>
        <!--如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签-->
        <value>哈哈</value>
        <value>呵呵</value>
    </set>
</property>

3. 如果是Map集合,注入的配置方式如下

<property name="map">
    <map>
        <entry key="老王2" value="38"/>
        <entry key="凤姐" value="38"/>
        <entry key="如花" value="29"/>
    </map>
</property>

4. 如果是Properties集合的方式,注入的配置如下

<property name="pro">
    <props>
        <prop key="uname">root</prop>
        <prop key="pass">123</prop>
    </props>
</property>

IoC和DI基于注解使用

学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用xml 还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
我们在讲解注解配置时,采用上一节的案例,把 spring 的 xml 配置内容改为使用注解逐步实现。

一、Ioc注解的使用

spring配置文件中,配置context:component-scan标签

类上面加上注解@Component,或者它的衍生注解@Controller、@Service、@Repository

//在需要被spring IoC容器管理的类上面加上组件注解
//value:指定该类在容器中的唯一标识,相当于bean标签的id
//默认的bean的id就是类名首字母小写,比如这个类的bean的id是userServiceImpl
@Component(value="userService")
public class UserServiceImpl implements UserService {

二、常用注解

Ioc注解

相当于:<bean id="" class="">

1. @Component注解

作用:
    把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
    value:指定 bean 的 id。
    如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

2. @Controller、@Service、@Repository注解

他们三个注解都是针对@Component的衍生注解
他们的作用及属性都是一模一样的。他们只不过是提供了更加明确的语义化。
    @Controller:一般用于表现层的注解。
    @Service:一般用于业务层的注解。
    @Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。

DI注解(依赖注入)

相当于:<property name="" ref="">

1. @Autowired

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

2. @Qualifier

在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。

3. @Resource

默认按照名称(byName)进行装配,名称可以通过name属性进行指定
这个注解属于J2EE的
如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,当找不到与名称匹配的bean时才按照类型进行装配。
但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

推荐使用@Resource注解,因为这个注解是属于J2EE的,减少了与spring的耦合。

4. @Value

给基本类型和String类型注入值
可以使用占位符获取属性文件中的值。
@Value(“${name}”) //name是properties文件中的key
private String name;

 spring配置文件

<!-- location:表示要加载的properties文件的路径 -->
<context:property-placeholder location="classpath:data.properties"/>

 data.properties

id=123
//简单类型的注入(配合properties文件使用)
@Value("${id}")
private int id;

改变作用范围

1. @Scope

相当于<bean id="" class="" scope="">
作用:
    指定 bean 的作用范围。
属性:
    value:指定范围的值。
    取值:singleton prototype request session globalsession

生命周期相关

相当于:<bean id="" class="" init-method="" destroy-method="" />
@PostConstruct和@PreDestroy

注解和XML的选择问题

注解的优势:
    配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML 的优势:
    修改时,不用改源码。不涉及重新编译和部署。


Spring的纯注解配置

@Configuration

  • 介绍:

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件
    相当于<beans>根标签
    配置类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

  • 属性:

    value:用于指定配置类的字节码

  • 示例代码:
    @Configuration
    public class SpringConfiguration {
        //spring容器初始化时,会调用配置类的无参构造函数
        public SpringConfiguration(){
            System.out.println(“容器启动初始化。。。”);
        }
    }

 

@Bean

  • 介绍:

    @Bean标注在方法上(返回某个实例的方法),等价于spring配置文件中的<bean>
    作用为:注册bean对象
    主要用来配置非自定义的bean,比如DruidDataSource、SqlSessionFactory

  • 属性:

    name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
    如果不指定,默认与标注的方法名相同
     @Bean注解默认作用域为单例singleton作用域,可通过@Scope(“prototype”)设置为原型作用域;

  • 示例代码:
    @Configuration
    public class SpringConfiguration {
        //spring容器初始化时,会调用配置类的无参构造函数
        public SpringConfiguration(){
            System.out.println(“容器启动初始化。。。”);
        }
        @Bean
        @Scope(“prototype”)
        public UserService userService(){
            return new UserServiceImpl(1,“张三”);
        }
    }

 

@ComponentScan

  • 介绍:

    相当于context:component-scan标签
    组件扫描器,扫描@Component、@Controller、@Service、@Repository注解的类。
    该注解是编写在类上面的,一般配合@Configuration注解一起使用。

  • 属性:

    basePackages:用于指定要扫描的包。
    value:和basePackages作用一样。

  • 示例代码:

    Bean类(Service类):

@Service
public class UserServiceImpl implements UserService {

	@Override
	public void saveUser() {
		System.out.println("保存用户   Service实现");
	}

}

   配置类:

@Configuration
@ComponentScan(basePackages="com.spring.service")
public class SpringConfiguration {

	public SpringConfiguration() {
		System.out.println("容器初始化...");
	}
	
//	@Bean
//	@Scope("prototype")
//	public UserService userService() {
//		return new UserServiceImpl(1,"张三");
//	}
}

 

@PropertySource

  • 介绍

    加载properties配置文件
    编写在类上面
    相当于context:property-placeholder标签

  • 属性

    value[]:用于指定properties文件路径,如果在类路径下,需要写上classpath

配置类:

@Configuration
@PropertySource(“classpath:jdbc.properties”)
public class JdbcConfig {
	@Value("${jdbc.driver}")
	private String driver;
	@Value("${jdbc.url}")
	private String url;
	@Value("${jdbc.username}")
	private String username;
	@Value("${jdbc.password}")
	private String password;

	/**
	 * 创建一个数据源,并存入 spring 容器中
	 */
	@Bean(name = "dataSource")
	public DataSource createDataSource() {
		try {
			ComboPooledDataSource ds = new ComboPooledDataSource();
			ds.setDriverClass(driver);
			ds.setJdbcUrl(url);
			ds.setUser(username);
			ds.setPassword(password);
			return ds;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

properties文件:

jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql:///spring
jdbc.username=root 
jdbc.password=root

@Import

  • 介绍

    用来组合多个配置类
    相当于spring配置文件中的import标签
    在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。

  •  属性

    value:用来指定其他配置类的字节码文件

@Configuration
@ComponentScan(basePackages = "com.spring")
@Import({JdbcConfig.class})
public class SpringConfiguration {
}


@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
}

通过注解获取容器

Java应用(AnnotationConfigApplicationContext)

ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService service = context.getBean(UserService.class);
service.saveUser();

Web应用(AnnotationConfigWebApplicationContext,后面讲解)

<web-app>

    <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.spring.test.SpringConfiguration

        </param-value>

    </context-param>

    <listener>

        <listener-class>

            org.springframework.web.context.ContextLoaderListener

        </listener-class>

    </listener>

</web-app>


Spring 分模块开发

分模块开发的场景描述:
    表现层:spring配置文件,只想管理表现层的Bean
    业务层:spring配置文件,只想管理业务层的Bean,并且进行事务控制
    持久层:spring配置文件,只想管理持久层的Bean,并且还有需要管理数据源的Bean
为了方便管理项目中不同层的Bean对象,一般都是将一个spring配置文件,分解为多个spring配置文件。

分解之后的spring配置文件如何一起被加载呢??
    一种就是同时指定多个配置文件的地址一起加载
    另一种就是:定义一个import.xml文件,通过import标签将其他多个spring配置文件导入到该文件中,tomcat启动时只需要加载import.xml就可以。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值