Spring框架学习

文章40 |   阅读 14976 |   点赞0

来源:https://blog.csdn.net/yerenyuan_pku/category_6457009.html

Spring入门第五讲——Spring IoC的注解开发

x33g5p2x  于2021-12-18 转载在 其他  
字(12.6k)|赞(0)|评价(0)|浏览(342)

还记得在《Spring入门第三讲——Spring中Bean的配置与管理以及分模块开发的配置》这一讲中,我说过Spring进行Bean的管理有两种方式吗?其中一种是使用配置文件的方式,这个我们已经讲过了,还有一种是使用注解的方式,本讲就来讲一下Spring IoC的注解开发。那啥是注解呢?注解是代码中的特殊标记,它可以使用在类、方法、属性上面,使用它可以实现一些基本的功能,其写法是@注解名称(属性=属性值)

Spring IoC注解开发的入门

创建web项目,引入Spring的开发包

首先创建一个动态web项目,例如spring_demo02,然后导入Spring框架相关依赖jar包,要导入哪些jar包呢?在Spring4的版本中,除了要导入Spring基本的开发包以外(可参考《Spring入门第一讲——Spring框架的快速入门》这一讲),还须导入Spring注解的jar包。

创建接口和实现类

首先,在src目录下创建一个com.meimeixia.spring.demo01包,并在该包下创建一个名为UserDao的接口。

package com.meimeixia.spring.demo01;

public interface UserDao {

	public void save();
	
}

然后,在com.meimeixia.spring.demo01包下创建UserDao接口的一个实现类——UserDaoImpl.java。

package com.meimeixia.spring.demo01;

/** * 用户Dao的实现类 * @author liayun * */
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("UserDaoImpl中保存用户的方法执行了......");
	}

}

引入Spring的核心配置文件

在src目录下面创建Spring的核心配置文件(即applicationContext.xml),由于现在我们使用的是注解方式开发,所以还需要在该配置文件中引入context约束,那么问题来了,这个约束又该怎么写呢?可参考docs\spring-framework-reference\html目录下的xsd-configuration.html文件,在其内容中找到如下内容。

将其复制黏贴到配置文件applicationContext.xml中,这样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" 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>

咱要想使用IoC的注解开发,还得配置一个组件扫描,就是告诉Spring哪些包下面的哪些类上要使用IoC注解,扫描是为了扫描类上的注解。

在创建的实现类上面使用注解

编写测试类

在com.meimeixia.spring.demo01包下创建一个SpringDemo01的单元测试类,其内容如下:

package com.meimeixia.spring.demo01;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/** * Spring的IOC的注解开发的测试类 * @author liayun * */
public class SpringDemo01 {
	
	/* * 传统方式 */
	@Test
	public void demo01() {
		UserDao userDao = new UserDaoImpl();
		userDao.save();
	}
	
	/* * Spring的IOC的注解方式 */
	@Test
	public void demo02() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		userDao.save();
	}
	
}

然后,运行以上demo02单元测试方法,Eclipse控制台就会打印出如下内容。

Spring IoC注解开发中的常用注解

@Component(作用在类上)

该注解是用于修饰一个类,即将这个类交给Spring来管理。这个注解有三个衍生注解(功能类似),同样用于修饰类,它们分别是以下三个注解(推荐使用)。

  • @Repository:用于对dao层实现类进行标注(持久层);

  • @Service:用于对service层实现类进行标注(业务层);
  • @Controller:用于对Controller实现类进行标注(web层)。

属性注入的注解

使用注解方式来设置普通属性

我们可以使用@Value这个注解来注入普通属性的值,这里我会举一个例子来演示该注解的使用。现在,如果想要给UserDaoImpl这个实现类里面的某一个属性(例如String类型的name)使用注解方式设置值,那该咋怎呢?使用注解方式来设置属性的值,也有两种方式。

  • 如果类中的属性有set方法,那么你需要将属性注入的注解添加到set方法上;

  • 如果类中的属性没有set方法,那么你需要将属性注入的注解添加到属性上。

