三、spring成长之路——springIOC容器详解(上)
一、springIOC
控制反转和依赖注入
:
简单的说就是将对象的创建,属性的的设置交给spring容器进行管理,而不再由用户自己创建,当用户需要使用该接口或者类的时候,直接注入就可以了,spring容器会自动帮助用户创建对象。
1.创建maven应用程序
【pom.xml】
1.引入spring依赖,junit依赖
2.引入maven插件——java编译插件
<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.itcloud</groupId>
<artifactId>resource</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>resource</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.15.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
该依赖会下载下面jar
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
- org.springframework:spring-aop:4.3.15.RELEASE
- org.springframework:spring-beans:4.3.15.RELEASE
- org.springframework:spring-core:4.3.15.RELEASE
- org.springframework:spring-expression:4.3.15.RELEASE
2.springIOC基础
基本概念:springIOC主要作用是用来管理javaBean的容器,将java对象以及对象和对象之间的关系交由Spring容器管理。
在没有spring容器之前对接口或者类进行实例化的时候都需要使用new
关键字,来进行对象的创建,那么自从有了spring,那么这些事情就交给了spring来做了。
2.1.了解spring的几种注入方式
【Teacher.java】
getter和setter方法在这里都会被省略。
一个老师对应多个学生,老师pojo类中包含setter注入,和List集合注入
public class Teacher implements Serializable {
private Long id;
private String name;
private Integer age;
private List<Student> students;
}
【Student.java】
一个学生对应一个老师,学生包含多种注入方式,有setter,Properties类注入,map注入以及构造方法注入
注意点,
1.如果添加了有参构造方法(没有参构造),那么在进行注入的时候必须要进行构造方法的注入
2.如果既有有参构造和无参构造可以不进行构造方法的注入
public class Student implements Serializable {
private Long id;
private String name;
private Teacher teacher;
private Properties pro;
private Map<String,Object> map;
public Student(){}
public Student(Long id, String name){
this.id = id;
this.name = name;
}
}
【applicationContext.xml】**创建spring容器
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.itcloud.pojo.Student">
<property name="id" value="1"/>
<property name="name" value="小明"/>
</bean>
<bean id="student2" class="com.itcloud.pojo.Student">
<!-- 构造方法注入 -->
<!--每一个标签都代表一个构造方法的属性,按照参数在构造方法中的顺序进行注入-->
<constructor-arg value="2"/>
<constructor-arg>
<value>张三</value>
</constructor-arg>
<property name="teacher" ref="teacher" />
<!-- map注入 -->
<property name="map">
<map>
<entry key="1" value="语文" />
<entry key="2" value="数学" />
</map>
</property>
<!-- Properties注入 -->
<property name="pro">
<props>
<prop key="身高">1.8</prop>
<prop key="体重">70kg</prop>
</props>
</property>
</bean>
<bean id="teacher" class="com.itcloud.pojo.Teacher">
<property name="id" value="100023" />
<property name="name" value="王老师" />
<property name="age" value="30" />
<!-- list集合注入 -->
<property name="students">
<list>
<ref bean="student2" />
<ref bean="student" />
</list>
</property>
</bean>
</beans>
【TestIOC.java】进行数据的测试,debug观察数据
package com.itcloud.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIOC {
//加载spring容器
private ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
@Test
public void testStudent() {
//context.getBean()获取spring容器中管理的bean,参数为Id
Student stu1 = (Student) context.getBean("student");
Student stu2 = context.getBean("student2", Student.class);
Teacher teacher = context.getBean("teacher", Teacher.class);
System.out.println("---------------------");
}
}
2.2.p标签和c标签的注入方式
p代表的就是属性,c代表的就是构造方法。
添加标签头,引入p和c
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
实例
c:id="3" c:name="smith"
就是构造方法中的两个参数 id和name,p:teacher-ref="teacher"
为类中中的属性teacher
<bean id="student3" class="com.itcloud.pojo.Student" c:id="3" c:name="smith" p:teacher-ref="teacher"/>
2.3.bean之间的继承关系
两个关键字段parent
和abstract
1.此时student3会继承student中的id,和name属性,这个是parent的作用,非必须标签
2.abstract表示student这个bean无法被实例化,即无法再代码中获取这个bean,也无法被外部所引用,非必须标签
例如:ref="student"是错的
<bean id="student" class="com.itcloud.pojo.Student" abstract="true">
<property name="id" value="1"/>
<property name="name" value="小明"/>
</bean>
<bean id="student3" class="com.itcloud.pojo.Student" parent="student" p:teacher-ref="teacher"/>
2.4.bean的作用域
概述
作用域 | 描述 |
---|---|
单例(singleton) | (默认)每一个Spring IoC容器都拥有唯一的一个实例对象 |
原型(prototype) | 一个Bean定义,任意多个对象 |
scope="singleton"
默认值,只会产生一个实例化对象
scope="prototype"
原型,每次获取bean的时候都会获取一个新的实例化对象
<bean id="student4" class="com.itcloud.pojo.Student" parent="student" p:teacher-ref="teacher" scope="singleton"/>
2.6.bean的生命周期
两个关键点:
1.<bean/>
标签中的字段:init-method=""
表示bean初始化(构造方法)之后调用的方法 destroy-method=""
容器关闭之后调用的方法.
2. bean的后置处理器,需要实现方法,BeanPostProcessor
这个类,两个方法:
postProcessBeforeInitialization()
:在每个bean初始化后(构造方法)调用一次(在init-method
方法之前被调用)。
postProcessAfterInitialization()
:在init-method
之后被调用,destroy-method
之前被调用
实现案例0001
【LifeCycle.java】
package com.itcloud.pojo;
public class LifeCycle {
public LifeCycle(){
System.out.println("构造方法初始化..........");
}
public void init(){
System.out.println("init()初始化方法.......");
}
public void destory(){
System.out.println("destory()销毁方法.......");
}
}
【applicationContext.xml】
init-method="init" destroy-method="destory"
<bean id="lifeCycle" class="com.itcloud.pojo.LifeCycle" init-method="init" destroy-method="destory"></bean>
【TestIOC.java】测试
public class TestIOC {
//加载spring容器
private ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
@Test
public void testStudent() {
LifeCycle lifeCycle = context.getBean("lifeCycle", LifeCycle.class);
context.close();
//测试结果
/*
信息: Loading XML bean definitions from class path resource [spring/applicationContext.xml]
构造方法初始化..........
init()初始化方法.......
四月 08, 2018 10:09:34 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@27ddd392: startup date [Sun Apr 08 10:09:33 CST 2018]; root of context hierarchy
destroy()销毁方法.......
*/
}
}
第二个关键点实现案例0002
【CycleProcessor.java】
package com.itcloud.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
public class CycleProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
System.out.println("CycleProcessor start....." + name);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
System.out.println("CycleProcessor end....." + name);
return bean;
}
}
【TestIOC.java】测试类不变,测试结果:
/*
构造方法初始化..........
CycleProcessor start.....lifeCycle
init()初始化方法.......
CycleProcessor end.....lifeCycle
四月 08, 2018 10:13:31 上午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@58c1670b: startup date [Sun Apr 08 10:13:00 CST 2018]; root of context hierarchy
destroy()销毁方法.......
*/
2.7.工厂注入(了解即可)
注意点,如果工厂方法有参数,通过<constructor-arg value="xxx"></constructor-arg>
进行参数匹配
静态工厂注入
public class StudentFactory {
public static Student getInstance(){
Student stu = new Student();
stu.setId(10L);
stu.setName("小十");
return stu;
}
}
<bean id="student6" class="com.itcloud.pojo.StudentFactory" factory-method="getInstance"></bean>
测试
Student stu = context.getBean("student6", Student.class);
实例工厂注入
package com.itcloud.pojo;
public class TeacherFactory {
public Teacher getInstance(Long id, String name){
Teacher teacher = new Teacher();
teacher.setId(id);
teacher.setName(name);
return teacher;
}
}
<!-- 实例工厂必须单独配置一个bean -->
<bean id="teacherFactory" class="com.itcloud.pojo.TeacherFactory"/>
<bean id="teacher2" factory-bean="teacherFactory" factory-method="getInstance">
<constructor-arg>
<value>222</value>
</constructor-arg>
<constructor-arg name="name" value="张老师" />
</bean>
测试
Teacher teacher = context.getBean("teacher2", Teacher.class);
FactoryBean配置
跳转标志
package com.itcloud.pojo;
import org.springframework.beans.factory.FactoryBean;
public class TeacherFactoryBean implements FactoryBean {
private String name;
@Override
public Object getObject() throws Exception {
Teacher teacher = new Teacher();
teacher.setName(name);
return teacher;
}
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
@Override
public boolean isSingleton() {
return true;
}
public void setName(String name) {
this.name = name;
}
}
<!--FactoryBean方法配置bean-->
<bean id="teacherBean" class="com.itcloud.pojo.TeacherFactoryBean">
<property name="name" value="李老师"/>
</bean>
Teacher teacher = context.getBean("teacherBean", Teacher.class);
3.spring注解注入
三个注解将类注入到spring容器中,注意点:注解默认注入的Id为当前类的名称首字母小写
@Repository
主要用于dao,数据访问层@Service
用于Service层,调用数据访问层的方法,进行逻辑操作@Component
用户普通类的注册,用户自己定义的组件
我们知道Service层一定会调用dao层的相关方法,dao层已经被注册到Spring容器之中,这是后就需要使用Spring为我们提供的注解来引用对应的实例
@Autowired
按照类型进行匹配,如果一个接口存在两个子类,可以配合@Qualifier
注解来使用@Resource
按照名称进行匹配,
3.1简单应用应用案例
【applicationContext-annotation.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c" 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">
<!-- 此配置表示com.itcloud包及其子包支持注解配置 -->
<context:component-scan base-package="com.itcloud">
</context:component-scan>
</beans>
【UserDAO.java】
package com.itcloud.dao;
public interface UserDAO {
//用户更新
int update();
}
【UserDAOImpl.java】注意点:@Repository的value的值默认是(userDAOImpl)
@Repository
public class UserDAOImpl implements UserDAO {
@Override
public int update() {
System.out.println("进行数据库语句编写");
return 0;
}
}
【UserService.java】
package com.itcloud.service;
public interface UserService {
int doUpdate();
}
【UserServiceImpl.java】@Service的value的默认值是:userServiceImpl
ackage com.itcloud.service.impl;
import com.itcloud.dao.UserDAO;
import com.itcloud.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Override
public int doUpdate() {
System.out.println("UserServiceImpl update()方法开始....");
userDAO.update();
return 0;
}
}
测试
context.getBean("userDAOImpl");//结果:userDAOImpl@127 即:获取到UserDAO的对象
理解bean之间的相互调用
我们在UserServiceImpl
这个类中可以看到如下这句话,这句话表示的意思就是引用外部bean,在没有Spring之前我们引入外部bean的过程是:private UserDAO userDAO = new UserDAOImpl()
,在有了Spring之后,spring会自动帮我们进行对象的创建,以及维护对象之间的关系。
@Autowired
private UserDAO userDAO;
测试Autowired
@Test
public void testAnnotation(){
UserService userService = context.getBean(UserService.class);
userService.doUpdate();
}
//结果
/*
UserServiceImpl update()方法开始....
进行数据库语句编写
*/
前面提到,@Autowired
是根据类型进行注入的,此时因为UserDAO
只有一个子类,但是如果有两个子类要怎么书写呢:
//方案一,官方推荐
@Autowired
@Qualifier("userDAOImpl")
private UserDAO userDAO;
//方案二
@Resource(name = "userDAOImpl")
private UserDAO userDAO;
//或者
@Resource
private UserDAO userDAOImpl;
3.2理解开启注解支持配置
【applicationContext-annotation.xml】
<context:component-scan base-package="com.itcloud">
</context:component-scan>
这里也可以添加子元素,对注解数据进行过滤
最常用的过滤方式
<context:component-scan base-package="com.itcloud" use-default-filters="false">
<!--只包含Service注解的bean,其他注解不会被扫描-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
use-default-filters="false"
不使用默认过滤方式,如果为true的话,<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
表示的意思只是将@Service包含进来,其他注解也会包含的
<!--表示不扫描Repository注解-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
4.spring引用外部属性文件
4.1 applicationContext.xml文件配置bean并且注入属性文件中的内容
【value.properties】定义一个外部属性文件
value.driverName=com.mysql.jdbc.Driver
value.url=jdbc:mysql://localhost:3306/test
value.username=root
value.password=123456
【DataSource.java】定义属性类
package com.itcloud.value;
public class DataSource {
private String driverName;
private String username;
private String password;
private String url;
//getter setter方法略
}
【applicationContext-annotation.xml】在spring配置文件中获取属性文件内容
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c" 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">
<context:property-placeholder location="classpath:properties/value.properties"/>
<bean id="dataSource" class="com.itcloud.value.DataSource">
<property name="username" value="${value.username}"/>
<property name="password" value="${value.password}"/>
<property name="driverName" value="${value.driverName}"/>
<property name="url" value="${value.url}"/>
</bean>
</beans>
此时,当spring容器加载的时候,DataSource.java
被实例化, value.properties
属性文件中的内容会被注入到DataSource中。
4.2不通过配置文件的方式读取属性
【applicationContext-annotation.xml】开启注解配置
<context:component-scan base-package="com.itcloud"/>
<context:property-placeholder location="classpath:properties/value.properties"/>
<!--<bean id="dataSource" class="com.itcloud.value.DataSource">-->
<!--<property name="username" value="${value.username}"/>-->
<!--<property name="password" value="${value.password}"/>-->
<!--<property name="driverName" value="${value.driverName}"/>-->
<!--<property name="url" value="${value.url}"/>-->
<!--</bean>-->
【DataSource.java】 此时可以没有setter方法
package com.itcloud.value;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class DataSource {
@Value("${value.driverName}")
private String driverName;
@Value("${value.username}")
private String username;
@Value("${value.password}")
private String password;
@Value("${value.url}")
private String url;
}