3. IOC容器和Bean的装填
- IOC:控制翻转,
简单说由原来的自己控制对象的创建等过程,现在交给ioc容器进行创建对象。
AOP、事务的都是依赖于IOC。
- DI:(Dependency Injection)依赖注入:
比如service依赖dao(没有实例化的),如果设置一个实例化的dao对象过程(方式),称之为DI
3.1. BeanFactory与ApplicationContext接口
都是IOC容器,ApplicationContext是对BeanFactory的扩展,有两种子类型
- ClassPathXMLApplicationContext:
从类路径中查找配置文件
- FileSystemXMLApplicationContext
从文件系统中查找配置文件
BeanFactory与ApplicationContext区别:
BeanFactory,在使用的时候创建对象.如果配置出错问题会滞后报出。
ApplicationContext,在启动的时候创建的对象,能够在执行之初暴露一些问题。
3.2. 依赖注入(DI)
Spring提供了两种实现方案构造函数的注入,属性的注入
3.2.1. 创建一个新工程(quickstart)
3.2.2. 四个类实现(代码创建完)
3.2.2.1. IUserDao 接口
/**
* 项目 : spring-java1
* 创建时间 :2020/3/19 16:18 19
* author :张金山
* site : https://jshand.gitee.io
* 描述 : dao层接口
*/
public interface IUserDao {
List selectUser();
}
3.2.2.2. UserDaoImpl 实现类
import java.util.List;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/19 16:18 19
* author :张金山
* site : https://jshand.gitee.io
* 描述 : dao层的实现类
*/
public class UserDaoImpl implements IUserDao {
@Override
public List selectUser() {
System.out.println("执行dao层的方法......");
return null;
}
}
3.2.2.3. IUserService 接口
import java.util.List;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/19 16:19 19
* author :张金山
* site : https://jshand.gitee.io
* 描述 : Service层接口
*/
public interface IUserService {
List queryUser();
}
3.2.2.4. UserServiceImpl 实现类
import com.neuedu.dao.IUserDao;
import java.util.List;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/19 16:20 19
* author :张金山
* site : https://jshand.gitee.io
* 描述 : service层的实现类
*/
public class UserServiceImpl implements IUserService {
IUserDao userDao ;
public UserServiceImpl(IUserDao userDao) {
this.userDao = userDao;
}
@Override
public List queryUser() {
userDao.selectUser();
return null;
}
}
3.3. 依赖Spring的类库
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
3.4. 在xml中声明需要管理的Bean
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.neuedu.dao.UserDaoImpl">
</bean>
<bean id="userService" class="com.neuedu.service.UserServiceImpl">
</bean>
</beans>
3.4.1. 构造函数的注入
3.4.1.1. 声明带参数的构造器
3.4.1.2. 使用constructor-arg标签调用带参的构造器注入
Name :构造器中参数的名字
Index:构造器中参数的位置 从0开始
Value代表注入的值(常量类型)
Ref:注入的引用对象的id或者name
3.4.2. 属性注入
需求:UserController依赖于UserService
3.4.2.1. UserCongtroller
import com.neuedu.service.IUserService;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/20 13:52 20
* author :张金山
* site : https://jshand.gitee.io
* 描述 : 控制层的代码(模拟)
*/
public class UserController {
//1 声明属性
private IUserService userService;
private String address;
//2 设置setter 和getter 方法
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void queryUsers(){
System.out.println("调用Controller.."+address);
userService.queryUser();
}
}
3.4.2.2. Xml中Bean的声明
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--声明Dao层-->
<bean id="userDao" class="com.neuedu.dao.UserDaoImpl">
</bean>
<!--声明Service层-->
<bean id="userService" class="com.neuedu.service.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="age" value="50"/>
<!-- <constructor-arg index="0" ref="userDao"/>-->
<!-- <constructor-arg index="1" value="30"/>-->
</bean>
<!--声明Controller层
property 向属性中注入
name : 属性的名字
ref: 注入的bean对象的id
value : 常量值
-->
<bean id="userController" class="com.neuedu.controller.UserController">
<property name="userService" ref="userService" />
<property name="address" value="黑龙江哈尔滨市" />
</bean>
</beans>
3.5. 依赖注入的参数类型
1 ) 常量值 字面量(字符串、数字、布尔类型等)
2)引用类型
3)集合类型List\Set、Properties、Map
3.5.1. Bean的Java代码
package com.neuedu;
import com.neuedu.controller.UserController;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/20 14:16 20
* author :张金山
* site : https://jshand.gitee.io
* 描述 : 声明一个普通的bean
*/
public class Person {
//字面量的类型
private String address;
private Integer age;
//引用类型
private UserController userController;
//集合类型
private List<String> names;
private Set<Integer> ages;
private Map<String,String> stus;
private Properties props;
public void testDI(){
System.out.println("address:"+address);
System.out.println("age:"+age);
// System.out.println("address:"+address);
System.out.println("names:"+names);
System.out.println("ages:"+ages);
System.out.println("stus:"+stus);
System.out.println("props:"+props);
userController.queryUsers();
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public UserController getUserController() {
return userController;
}
public void setUserController(UserController userController) {
this.userController = userController;
}
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
public Set<Integer> getAges() {
return ages;
}
public void setAges(Set<Integer> ages) {
this.ages = ages;
}
public Map<String, String> getStus() {
return stus;
}
public void setStus(Map<String, String> stus) {
this.stus = stus;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
}
3.5.2. Xml注入
<!--声明bean对象-->
<bean id="person" class="com.neuedu.Person">
<property name="address" value="南岗区" > </property>
<property name="age" value="30" > </property>
<property name="userController" ref="userController" > </property>
<property name="names" >
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
<value>张三</value>
</list>
</property>
<property name="ages" >
<set>
<value>30</value>
<value>32</value>
<value>30</value>
</set>
</property>
<property name="stus" >
<map>
<entry>
<key><value>key00001</value></key>
<value>val0001</value>
</entry>
<entry>
<key><value>key00002</value></key>
<value>val0002</value>
</entry>
</map>
</property>
<property name="props" >
<props>
<prop key="proKey0001" >proVal0001</prop>
<prop key="proKey0002" >proVal0002</prop>
</props>
</property>
</bean>
3.5.3. 单元测试
import static org.junit.Assert.assertTrue;
import com.neuedu.controller.UserController;
import com.neuedu.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Unit test for simple App.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-beans.xml")
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Autowired
IUserService userService;
@Test
public void test1() {
userService.queryUser();
}
@Autowired
UserController userController;
@Test
public void test2() {
userController.queryUsers();
}
/**
* 测试各种数据类型的 注入
*/
@Autowired
Person person;
@Test
public void test3() {
person.testDI();
}
}
3.6. 自动注入
配置自动注入的方式
配置自动注入的方式有两种,一种是全局配置,另一种是局部单独配置。
全局配置:只配置一次,之后配置文件中的所有bean,都按照全局配置进行注入,全局配置是在<beans>
标签中配置default-autowire="Xxx";
局部单独配置:对于每一个bean,单独设置注入方式,单独配置是在单独的<bean>
标签中配置autowire="xxx"。
对于全局配置和局部单独配置,都有5个值可以选择:
Mode | Explanation |
---|---|
no | (Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
byName | Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property. |
byType | Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set). |
constructor | Analogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
3.6.1. Java代码
import com.neuedu.controller.UserController;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/20 14:44 20
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
public class MyApplication {
private UserController userController;
public UserController getUserController() {
return userController;
}
public void setUserController(UserController userController) {
this.userController = userController;
}
public void testController(){
System.out.println(userController);
}
}
3.6.2. Xml的声明
使用autowire属性根据名字、类型、构造器自动的注入。
<!--autowire 自动注入:
byName:查找相同的 bean的id和属性名相同的,自动注入
byType:查找bean类型 和属性类型相同的自动注入-->
<bean id="myApp" class="com.neuedu.MyApplication" autowire="byType">
</bean>
跟标签beans:default-autowire="no",可以指定装填的方式,在生产中保持不变,使用注解扫描的形式bean,通过@Autowired注解自动的装填,@Resource默认按照类型装填,可以指定name或者type。
3.7. 作用域
自定义一个MyBean的类(空)
Spring在声明Bean可以指定Scope用于设置Bean的作用域
3.7.1. Singleton(默认)
单例模式的对象,全局唯一.
3.7.2. prototype
prototype:原型模式,每次从容器获取bean时,容器都将创建一个新的Bean实例。
3.7.3. request
request:每一次Http请求都会创建一个新的Bean,仅在当前Http Request内有效。
3.7.4. session
session:同一个Http Session共享一个Bean,不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效。
3.7.5. session
global Session:在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅适用于Portlet应用环境。
3.8. 整合多文件
3.8.1. Spirng-service.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
https://www.springframework.org/schema/beans/spring-beans.xsd"
>
<!--模拟一大堆的service -->
<bean id="userService" class="com.neuedu.service.UserService"/>
</beans>
3.8.2. UserService
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:02 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 : service层
*/
public class UserService {
}
3.8.3. Spring-controller.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
https://www.springframework.org/schema/beans/spring-beans.xsd"
>
<!-- 模拟一堆的 Controller-->
<bean id="userController" class="com.neuedu.controller.UserController" />
</beans>
3.8.4. UserController 依赖service
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:02 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 : 控制器
*/
public class UserController {
}
3.8.5. 整合后配合文件
通过import引入其他配置文件。
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd"
>
<!--包含子配置文件-->
<import resource="spring-controller.xml"/>
<import resource="spring-service.xml"/>
<bean id="myBean" class="com.neuedu.MyBean" scope="singleton"/>
<bean id="myBean2" class="com.neuedu.MyBean" scope="prototype"/>
</beans>
3.9. 使用注解声明Bean及自动注入
声明Bean的注解
@Component
@Controlle
@Service
@Repository
自动注入的注解
@Autowired 不需要声明setter、getter方法
@Qualifier 注解用于获取同类型有多个的情况,用于区分Bean的名字
@Scope(value="prototype") 用于指定Bean的作用域
@Lazy让类的加载(初始化延迟,需要用到属性的时在加载)在属性、类上面同时声明
@Primary: 在Bean的声明处
由于按类型自动装配可能会导致多个候选者,因此通常需要对选择过程有更多的控制。实现这一点的一种方法是使用Spring的@Primary注释。@Primary表示,当多个bean是自动连接到单值依赖项的候选者时,应该优先考虑特定的bean。如果候选bean中只存在一个主bean,它将成为自动连接的值。
3.9.1. 定义Bean
3.9.1.1. IAccountService 接口
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:59 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
public interface IAccountService {
}
3.9.1.2. AccountServiceImpl 实现类
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:59 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
//@Component("accountServiceImpl")
@Component
public class AccountServiceImpl implements IAccountService {
}
3.9.1.3. AccountServiceImpl2实现类
import org.springframework.stereotype.Component;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:59 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
@Component
public class AccountServiceImpl2 implements IAccountService {
}
3.9.1.4. AccountController (依赖Service)
import com.neuedu.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 10:33 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
@Component
public class AccountController {
@Autowired //按照类型匹配
@Qualifier("accountServiceImpl2")
private IAccountService iAccountService;
public void testService(){
System.out.println(iAccountService);
}
}
3.9.2. 使用xml声明扫描的包
使用context标签指定扫描的包
<?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"
>
<!--使用scan 进行扫描Bean-->
<context:component-scan
base-package="com.neuedu.service,com.neuedu.controller"/>
</beans>
3.9.3. 使用注解声明类为IOC中Bean
如果出现多个同类型的Bean @AutoWried是默认按照类型匹配的,需要通过Qualifier注解指定名字
3.9.4. 单元测试
@Test
public void test3(){
String config = "spring-annotation.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
AccountController accountController = applicationContext.getBean(AccountController.class);
// AccountService accountService = applicationContext.getBean(AccountService.class);
System.out.println(accountController);
// System.out.println(accountService);
accountController.testService();//测试Controller是否依赖成功 service
}
3.10. 使用Java类的形式进行配置Bean并自动注入
1)在配置类上申明@Configuration注解,
2)在需要创建Bean的方法上声明@Bean注解
3)使用AnnotationConfigApplicationContext类初始化IOC容器
4)使用@import注解引入其他配置类。
3.10.1. 主配置类JavaConfig
import com.neuedu.MyBean;
import com.neuedu.controller.UserController;
import com.neuedu.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 11:29 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 :
*/
@Configuration
@Import(OtherConfig.class)
public class JavaConfig {
@Bean
public UserService createService(){
return new UserService();
}
@Bean
public UserController createController(UserService userService){
System.out.println("bean工厂 声明Controller时,注入的 "+userService);//
return new UserController();
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
UserController userController = context.getBean(UserController.class);
MyBean myBean = context.getBean(MyBean.class);
System.out.println(userController);
System.out.println(myBean);
}
}
3.10.2. 其他配置类
import com.neuedu.MyBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 项目 : spring-java1
* 创建时间 :2020/3/23 11:36 23
* author :张金山
* site : https://jshand.gitee.io
* 描述 : 另外一个配置类
*/
@Configuration
public class OtherConfig {
@Bean
public MyBean createBean(){
return new MyBean();
}
}
3.11 Bean的声明周期
1.bean的生命周期 通过构造函数(不管是有参还是无参)来实例化bean
2.为bean的属性设置值 和对其他的bean引用(调用set方法)
3.在初始化之前执行的方法 postProcessBeforeInitialization 需要实现: BeanPostProcessor
4.调用bean的初始化的方法(需要进行配置) xml文件中加上init -method
5.在初始化之后执行的方法 postProcessAfterInitialization
6.获取创建bean实例对象
7.执行销毁方法
3.12 Method Injection方法注入
在大多数应用程序场景中,容器中的大多数bean都是单例的。当一个单例bean需要与另一个单例bean协作,或者一个非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖关系。当bean的生命周期不同时,问题就出现了。假设单例bean A需要使用非单例(原型)bean B,可能是在A上的每个方法调用上。容器只创建单例bean A一次,因此只有一次设置属性的机会。容器不能在每次需要bean B的实例时都为bean A提供新的实例。
一个解决方案是放弃一些控制反转。您可以通过实现ApplicationContextAware接口,并在bean A每次需要时对容器请求(通常是新的)bean B实例进行getBean(“B”)调用,使bean A意识到容器。下面的例子展示了这种方法:
ApplicationContextAware
除了ApplicationContextAware和BeanNameAware(前面讨论过)之外,Spring还提供了广泛的Aware回调接口,让bean向容器表明它们需要特定的基础设施依赖。作为一般规则,名称指示依赖项类型。下表总结了最重要的Aware接口
参考官网doc
Aware类型 | Injected Dependency 依赖注入 | 具体阐述 Explained in… |
---|---|---|
ApplicationContextAware | Declaring ApplicationContext . | ApplicationContextAware and BeanNameAware |
ApplicationEventPublisherAware | Event publisher of the enclosing ApplicationContext . | Additional Capabilities of the ApplicationContext |
BeanClassLoaderAware | Class loader used to load the bean classes. | Instantiating Beans |
BeanFactoryAware | Declaring BeanFactory . | The BeanFactory API |
BeanNameAware | Name of the declaring bean. | ApplicationContextAware and BeanNameAware |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time. | Load-time Weaving with AspectJ in the Spring Framework |
MessageSourceAware | Configured strategy for resolving messages (with support for parametrization and internationalization). | Additional Capabilities of the ApplicationContext |
NotificationPublisherAware | Spring JMX notification publisher. | Notifications |
ResourceLoaderAware | Configured loader for low-level access to resources. | Resources |
ServletConfigAware | Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext . | Spring MVC |
ServletContextAware | Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext . | Spring MVC |
