基于注解的IOC和DI(下篇)

说明:由于能更好的说明IOC,本文章基于Spring 4.2.4版本。

此篇为基于注解的IOC的下篇,有关于上篇请看Spring入门到精通之基于XML的IOC(上篇)

从上篇可以看到基于XML的方式还是比较繁琐的,感觉就是在面向XML编程……因此Spring框架的大佬也考虑到这一点,因此开发出基于注解的方式来配置,接下来看下注解是怎么定义的吧!

一、IOC基于注解

// value参数可以省略,默认是类的短名称,开头字母小写。
@Component(value = "customerService")
public class CustomerServiceImpl implements CustomerService {
	// 
}

上述代码等同于XML中的定义

<bean id="customerService" class="com.youngledo.service.impl.CustomerServiceImpl"></bean>

由代码可知就是增加了一个叫@Component的注解,这个注解就相当于在商品中配置的<bean id="customerService" class="com.youngledo.service.impl.CustomerServiceImpl"></bean>,这样是不是简单多了?但从@Component单词翻译过来就是组件的意思,为了更好的区分不同层次的业务,因此Spring框架又定义了这些注解:

  • @Controller:代表控制层
  • @Service:代表业务层
  • @Reponsitory:代表数据层

@Component你可以用在其它层面。

接下来获取以下对象看看:

	// 1. 实例化Spring上下文对象,并使用上述的bean.xml文件来加载。加载的同时会使用文件中定义好的bean来创建对象
	ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
	// 2. 根据bean的id获取对象
	CustomerService customerService = (CustomerService)ac.getBean("customerService");

运行之后,结果Spring抛出了NoSuchBeanDefinitionException异常,原因是因为Spring框架找不到这个被注解标注的Bean对象。这又是怎么回事,难道配置有误?其实不是,原因是因为这样只是声明了该类是一个可以被Spring框架认识的Bean类,但要想被Spring框架加载,还得配置一下Spring能找到的路径,在XML配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
		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框架在创建容器(Bean容器)时要扫描的包名,配置完之后Spring框架就会去找该包下被上述注解标记的类,然后完成创建。

		注意:该标签需要导入context相关的命名空间
	 -->
	<context:component-scan base-package="com.youngledo"></context:component-scan>
</beans>

配置完后,重新运行即可。

从上述可以看出,最后在加载的时候还是少不了需要在XML中配置,并且这还只是声明了一个@Component,如果还需要配置其它的功能(比如事务、数据源等等)还得在这里面配置,这样看来是不是觉得还是抛弃不了XML,还是很繁琐?是的,没错,所以Spring框架大佬也考虑到了这一点,又新增了一个新的注解来替代context:component-scan标签即@ComponentScan。但如果想彻底或者说更方便的使用Spring框架,可以使用Spring大佬新起的神器,那就是——Spring Boot。不过Spring Boot也是基于Spring的,因此掌握好Spring原始的配置,非常利于掌握、理解Spring Boot,包括后续的Spring Cloud。

二、基于注解的注入

关于XML的注入方式,请看上篇的基于XML的注入,接下来看下使用注解是怎么做的。

2.1. @Autowired

自动按照数据类型注入。

@Service
public class CustomerServiceImpl implements CustomerService {
	@Autowired
	private CustomerDao customerDao;
}

@Reponsitory("customerDao")
public class CustomerDaoImpl implements CustomerDao {
	// 第一个
}

注意点如下:

  1. 只要Spring容器中有唯一类型匹配的就能注入成功;
  2. 如果注入的Bean在容器中类型不唯一,则会把变量名称作为bean的id,在容器中查找,找到即注入成功。
    比如上述代码中如果CustomerDao接口有多个实现类CustomerDaoImplCustomerDaoImpl2,并且给它们定义了不同的id名,因此CustomerServiceImpl一样可以把CustomerDaoImpl注入成功。
    @Reponsitory("customerDao")
    public class CustomerDaoImpl implements CustomerDao {
    	// 第一个
    }	
    @Reponsitory("customerDao2")
    public class CustomerDaoImpl2 implements CustomerDao {
    	// 第二个
    }
    

只要不能满足上述两点,则代表注入失败,Spring框架抛出异常。

2.2. @Qualifier

由上可知,@Autowired有个弊端就是无法直接指定id的名称,那么有没有一种注解可以指定id名称呢?它就是@Qualifier!但该注解在给类成员注入时不能单独使用,如下:

	@Qualifier("customerDao2")
	@Autowired
	private CustomerDao customerDao;

不过在给方法的形参注入时则可以独立使用:

	public void setCustomerDao(@Qualifier("customerDao2") CustomerDao customerDao) {
		this.customerDao = customerDao;
	}

2.3. @Resource

介绍了上面两种注解可以互相搭配使用,但是是不是觉得如果在需要指定id名称注入时显得很啰嗦?那么Java原生自带的注解就可以使用了:

	@Resource(name = "customerDao2")
	private CustomerDao customerDao;

方便快捷,需要注意的是使用name属性来定义的。

2.4. @Value

用于注入基本数据类型String类型的数据。类似的注解还有@ConfigurationProperties@PropertySource,因介绍的内容不一样,具体可参考https://blog.csdn.net/wangmx1993328/article/details/81005170

	@Value(name = "养乐多")
	private String name;

@Value注解还可以使用spEL表达式读取properties或yaml文件中的配置。

server.port=2020
# 或者yaml格式
server:
  port: 1990
	@Value(name = "${server.port}")
	private Integer port;