下面,我将UserDaoImpl这个实现类修改成下面这个样子。

package com.meimeixia.spring.demo01;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/** * 用户Dao的实现类 * @author liayun * */
@Component("userDao")//写了这个注解之后,它就相当于在applicationContext.xml里面配置了<bean id="userDao" class="com.meimeixia.spring.demo01.UserDaoImpl" />
public class UserDaoImpl implements UserDao {
	
	//使用注解方式来设置属性的值
	@Value("狄仁杰")
	private String name;
	
// @Value("狄仁杰")
// public void setName(String name) {
// this.name = name;
// }

	@Override
	public void save() {
		System.out.println("UserDaoImpl中保存用户的方法执行了......" + name);
	}

}

此时,Spring配置文件不用修改,然后运行SpringDemo01单元测试类中demo02方法,你就会看到Eclipse控制台打印出了如下内容。

使用注解方式来设置对象类型的属性

这里,我会举一个例子来说明如何使用注解方式注入对象类型的属性。首先,在com.meimeixia.spring.demo01包下创建一个名为UserService的接口。

package com.meimeixia.spring.demo01;

public interface UserService {
	public void save();
}

然后,在该包下再创建该接口的一个实现类——UserServiceImpl.java。

package com.meimeixia.spring.demo01;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")//它相当于在applicationContext.xml里面配置了<bean id="userService" class="com.meimeixia.spring.demo01.UserServiceImpl" />
public class UserServiceImpl implements UserService {
	
	//注入UserDao,此时可以没有set方法
	@Autowired
	private UserDao userDao;

	@Override
	public void save() {
		System.out.println("UserServiceImpl中的save方法执行了......");
		userDao.save();
	}

}

从上面可以看到,在UserServiceImpl类里面注入UserDao类的对象时,是使用@Autowired注解来实现的。@Autowired虽说可以设置对象类型的属性的值,但它默认是按照类型来完成属性注入的,咱之前是按照名称来完成属性注入的。
接着,在SpringDemo01的单元测试类中编写如下的一个demo03测试方法。

package com.meimeixia.spring.demo01;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/** * Spring的IOC的注解开发的测试类 * @author liayun * */
public class SpringDemo01 {
	
	/* * 传统方式 */
	@Test
	public void demo01() {
		UserDao userDao = new UserDaoImpl();
		userDao.save();
		
		
// UserDaoImpl userDao = new UserDaoImpl();
// userDao.setName("李元芳");
// userDao.save();
	}
	
	/* * Spring的IOC的注解方式 */
	@Test
	public void demo02() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		userDao.save();
	}
	
	/* * Spring的IOC的注解方式 */
	@Test
	public void demo03() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) applicationContext.getBean("userService");
		userService.save();
	}
	
}

最后,运行以上demo03单元测试方法,你就会发现Eclipse控制台打印了如下内容。

上面我说过@Autowired虽然可以设置对象类型的属性的值,但是它默认是按照类型来完成属性注入的,而我们习惯是按照名称来完成属性注入,所以还必须让@Autowired这个注解和@Qualifier这个注解一起使用,按照名称来完成属性注入。

@Autowired和@Qualifier这2个注解结合一起使用,它有一个替代的注解(即@Resource),我们在实际的开发中,一般使用这个替代的注解,但这个替代的注解本身不是Spring里面提供的,是Spring实现的一套规范里面提供的。这个替代的注解是@Resource,用它可以完成对象类型的属性注入,而且是按照名称来完成属性注入。

与生命周期相关的注解

《Spring入门第三讲——Spring中Bean的配置与管理以及分模块开发的配置》这一讲中,我就已经讲过Bean的生命周期的配置,现在我来用注解的方式再讲一遍。与Bean生命周期相关的注解有以下两个:

  • @PostConstruct:用它来标注Bean被初始化的时候执行的方法;
  • @PreDestroy:用它来标注Bean被销毁的时候执行的方法。

