文章目录
第三章、基于注解配置IoC容器
一、基于注解的优势
摆脱繁琐的XML形式的bean与依赖注入配置。
基于“声明式”的原则,更适合轻量级的现代企业应用。
让代码可读性变得更好,研发人员拥有更好的开发体验。
1、三类注解
组件类型注解 - 声明当前类的功能与职责。
自动装配注解 - 根据属性特征自动注入对象。
元数据注解 - 更细化的辅助IoC容器管理对象的注解。
二、四种组件类型注解
1、开启组件扫描
2、代码演示
打开IDEA创建新的maven工程,在pom.xml引入Spring依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.ql.spring</groupId>
<artifactId>ioc_annotation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
</project>
在resources目录下创建Spring配置文件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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository
@Service
@Controller
@Component
-->
<context:component-scan base-package="com.ql.spring"/>
</beans>
在com.ql.spring.ioc.dao包下创建UserDao类
package com.ql.spring.ioc.dao;
import org.springframework.stereotype.Repository;
//组件类型注解默认beanId为类名首字母小写
//beanId = userDao
@Repository
public class UserDao {
}
在com.ql.spring.ioc.service包下创建UserService类
package com.ql.spring.ioc.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}
在com.ql.spring.ioc.controller包下创建UserController类
package com.ql.spring.ioc.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
在com.ql.spring.ioc.utils包下创建StringUtils类
package com.ql.spring.ioc.utils;
import org.springframework.stereotype.Component;
@Component
public class StringUtils {
}
然后com.ql.spring.ioc包下创建入口类SpringApplication执行
package com.ql.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
String[] ids = context.getBeanDefinitionNames();
for (String id: ids) {
System.out.println(id + ":" + context.getBean(id));
}
}
}
/*
userController:com.ql.spring.ioc.controller.UserController@6483f5ae
userDao:com.ql.spring.ioc.dao.UserDao@b9afc07
userService:com.ql.spring.ioc.service.UserService@382db087
stringUtils:com.ql.spring.ioc.utils.StringUtils@73d4cc9e
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@80169cf
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@5427c60c
org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@15bfd87
org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@543e710e
org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@57f23557
*/
三、两类自动装配注解
1、按类型装配@Autowired
修改com.ql.spring.ioc.dao包下的UserDao
package com.ql.spring.ioc.dao;
import org.springframework.stereotype.Repository;
//组件类型注解默认beanId为类名首字母小写
//beanId = userDao
@Repository
public class UserDao {
public UserDao() {
System.out.println("正在创建UserDao:"+this);
}
}
修改com.ql.spring.ioc.service包下的UserService
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserService(){
System.out.println("正在创建UserService:"+this);
}
@Autowired
private UserDao udao;
public UserDao getUdao() {
return udao;
}
public void setUdao(UserDao udao) {
System.out.println("setUdao:"+udao);
this.udao = udao;
}
}
然后修改入口类SpringApplication重新执行
package com.ql.spring.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getUdao());
}
}
/*
正在创建UserDao:com.ql.spring.ioc.dao.UserDao@149494d8
正在创建UserService:com.ql.spring.ioc.service.UserService@710726a3
com.ql.spring.ioc.dao.UserDao@149494d8
*/
从运行结果得知,装配注解放在属性上,不执行set方法。其原理为:Spring IoC容器会自动通过反射技术将属性private修饰符自动修改改为public,直接进行赋值。所以日常开发时如果用注解注入时不会生成set方法。
如果把@Autowired注解放在set方法上再执行,如下:
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserService(){
System.out.println("正在创建UserService:"+this);
}
//@Autowired
private UserDao udao;
public UserDao getUdao() {
return udao;
}
@Autowired
public void setUdao(UserDao udao) {
System.out.println("setUdao:"+udao);
this.udao = udao;
}
}
/*
正在创建UserDao:com.ql.spring.ioc.dao.UserDao@306279ee
正在创建UserService:com.ql.spring.ioc.service.UserService@77846d2c
setUdao:com.ql.spring.ioc.dao.UserDao@306279ee
com.ql.spring.ioc.dao.UserDao@306279ee
*/
得知:如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入。
下面再分析@Autowired注解:
在com.ql.spring.ioc.dao包下创建IUserDao接口
package com.ql.spring.ioc.dao;
public interface IUserDao {
}
然后UserDao实现这个接口
package com.ql.spring.ioc.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao implements IUserDao{
public UserDao() {
System.out.println("正在创建UserDao:"+this);
}
}
在com.ql.spring.ioc.dao包下再创建一个UserOracleDao类也实现IUserDao接口
package com.ql.spring.ioc.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserOracleDao implements IUserDao{
public UserOracleDao() {
System.out.println("正在创建UserOracleDao:"+this);
}
}
此时在UserService中属性注解注入形式,然后运行代码就会报错
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.IUserDao;
import com.ql.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserService(){
System.out.println("正在创建UserService:"+this);
}
@Autowired
private IUserDao udao;
public IUserDao getUdao() {
return udao;
}
}
//NoUniqueBeanDefinitionException: No qualifying bean of type 'com.ql.spring.ioc.dao.IUserDao' available: expected single matching bean but found 2: userDao,userOracleDao
报错的原因为按类型注入时,有两个对象都实现了IUserDao接口,所以不知道该注入哪个bean。
解决方案有几种:
1、去掉两个实现类其中一个类的注解,如去掉UserDao类的@Repository注解。
2、两个实现类其中一个类再加个注解,如UserOracleDao类再加个注解为@Primary,@Primary代表主要的意思,其含义是如果IoC容器中出现了多个相同类型的对象的话,默认采用@Primary所描述的对象完成注入。
3、注入注解选择@Resource按名称装配。
2、按名称装配@Resource
1、@Resource设置name属性,则按name在IoC容器中将bean注入。
2、@Resource未设置name属性。
2.1、以属性名作为bean name在IoC容器中匹配bean,如有匹配则注入。
2.2、按属性名未匹配,则按类型进行匹配,同@Autowired,需加入@Primary解决类型冲突。
使用建议:在使用@Resource对象时推荐设置name或保证属性名与bean名称一致。
在com.ql.spring.ioc.service包下创建DepartmentService
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.IUserDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class DepartmentService {
//@Resource(name="userOracleDao")
//private IUserDao udao;
@Resource
private IUserDao userOracleDao;
public void joinDepartment(){
System.out.println(userOracleDao);
}
}
然后在入口类SpringApplication改为如下再执行
package com.ql.spring.ioc;
import com.ql.spring.ioc.service.DepartmentService;
import com.ql.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getUdao());
DepartmentService departmentService = context.getBean("departmentService", DepartmentService.class);
departmentService.joinDepartment();
/* String[] ids = context.getBeanDefinitionNames();
for (String id: ids) {
System.out.println(id + ":" + context.getBean(id));
}*/
}
}
/*
正在创建UserDao:com.ql.spring.ioc.dao.UserDao@548ad73b
正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@4c762604
正在创建UserService:com.ql.spring.ioc.service.UserService@10d59286
com.ql.spring.ioc.dao.UserOracleDao@4c762604
com.ql.spring.ioc.dao.UserOracleDao@4c762604
*/
如果在DepartmentService中将属性名再改为userDao再运行
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.IUserDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class DepartmentService {
//@Resource(name="userOracleDao")
//private IUserDao udao;
@Resource
private IUserDao userDao;
public void joinDepartment(){
System.out.println(userDao);
}
}
/*
正在创建UserDao:com.ql.spring.ioc.dao.UserDao@548ad73b
正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@4c762604
正在创建UserService:com.ql.spring.ioc.service.UserService@10d59286
com.ql.spring.ioc.dao.UserOracleDao@4c762604
com.ql.spring.ioc.dao.UserDao@548ad73b
*/
四、其他元数据注解
在resources目录添加属性文件config.properties添加内容为
metaData = ql.com
在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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--通过Spring IoC容器初始化时加载属性文件-->
<context:property-placeholder location="classpath:config.properties"/>
<!-- 在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository
@Service
@Controller
@Component
-->
<context:component-scan base-package="com.ql.spring"/>
</beans>
修改UserService类,添加相关注解
package com.ql.spring.ioc.service;
import com.ql.spring.ioc.dao.IUserDao;
import com.ql.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
@Scope("prototype")//设置单例/多例,在XML中bean scope完全相同
public class UserService {
@Value("${metaData}")
private String metaData;
private UserService(){
System.out.println("正在创建UserService:"+this);
}
@PostConstruct//XML中bean init-method完全相同
public void init(){
System.out.println("初始化UserService对象,metaData="+metaData);
}
@Autowired
private IUserDao udao;
public IUserDao getUdao() {
return udao;
}
}
运行入口类SpringApplication.java
package com.ql.spring.ioc;
import com.ql.spring.ioc.service.DepartmentService;
import com.ql.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService.getUdao());
}
}
/*
正在创建UserDao:com.ql.spring.ioc.dao.UserDao@1efee8e7
正在创建UserOracleDao:com.ql.spring.ioc.dao.UserOracleDao@1ee807c6
正在创建UserService:com.ql.spring.ioc.service.UserService@3c0a50da
初始化UserService对象,metaData=ql.com
com.ql.spring.ioc.dao.UserOracleDao@1ee807c6
*/