|
Spring IoC -- 控制反转
- IOC控制反转,是一种设计理念
- 由代理人来创建与管理对象,消费者通过代理人来获取对象
- IOC的目的是降低对象之间直接耦合
- 加入IOC容器将对象统一管理,让对象关系变为弱耦合
- 用于统一创建与管理对象依赖
DI依赖注入
- IOC是设计理念,是现代程序设计遵循的标准,是宏观目标
- DI是具体技术实现,是微观实现
- DI在Java中利用反射技术实现对象注入
狭义的Spring框架
- Spring框架是企业开发复杂性的一站式解决方案
- Spring框架的核心是IOC容器与AOP面向切面编程
- Spring IOC负责创建与管理系统对象,并在此基础上扩展功能
Spring IoC容器职责
- 对象的控制权交由第三方统一管理(IoC控制反转)
- 利用Java反射技术实现运行时对象创建与关联(DI依赖注入)
- 基于配置提高应用程序的可维护性与扩展性
Spring IoC初体验
1.加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>2.在resource 下新建applicationContext.xml配置文件
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<!--bean标签-用于实例化Java对象的标签-->
<!--在IoC容器启动时,自动由Spring实例化Apple对象,取名sweetApple放入到容器中-->
<bean id=&#34;sweetApple&#34; class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<!--设置对象对应的属性-->
<property name=&#34;title&#34; value=&#34;红富士&#34;></property>
<property name=&#34;origin&#34; value=&#34;欧洲&#34;></property>
<property name=&#34;color&#34; value=&#34;红色&#34;></property>
</bean>
<bean id=&#34;sourApple&#34; class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<property name=&#34;title&#34; value=&#34;青苹果&#34;></property>
<property name=&#34;origin&#34; value=&#34;中亚&#34;></property>
<property name=&#34;color&#34; value=&#34;绿色&#34;></property>
</bean>
<bean id=&#34;softApple&#34; class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<property name=&#34;title&#34; value=&#34;金帅&#34;></property>
<property name=&#34;origin&#34; value=&#34;中国&#34;></property>
<property name=&#34;color&#34; value=&#34;黄色&#34;></property>
</bean>
<bean id=&#34;lily&#34; class=&#34;com.imooc.spring.ioc.entity.Child&#34;>
<property name=&#34;name&#34; value=&#34;莉莉&#34;/>
<property name=&#34;apple&#34; ref=&#34;sweetApple&#34;/>
</bean>
<bean id=&#34;andy&#34; class=&#34;com.imooc.spring.ioc.entity.Child&#34;>
<property name=&#34;name&#34; value=&#34;安迪&#34;/>
<property name=&#34;apple&#34; ref=&#34;sourApple&#34;/>
</bean>
<bean id=&#34;luna&#34; class=&#34;com.imooc.spring.ioc.entity.Child&#34;>
<property name=&#34;name&#34; value=&#34;露娜&#34;/>
<property name=&#34;apple&#34; ref=&#34;softApple&#34;/>
</bean>
</beans>3.创建Spring Application类
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.entity.Apple;
import com.imooc.spring.ioc.entity.Child;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author R.Yu
*/
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;);
// 从IoC容器中提取beanId=sweetApple的对象
Apple sweetApple = context.getBean(&#34;sweetApple&#34;, Apple.class);
System.out.println(sweetApple.getTitle());
Child lily = context.getBean(&#34;lily&#34;, Child.class);
Child andy = context.getBean(&#34;andy&#34;, Child.class);
Child luna = context.getBean(&#34;luna&#34;, Child.class);
lily.eat();
andy.eat();
luna.eat();
}
}XML管理对象(Bean)
1.基于XML配置Bean
实例化Bean的三种方式
- 基于构造方法对象实例化
- 基于静态工厂实例化
- 基于工厂实例方法实例化
1.基于构造方法对象实例化
1.利用构造方法参数名实例化
<bean id=&#34;apple&#34; class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<constructor-arg name=&#34;title&#34; value=&#34;红富士&#34;/>
<constructor-arg name=&#34;color&#34; value=&#34;红色&#34;/>
<constructor-arg name=&#34;origin&#34; value=&#34;欧洲&#34;/>
</bean>2.利用构造方法参数位置实例化<不推荐>
<bean id=&#34;apple3&#34; class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<constructor-arg index=&#34;0&#34; value=&#34;红富士&#34;/>
<constructor-arg index=&#34;1&#34; value=&#34;红色&#34;/>
<constructor-arg index=&#34;2&#34; value=&#34;欧洲&#34;/>
</bean>2.基于静态工厂实例化
1.创建工厂
package com.imooc.spring.ioc.factory;
import com.imooc.spring.ioc.entity.Apple;
/**
* 静态工厂通过静态方法创建对象,隐藏创建对象的细节
* @author R.Yu
*/
public class AppleStaticFactory {
public static Apple createSweetApple() {
Apple apple = new Apple();
apple.setTitle(&#34;红富士&#34;);
apple.setOrigin(&#34;欧洲&#34;);
apple.setColor(&#34;红色&#34;);
return apple;
}
}2.配置xml文件
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<!--利用静态工厂获取对象-->
<bean id=&#34;apple4&#34; class=&#34;com.imooc.spring.ioc.factory.AppleStaticFactory&#34;
factory-method=&#34;createSweetApple&#34;/>
</beans>3.基于工厂实例方法实例化
1.创建工厂实例方法类
package com.imooc.spring.ioc.factory;
import com.imooc.spring.ioc.entity.Apple;
/**
* 工厂实例方法创建对象是指IoC容器对工厂类进行实例化并调用对应的实例方法创建对象的过程
* @author R.Yu
*/
public class AppleFactoryInstance {
public Apple createSweetApple() {
Apple apple = new Apple();
apple.setTitle(&#34;红富士&#34;);
apple.setOrigin(&#34;欧洲&#34;);
apple.setColor(&#34;红色&#34;);
return apple;
}
}2.配置xml文件
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<!--利用工厂实例方法获取对象-->
<bean id=&#34;factoryInstance&#34; class=&#34;com.imooc.spring.ioc.factory.AppleFactoryInstance&#34;/>
<bean id=&#34;apple5&#34; factory-bean=&#34;factoryInstance&#34; factory-method=&#34;createSweetApple&#34;/>
</beans>从IoC容器中获取Bean
id与name属性相同点
- bean id与name都是设置对象在IoC容器中唯一标识
- 两者在同一个配置文件中都不允许出现重复
- 两者允许在多个配置文件中出现重复,新对象覆盖旧对象(新旧对象取决于配置文件的加载顺序)
id与name属性区别
- id要求更为严格,一次只能定义一个对象标识(推荐)
- name更为宽松,一次允许定义多个对象标识,用逗号隔开
- tips:id与name的命名要求有意义,按驼峰命名书写
没有id与name的bean默认使用类名全称作为bean标识
<bean class=&#34;com.imooc.spring.ioc.entity.Apple&#34;>
<constructor-arg name=&#34;title&#34; value=&#34;红富士&#34;/>
<constructor-arg name=&#34;color&#34; value=&#34;红色&#34;/>
<constructor-arg name=&#34;origin&#34; value=&#34;欧洲&#34;/>
</bean>
Apple apple = context.getBean(&#34;com.imooc.spring.ioc.entity.Apple&#34;,Apple.class);路径表达式用法
ApplicationContext context = new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;)
String[] configLocations = new String[]{&#34;classpath:applicationContext.xml&#34;,&#34;classpath:applicationContext-1.xml&#34;};
ApplicationContext context = new ClassPathXmlApplication(configLocations);路径表达式
表达式实例 | 说明 | classpath:config.xml | 扫描classpath根路径(不包含jar)的config.xml | classpath:com/imooc/config.xml | 扫描classpath下(不包含jar)com.imooc包中的config.xml | classpath*:com/imooc/config.xml | 扫描classpath下(包含jar)com.imooc包中的config.xml | classpath:config-*.xml | 扫描classpath根路径下所有以config-开头的xml文件 | classpath:com/**/config.xml | 扫描com包下(包含任何子包)的config.xml | file:c:/config.xml | 扫描C盘根路径config.xml | 对象依赖注入
- 依赖注入是指运行时将容器内对象利用反射赋给其他对象的操作
- 基于setter方法注入对象(bean property)
- 基于构造方法注入对象(bean constructor-arg)
注入集合对象
准备java实体类
package com.imooc.spring.ioc.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author R.Yu
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
private Set<String> rooms;
private Map<String,Computer> computers;
private Properties info;
}
package com.imooc.spring.ioc.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author R.Yu
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Computer {
private String brand;
private String type;
private String sn;
private Float price;
}
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<bean id=&#34;c1&#34; class=&#34;com.imooc.spring.ioc.entity.Computer&#34;>
<constructor-arg name=&#34;brand&#34; value=&#34;联想&#34;/>
<constructor-arg name=&#34;type&#34; value=&#34;台式机&#34;/>
<constructor-arg name=&#34;sn&#34; value=&#34;1234232&#34;/>
<constructor-arg name=&#34;price&#34; value=&#34;5929&#34;/>
</bean>
<bean id=&#34;company&#34; class=&#34;com.imooc.spring.ioc.entity.Company&#34;>
<property name=&#34;rooms&#34;>
<set>
<value>2001-总裁办</value>
<value>2003-总经理办公室</value>
<value>2010-研发部会议室</value>
<value>2010-研发部会议室</value>
</set>
</property>
<property name=&#34;computers&#34;>
<map>
<entry key=&#34;dev-88172&#34; value-ref=&#34;c1&#34;/>
<!--也可以这样写-->
<entry key=&#34;dev-88173&#34;>
<bean class=&#34;com.imooc.spring.ioc.entity.Computer&#34;>
<constructor-arg name=&#34;brand&#34; value=&#34;联想&#34;/>
<constructor-arg name=&#34;type&#34; value=&#34;笔记本&#34;/>
<constructor-arg name=&#34;sn&#34; value=&#34;234234232&#34;/>
<constructor-arg name=&#34;price&#34; value=&#34;7989&#34;/>
</bean>
</entry>
</map>
</property>
<property name=&#34;info&#34;>
<props>
<prop key=&#34;phone&#34;>010-2343123</prop>
<prop key=&#34;address&#34;>北京市朝阳区XXX路XX号</prop>
<prop key=&#34;website&#34;>http://www.xxx.com</prop>
</props>
</property>
</bean>
</beans>
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;);
Company company = context.getBean(&#34;company&#34;, Company.class);
System.out.println(company);
String phone = company.getInfo().getProperty(&#34;phone&#34;);
System.out.println(&#34;phone = &#34; + phone);
}
}查看容器类对象
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;);
// 获取容器内所有beanId数组
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
System.out.println(&#34;类型:&#34; + context.getBean(beanName).getClass().getName());
System.out.println(&#34;内容:&#34; + context.getBean(beanName));
}
}
}bean scope属性
- bean scope属性用于决定对象何时被创建与作用范围
- bean scope配置将影响容器内对象的数量
- 默认情况下bean会在IoC容器创建后自动实例化,全局唯一
属性清单
scope属性 | 说明 | singleton | 单例(默认值),每一个容器有且只有唯一的实例,实例被全局共享 | prototype | 多例,每次使用时都是创建一个实例 | request | web环境下,每一次独立请求存在唯一实例 | session | web环境下,每一个session存在有唯一实例 | global session | portlet的web应用的共享session中 | websocket | 每一次WebSocket连接中存在唯一实例 | singleton在容器是单例多线程执行,存在线程安全风险
prototype在容器中多实例,占用更多 资源,不存在线程安全问题
<bean id=&#34;...&#34; class=&#34;...&#34; scope=&#34;prototype&#34;>...</bean>singleton与prototype对比
singleton | prototype | 对象数量 | 全局唯一 | 存在多个 | 实例化时机 | IoC容器启动时 | getBean()或对象注入时 | 线程安全问题 | 存在 | 不存在 | 执行效率 | 高 | 低 | bean的生命周期
- IoC容器准备初始化解析XML (容器行为)
- 对象实例化执行构造方法(对象行为)
- IoC容器为对象注入属性(容器行为)
- 调用init-method初始化方法(对象行为)
- IoC容器初始化完毕(容器行为)
- 执行业务代码(对象行为)
- IoC容器准备销毁(容器行为)
- 调用destroy-method释放资源(对象行为)
- IoC容器销毁完毕(容器行为)
创建Java类
package com.imooc.spring.ioc.entity;
public class Order {
private Float price;
private Integer quantity;
private Float total;
public Order(){
System.out.println(&#34;创建Order对象,&#34; + this);
}
public void init(){
System.out.println(&#34;执行init()方法&#34;);
total = price * quantity;
}
public void pay(){
System.out.println(&#34;订单金额为:&#34; + total);
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
System.out.println(&#34;设置price:&#34; + price);
this.price = price;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
System.out.println(&#34;设置quantity:&#34; + quantity);
this.quantity = quantity;
}
public Float getTotal() {
return total;
}
public void setTotal(Float total) {
this.total = total;
}
public void destroy(){
System.out.println(&#34;释放与订单对象相关的资源&#34;);
}
}配置Bean
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<bean id=&#34;order1&#34; class=&#34;com.imooc.spring.ioc.entity.Order&#34; init-method=&#34;init&#34; destroy-method=&#34;destroy&#34;>
<property name=&#34;price&#34; value=&#34;19.8&#34;/>
<property name=&#34;quantity&#34; value=&#34;1000&#34;/>
</bean>
</beans>
测试
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.entity.Order;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;);
System.out.println(&#34;======IoC容器已初始化=======&#34;);
Order order1 = context.getBean(&#34;order1&#34;, Order.class);
order1.pay();
context.registerShutdownHook();
}
}
运行结果如下:
创建Order对象,com.imooc.spring.ioc.entity.Order@52af6cff
设置price:19.8
设置quantity:1000
执行init()方法
======IoC容器已初始化=======
订单金额为:19800.0
释放与订单对象相关的资源实现极简IoC容器
引入依赖
<!--Dom4j是Java的XML解析组件 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<versino>2.1.1</versino>
</dependency>
<!-- Jaxen是Xpath表达式解释器 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<versino>1.1.1</versino>
</dependency>创建容器接口
public interface ApplicationContext {
public Object getBean(Stirng beanId);
}创建对象
public class ClassPathXmlApplication implements ApplicationContext {
private Map iocContainer = new HashMap();
public ClassPathXmlApplication() {
try {
String filePath = this.getClass().getResource(&#34;/applicationContext.xml&#34;).getPath();
filePath = new URLDecoder().decode(filePath, &#34;UTF-8&#34;);
SAXReader reader = new SAXReader();
Document document = reader.read(new File(filePath));
List<Node> beans = document.getRootElement().selectNodes(&#34;bean&#34;);
for(Node node : beans) {
Element ele = (Element)node;
String id = ele.attributeValue(&#34;id&#34;);
String className = ele.attributeValue(&#34;class&#34;);
Class c = Class.forName(className);
Object obj = c.newInstance();
List<Node> properties = ele.selectNodes(&#34;property&#34;);
for(Node p : properties) {
Element property = (Element) p;
String propName = property.attributeValue(&#34;name&#34;);
String propValue = property.attributeValue(&#34;value&#34;);
String setMethodName = &#34;set&#34; + propName.substring(0,1).toUpperCase() + propName.substring(1);
Method setMethod = c.getMethod(setMethodName, String.class);
setMethod.invoke(obj, propValue); // 通过setter方法注入数据
}
iocContainer.put(id, obj);
}
System.out.println(&#34;IoC容器初始化完毕&#34;);
}catch (Exception e) {
e.printStackTrace();
}
}
public Objext getBean(String beanId) {
return iocContainer.get(beanId);
}
}
2.基于注解配置Bean
四种组件类型注解
注解 | 说明 | @Component | 组件注解,通用注解,被该注解描述的类将被IoC容器管理并实例化 | @Controller | 语义注解,说明当前类是MVC应用中的控制器类 | @Service | 语义注解,说明当前类是Service业务服务类 | @Repository | 语义注解,说明当前类用于业务持久层,通常描述对应Dao类 | 开启组件扫描
<!--XML配置开启组件扫描,才能使用注解-->
<context:component-scan base-package=&#34;com.imooc&#34;>
<context:exclude-filter type=&#34;regex&#34; expression=&#34;com.imooc.exl.*&#34;/>
</context:component-scan>创建applicationContext.xml配置文件
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xmlns:context=&#34;http://www.springframework.org/schema/context&#34;
xsi:schemaLocation=&#34;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&#34;>
<!--在IoC容器初始化时自动扫描四种组件类型注解并完成实例化
@Repository @Service @Controller @Component
-->
<!--context为命名空间-->
<context:component-scan base-package=&#34;com.imooc&#34;/>
</beans>
package com.imooc.spring.ioc.dao;
import org.springframework.stereotype.Repository;
// 组件类型注解默认beanId为类名首字母小写 userDao 也可以进行手动设置value属性
@Repository(&#34;udao&#34;)
public class UserDao {
}两类自动装配注解
分类 | 注解 | 说明 | 按类型装配 | @Autowired | 按容器内对象类型动态注入属性,由Spring机构提供 | @Inject | 基于JSR-330标准,其他同@Autowired,但不支持required属性 | 按名称装配 | @Named | 与@Inject配合使用,JSR-330规范,按属性名自动装配属性 | @Resource | 基于JSR-250规范,优先按名称、再按类型智能匹配 | package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值,不再执行set方法
@Autowired
private UserDao udao;
public UserService() {
System.out.println(&#34;正在创建UserService:&#34; + this);
}
public UserDao getUdao() {
return udao;
}
public void setUdao(UserDao udao) {
System.out.println(&#34;setUdao = &#34; + udao);
this.udao = udao;
}
}
public class SpringApplication {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(&#34;classpath:applicationContext.xml&#34;);
UserService userService = context.getBean(&#34;userService&#34;, UserService.class);
System.out.println(userService.getUdao());
}
}结果如下:
正在创建UserDao:com.imooc.spring.ioc.dao.UserDao@544a2ea6
正在创建UserService:com.imooc.spring.ioc.service.UserService@47db50c5
com.imooc.spring.ioc.dao.UserDao@544a2ea6
package com.imooc.spring.ioc.service;
import com.imooc.spring.ioc.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author R.Yu
*/
@Service
public class UserService {
// @Autowired
// Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值,不再执行set方法
private UserDao udao;
public UserService() {
System.out.println(&#34;正在创建UserService:&#34; + this);
}
public UserDao getUdao() {
return udao;
}
@Autowired
// 如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注入
public void setUdao(UserDao udao) {
System.out.println(&#34;setUdao = &#34; + udao);
this.udao = udao;
}
}结果如下:
正在创建UserDao:com.imooc.spring.ioc.dao.UserDao@10dba097
正在创建UserService:com.imooc.spring.ioc.service.UserService@481a15ff
setUdao = com.imooc.spring.ioc.dao.UserDao@10dba097
com.imooc.spring.ioc.dao.UserDao@10dba097
@Autowired可能遇到的问题:
如果IUserDao接口有两个实现类(即容器中有两个同类型的对象), 那么@Autowired注解按类型注入会失败. 解决办法:
- 将不需要的一个实现类取消交给SpringIOC容器管理;
- 在需要的那个接口实现类上面添加@Primary注解即可.
推荐按照名称进行注入
@Service
public class DepartmentService{
/**
* 1.@Resource设置name属性,则按照name在IoC容器中将bean注入
* 2.@Resource未设置name属性
* 2.1 以属性名作为bean name在IoC容器中匹配bean,如有匹配则注入
* 2.2 按属性名未匹配,则按类型进行匹配,同@Autowired,需加入@Primary解决类型冲突
* 使用建议:在使用@Resource注解时推荐设置name或保证属性名与bean名称一致
*/
@Resource(name = &#34;userOracleDao&#34;)
private IUserDao udao;
//或者这种写法
//@Resource
//private IUserDao userOracleDao;
}其他元数据注解
注解 | 说明 | @Primary | 按类型装配时出现多个相同类型对象,拥有此注解对象优先被注入 | @PostConstruct | 描述方法,相当于XML中init-method配置的注解版本 | @PreDestroy | 描述方法,相当于XML中destroy-method配置的注解版本 | @Scope | 设置对象的scope属性 | @Value | 为属性注入静态数据 |
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xmlns:context=&#34;http://www.springframework.org/schema/context&#34;
xsi:schemaLocation=&#34;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&#34;>
<!--通知Spring IoC容器初始化时加载属性文件-->
<context:property-placeholder location=&#34;classpath:config.properties&#34;/>
</beans>
3.基于Java代码配置Bean
- 完全摆脱XML的束缚,使用独立Java类管理对象与依赖
- 注解配置相对分散,利用Java Config可对配置集中管理
- 可以在编译时进行依赖检查,不容易出错
Java Config核心注解
注解 | 说明 | @Configuration | 描述类,说明当前类是Java Config配置类,完全替代XML文件 | @Bean | 描述方法,方法返回对象将被IoC容器管理,beanId默认为方法名 | @ImportResource | 描述类,加载静态文件,可使用@Value注解获取 | @ComponentScan | 描述类,同XML的<context:component-scan>标签 | 创建Java类
public class UserDao {
}
public class UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
创建配置类
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 当前类是一个配置类,用于替代applicationContext.xml
public class Config {
@Bean // Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
public UserDao userDao() {
return new UserDao();
}
@Bean
public UserService userService() {
return new UserService();
}
}创建启动类
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringApplication {
public static void main(String[] args) {
// 基于Java Config配置IoC容器的初始化
ApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
String[] ids = context.getBeanDefinitionNames();
for (String id : ids) {
System.out.println(id + &#34;:&#34; + context.getBean(id));
}
}
}结果如下
org.springframework.context.annotation.internalConfigurationAnnotationProcessor:org.springframework.context.annotation.ConfigurationClassPostProcessor@5a4aa2f2
org.springframework.context.annotation.internalAutowiredAnnotationProcessor:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6591f517
org.springframework.context.annotation.internalCommonAnnotationProcessor:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@345965f2
org.springframework.context.event.internalEventListenerProcessor:org.springframework.context.event.EventListenerMethodProcessor@429bd883
org.springframework.context.event.internalEventListenerFactory:org.springframework.context.event.DefaultEventListenerFactory@4d49af10
config:com.imooc.spring.ioc.Config$$EnhancerBySpringCGLIB$$216fe7e@279ad2e3
userDao:com.imooc.spring.ioc.dao.UserDao@58134517
userService:com.imooc.spring.ioc.service.UserService@4450d156
属性注入
package com.imooc.spring.ioc;
import com.imooc.spring.ioc.dao.UserDao;
import com.imooc.spring.ioc.service.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 当前类是一个配置类,用于替代applicationContext.xml
@ComponentScan(basePackages = &#34;com.imooc&#34;) // 此注解也会扫描该包及子包下其他组件
public class Config {
@Bean // Java Config利用方法创建对象,将方法返回对象放入容器,beanId=方法名
public UserDao userDao() {
return new UserDao();
}
@Bean
// 先按name尝试注入,name不存在则按类型注入
public UserService userService(UserDao userDao) {
UserService userService = new UserService();
userService.setUserDao(userDao);
return userService;
}
}Spring Test测试模块
- Spring Test是Spring中用于测试的模块
- Spring Test对JUnit单元测试框架有良好的整合
- 通过Spring Test可在JUnit在单元测试时自动初始化IoC容器
Spring与JUnit整合过程
- Maven工程依赖spring-test
- 利用@RunWith与@ContextConfiguration描述测试用例类
- 测试用例类从容器获取对象完成测试用例的执行
加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>创建dao层
public class UserDao {
public void insert() {
System.out.println(&#34;新增数据&#34;);
}
}创建service层
public class UserService {
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void createUser() {
System.out.println(&#34;调用创建用户业务代码&#34;);
userDao.insert();
}
}创建配置文件
<?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?>
<beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;
xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;
xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd&#34;>
<bean id=&#34;userDao&#34; class=&#34;com.imooc.spring.ioc.dao.UserDao&#34;/>
<bean id=&#34;userService&#34; class=&#34;com.imooc.spring.ioc.service.UserService&#34;>
<property name=&#34;userDao&#34; ref=&#34;userDao&#34;/>
</bean>
</beans>创建测试类
import com.imooc.spring.ioc.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
// 将JUnit4的执行权交由Spring Test,在测试用例执行前自动初始化IoC容器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {&#34;classpath:applicationContext.xml&#34;})
public class SpringTestor {
@Resource
private UserService userService;
@Test
public void testUserService() {
userService.createUser();
}
} |
|