下面我会通过一个案例来演示与Bean生命周期相关的这两个注解。首先,在src目录下创建一个com.meimeixia.spring.demo02包,并在该包下创建一个名为CustomerService的类。

package com.meimeixia.spring.demo02;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Service;

@Service("customerService")
//@Scope("prototype")
public class CustomerService {
	
	//<bean id="customerService" class="..." init-method="init" destroy-method="destroy" />
	@PostConstruct //相当于我们配置的init-method="init"
	public void init() {
		System.out.println("CustomerService被初始化了......");
	}

	public void save() {
		System.out.println("CustomerService中的save方法执行了......");
	}
	
	@PreDestroy //相当于我们配置的destroy-method="destroy"
	public void destroy() {
		System.out.println("CustomerService被销毁了......");
	}
	
}

然后,在com.meimeixia.spring.demo02包下创建一个SpringDemo02的单元测试类。

package com.meimeixia.spring.demo02;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo02 {

	@Test
	public void demo01() {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		CustomerService customerService = (CustomerService) applicationContext.getBean("customerService");
		System.out.println(customerService);
		applicationContext.close();//此时,CustomerService这个类是被单例创建的,当工厂类被关闭时,被@PreDestroy注解标注的销毁方法就能被执行了。
	}
	
}

接着,还要将以上类交给Spring来管理,所以咱们得告诉Spring扫描com.meimeixia.spring.demo02这个包下面的类。

当然了,我们还可以像下面这样配置,即告诉Spring扫描com.meimeixia.spring.demo01和com.meimeixia.spring.demo02的父包——com.meimeixia.spring。

最后,运行SpringDemo02单元测试类中的demo01方法,你就会看到Eclipse控制台打印出了如下内容。

之前我就说过,要想Bean被销毁的时候执行@PreDestroy注解标注的销毁方法,那么前提Bean得是单例创建的,默认即单例创建,就像下面这样。

如果Bean改为了多例模式创建,就像下面这样。

那么当工厂类被关闭时,@PreDestroy注解标注的销毁方法也就不会被执行了。你可以试着运行一下demo01测试方法,你会发现Eclipse控制台打印出了如下内容。

与Bean作用范围相关的注解

@Scope就是与Bean作用范围相关的注解,它的取值咱之前就已经讲过了,这里不再赘述,重点关注singleton和prototype这两个取值。

@Scope(“singleton”)

在我们把CustomerService类交给Spring来管理时,明确该Bean的@Scope注解取值为singleton。

接着,在SpringDemo02的单元测试类中编写如下的一个demo02测试方法。

package com.meimeixia.spring.demo02;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo02 {

	@Test
	public void demo01() {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		CustomerService customerService = (CustomerService) applicationContext.getBean("customerService");
		System.out.println(customerService);
		applicationContext.close();//此时,CustomerService这个类是被单例创建的,当工厂类被关闭时,被@PreDestroy注解标注的销毁方法就能被执行了。
	}
	
	@Test
	public void demo02() {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		CustomerService customerService1 = (CustomerService) applicationContext.getBean("customerService");
		System.out.println(customerService1);
		
		CustomerService customerService2 = (CustomerService) applicationContext.getBean("customerService");
		System.out.println(customerService2);
		
		applicationContext.close();
	}
	
}

紧接着,运行以上demo02单元测试方法,你就会发现Eclipse控制台打印了如下内容。

验证了Bean默认就是单例创建的。

@Scope(“prototype”)

如果把CustomerService类交给Spring来管理时,明确该Bean的@Scope注解取值为prototype。

那么此时运行以上demo02单元测试方法,你就会发现Eclipse控制台打印了如下内容。

从上图中,我们就能看出Bean现在是多例模式创建的了。而且工厂类关闭之后,它也没被销毁,因为现在内存里面是有很多个对象,它不知道要销毁哪一个具体的CustomerService类的对象,所以,要想工厂类被关闭之后,Bean被销毁(此时将会执行@PreDestroy注解标注的销毁方法),那么Bean得是单例创建的(默认即单例创建)!