总结:以上3个注解都是用于注入其它Bean(即被@Component、@Controller、@Service、@Repository这4个注解标记的类,以及在XML中配置的标签的类。而基本类型和String类型的注入则只能使用@Value注解来做,至于类似集合这种复杂类型Spring并没有提供相应的注解。

2.5. @Scope

用于改变Bean的作用范围,取值与XML中的scope属性一致,默认值为singleton

三、彻底摆脱XML

不管从上篇还是本篇的前两节来看,始终摆脱不了XML的配置,那么有没有一种方式完全不使用XML呢,全部使用注解的方式呢?答案是有的。不过在使用注解之前,我们先思考一个问题:假如我们要使用第三方定义的类,或者我自定义的类的参数需要用到第三方的类,那该如何配置呢?为了更好的描述问题,还是以上篇的XML中定义的示例来说明:

	<bean id="customerService" class="com.youngledo.service.impl.CustomerServiceImpl">
		<constructor-arg name="host" value="localhost"></constructor-arg>
		<constructor-arg name="port" value="8080"></constructor-arg>
		<!-- 使用下面定义的Bean对象,通过ref来赋值 -->
		<constructor-arg name="date" ref="now"></constructor-arg>
	</bean>
	
	<!-- 配置Date类的实例,会去调用Date类的默认构造方法 -->
	<bean id="now" class="java.util.Date"></bean>

该实例中host和port分别是字符串和整型,这个都好说能很方便的注入,但是now却是java.util.Date的实例,上述可以使用Spring提供的bean标签来配置,并且调用java.util.Date默认构造方法来获取实例的,如果是用注解呢?

3.1. @Bean

该注解等同于XML中的bean标签,并且该注解的属性name可以指定bean的id,不指定默认是方法名首字母小写。

@Configuration
public class BeanConfiguration {
	
	@Bean
	public Date now() {
		return new Date();
	}

}

对比上述的<bean id="now" class="java.util.Date"></bean>,区别就在于定义方式不一样。

这种还是最简单的方式,如果该bean还需要依赖其它的bean又该如何定义呢?

/**
 * 自定义一个配置类,并非Spring官方配置
 */
@Configuration
public class JdbcConfiguration {
	
	/**
	 * QueryRunner需要依赖DataSource实例,DataSource实例也需要进行注入。
	 */
	@Bean(name = "runner"
	public QueryRunner queryRunner(DataSource dataSource) {
		return new QueryRunner(dataSource);
	}

	@Bean
	public DataSource dataSource() {
		ComboPooledDateSource dataSource = new ComboPooledDateSource();
		// 省略中间的参数设置和异常处理
		return dataSource;
	}
}

由此可见,如果有依赖关系,都可以使用这种方式来定义。

3.2. @Configuration

但需要注意的是使用使用@Bean注解的前提是该类必须被@Configuration标注(当然这个类还可以与ClassPathXmlApplicationContext配合使用,来指定配置类),表示当前类是Spring框架的配置类,也相当于上篇的XML的bean.xml的配置。而要想完全不使用xml的配置则需要使用AnnotationConfigApplicationContext来加载Spring容器。

接下来看下如果使用注解标注的对象:

	// 注意使用的是AnnotationConfigApplicationContext类
	ApplicationContext ac = new AnnotationConfigApplicationContext(BeanConfiguration.class);
	QueryRunner runner = (QueryRunner)ac.getBean("runner");

3.3. @Import

在讲解该注解之前,我们先要弄清楚一个事情,那就是如果我们有很多类似的@Configuration配置类,但这些类并非Spring官方的配置,只不过是我们为了业务的需要定义的配置类,但我并不想或者完全没必要让该类成为Spring容器的实例,只想让该配置类中被@Bean标注的对象成为Spring容器的实例。或者说该配置类仅仅提供了Spring容器加载的Bean对象,而没有其它普通方法时,我们没必要在类上使用类似@Configuration或者@Component的注解。那该如何做呢?

/**
 * 自定义一个配置类,注意没有使用@Configuration来标注
 */
public class JdbcConfiguration {
	/**
	 * QueryRunner需要依赖DataSource实例,DataSource实例也需要进行注入。
	 */
	@Bean(name = "runner"
	public QueryRunner queryRunner(DataSource dataSource) {
		return new QueryRunner(dataSource);
	}

	@Bean
	public DataSource dataSource() {
		ComboPooledDateSource dataSource = new ComboPooledDateSource();
		// 省略中间的参数设置和异常处理
		return dataSource;
	}
}

再来定义一个统一的配置类

/**
 * 注意我们在这个类中使用@Import注解来统一导入其它配置类,
 */
@ComponentScan({"com.youngledo"})
@Import({JdbcConfiguration.class, RedisConfiguration.class})
@Configuration
public class GlobalConfiguration {
}

说明,但一般来说使用该注解有个麻烦的事就是只要新增了一个配置类,那么就需要在上述代码中增加一个,显得有些麻烦,所以大家一般直接使用@Configuration注解了事了。

四、总结

由此可见@Component以及@Bean这两个注解都可以让类称为Spring容器中的实例,那么它们到底有何区别呢,或者说在什么时候用呢?

  1. @Component只能作用在类上,而@Bean只能作用在方法和注解上。
  2. @Component一般来说用在我们自定义的类上,而像有的第三方类并没有使用@Component来标注,但你也想把它放在Spring容器中方便后续的使用,那么就可以使用@Bean来标注。

当然要想彻彻底底的完全抛弃XML的配置方式,说白了就是完全使用注解的方式来做了,因此随着Spring的发展,Spring Boot应运而生了,因此在学习Spring Boot之前还是很有必要学习Spring相关的知识的,否则你就是基于应用层开发不知背后的原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

养-乐多

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

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

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

打赏作者

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

抵扣说明:

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

余额充值