IoC的XML开发方式和注解开发方式的比较

这里,我会对IoC的XML开发方式和注解开发方式做一个简单的比较。

  • XML开发方式:可以适用于任何场景,而且结构清晰,便于后期维护;
  • 注解开发方式:有些地方用不了,比方说这个类不是自己提供的(可能是人家源码当中的一个类),但开发方便。

有时候,这两种方式都会使用,即XML文件和注解的整合开发,在这种整合开发中,我们会使用XML文件来管理Bean,使用注解完成属性注入。下面我会通过一个案例来演示XML文件和注解的整合开发。
首先,在src目录下创建一个com.meimeixia.spring.demo03包,并在该包下创建一个名为OrderDao的类和一个名为ProductDao的类。

  • OrderDao.java
package com.meimeixia.spring.demo03;

public class OrderDao {

	public void save() {
		System.out.println("OrderDao中的save方法执行了......");
	}
	
}
  • ProductDao.java
package com.meimeixia.spring.demo03;

public class ProductDao {

	public void save() {
		System.out.println("ProductDao中的save方法执行了......");
	}
	
}

然后,在com.meimeixia.spring.demo03包下创建一个ProductService类。

package com.meimeixia.spring.demo03;

import javax.annotation.Resource;

public class ProductService {
	
	@Resource(name="productDao")
	private ProductDao productDao;
	@Resource(name="orderDao")
	private OrderDao orderDao;
	
// public void setProductDao(ProductDao productDao) {
// this.productDao = productDao;
// }

// public void setOrderDao(OrderDao orderDao) {
// this.orderDao = orderDao;
// }

	public void save() {
		System.out.println("ProductService中的save方法执行了......");
		productDao.save();
		orderDao.save();
	}
	
}

从以上ProductService类的代码中,我们可以看到使用注解完成了属性注入。
接着,咱要将以上三个类交给Spring来管理,即使用XML文件来管理Bean。现在,我们只是运行com.meimeixia.spring.demo03包下的类,所以咱可以不用开启组件扫描,组件扫描是为了扫描类上的注解,但我们现在的类上是没有注解的,那么咱必须得开启另外一个东西,即<context:annotation-config />。一旦你在Spring配置文件里面写了这个东西,那么这时就可以在没有组件扫描的情况下,来使用那些属性注入的注解,也就是说可以直接使用@Resource、@Value、@Autowired、@Qualifier这些注解了。

<?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">
        
	<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~Spring的IOC的注解的入门~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
	<!-- 其实我们只运行com.meimeixia.spring.demo03包下的类的话,可以不用扫描,但是你得开个别的东西。 扫描是为了扫描类上的注解,但我们现在的类上是没有注解的。 那么你现在到这里面必须得开启另外一个东西。 <context:annotation-config />:这时可以在没有扫描的情况下,来使用那些属性注入的注解。也即可以直接使用@Resource、@Value、@Autowired、@Qualifier这些注解。 -->
	<context:annotation-config />
	
	<bean id="productService" class="com.meimeixia.spring.demo03.ProductService">
		<!-- <property name="productDao" ref="productDao" /> <property name="orderDao" ref="orderDao" /> -->
	</bean>
	
	<bean id="productDao" class="com.meimeixia.spring.demo03.ProductDao"></bean>
	<bean id="orderDao" class="com.meimeixia.spring.demo03.OrderDao"></bean>
	
</beans>

紧接着,在com.meimeixia.spring.demo03包下创建一个SpringDemo03的单元测试类,其内容如下:

package com.meimeixia.spring.demo03;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/** * XML文件和注解的整合开发 * @author liayun * */
public class SpringDemo03 {

	@Test
	public void demo01() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		ProductService productService = (ProductService) applicationContext.getBean("productService");
		productService.save();
	}
	
}

最后,运行以上demo01单元测试方法,Eclipse控制台就会打印出如下内容。

相关文章