文章目录
2.1 IOC简介
IOC:控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理 ;使用 IOC 目的是为了降低代码耦合度;IOC的底层原理是XML解析+工厂模式+反射。(详细了解IOC底层请见JavaWeb:(十一)DI/IOC,手撕IOC底层代码)
2.2 IOC容器
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。Spring 提供 IOC 容器实现两种方式:
- BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用 ,加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
- ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人 员进行使用 ,加载配置文件时候就会把在配置文件对象进行创建。
2.3 xml方式Bean管理
2.3.1 什么是Bean管理
Bean 管理指的是两个操作
- Spring 创建对象
- Spirng 注入属性
Bean 管理操作有两种方式
- 基于 xml 配置文件方式实现
- 基于注解方式实现(注解只是通过Spring自动帮我们配置Bean,XML则是手动进行配置,但二者本质上是一样的)
2.3.2 xml方式创建对象
在 spring 配置文件中,使用标签,标签里面添加对应属性,就可以实现对象创建;bean标签有两个主要的属性:
- id 属性:唯一标识
- class 属性:类全路径(包类路径)
创建对象时候,默认也是执行无参数构造方法完成对象创建,所以Java类都要事先无参构造函数
2.3.3 xml方式注入属性
DI:依赖注入,就是为Bean注入依赖,即注入属性,Spring提供了多种注入方式:
-
第一种注入方式:使用 set 方法进行注入
-
创建类,定义属性和对应的 set 方法
public class Book { private String bookName; private String bookAuthor; public void setBookName(String bookName) { this.bookName = bookName; } public void setBookAuthor(String bookAuthor) { this.bookAuthor = bookAuthor; } @Override public String toString() { return "Book{" + "bookName='" + bookName + '\'' + ", bookAuthor='" + bookAuthor + '\'' + '}'; } }
-
在 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="book" class="pojo.Book"> <property name="bookName" value="天龙八部"></property> <property name="bookAuthor" value="金庸"></property> </bean> </beans>
-
测试代码
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); System.out.println(book); } }
-
-
第二种注入方式:使用有参数构造进行注入
-
创建类,定义属性,创建属性对应有参数构造方法
public class Book { private String bookName; private String bookAuthor; public Book(String bookName, String bookAuthor) { this.bookName = bookName; this.bookAuthor = bookAuthor; } @Override public String toString() { return "Book{" + "bookName='" + bookName + '\'' + ", bookAuthor='" + bookAuthor + '\'' + '}'; } }
-
在 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="book" class="pojo.Book"> <constructor-arg name="bookName" value="天涯明月刀"></constructor-arg> <constructor-arg name="bookAuthor" value="古龙"></constructor-arg> </bean> </beans>
-
测试代码
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); System.out.println(book); } }
-
-
第三种注入方式:p名称空间注入
-
添加 p 名称空间在配置文件中
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
进行属性注入,在 bean 标签里面进行操作(本质上还是set注入,需要提供set方法)
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="book" class="pojo.Book" p:bookName="白发魔女传" p:bookAuthor="梁羽生"></bean> </beans>
-
测试代码
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); System.out.println(book); } }
-
2.3.4 xml注入各种类型的属性
-
注入null值
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="book" class="pojo.Book"> <property name="bookName"> <null/> </property> <property name="bookAuthor" value="无中生有的作者"></property> </bean> </beans>
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); System.out.println(book); } }
-
注入带特殊符号的属性值
<?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="book" class="pojo.Book"> <!--属性值包含特殊符号 1 把<>进行转义 < > 2 把带特殊符号内容写到 CDATA --> <property name="bookName"> <value><![CDATA[<<斗破苍穹>>]]></value> </property> <property name="bookAuthor" value="天蚕土豆"></property> </bean> </beans>
-
注入外部bean
-
创建两个类 service 类和 dao 类,在 service 调用 dao 里面的方法
public interface UserDAO { public void update() ; }
public class UserDAOImpl implements UserDAO { @Override public void update() { System.out.println("执行UserDAO的update方法..."); } }
public class UserService { //创建 UserDao 类型属性,生成 set 方法 private UserDAO userDao; public void setUserDao(UserDAO userDao) { this.userDao = userDao; } public void update() { System.out.println("执行UserService的update方法"); userDao.update(); } }
-
在 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="userDAO" class="dao.impl.UserDAOImpl"></bean> <bean id="userService" class="service.UserService"> <property name="userDao" ref="userDAO"></property> </bean> </beans>
-
代码测试
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); UserService userService = context.getBean("userService", UserService.class); userService.update(); } }
-
-
注入内部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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="service.UserService"> <property name="userDao"> <bean id="userDAO" class="dao.impl.UserDAOImpl"></bean> </property> </bean> </beans>
-
级联赋值
-
创建两个pojo类Dept和Employee
public class Dept { private String deptName; public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } @Override public String toString() { return "Dept{" + "deptName='" + deptName + '\'' + '}'; } }
public class Employee { private String employeeName; private String gender; private Dept dept; public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public void setGender(String gender) { this.gender = gender; } public void setDept(Dept dept) { this.dept = dept; } public Dept getDept() { return dept; } @Override public String toString() { return "Employee{" + "employeeName='" + employeeName + '\'' + ", gender='" + gender + '\'' + ", dept=" + dept + '}'; } }
-
在 spring 配置文件中进行配置
<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="employee" class="pojo.Employee"> <property name="employeeName" value="张三"></property> <property name="gender" value="男"></property> <property name="dept" ref="dept"></property> <property name="dept.deptName" value="技术部"></property> </bean> <bean id="dept" class="pojo.Dept"></bean> </beans>
-
代码测试
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Employee employee = context.getBean("employee", Employee.class); System.out.println(employee); } }
-
-
注入数组,list,set,map
-
创建一个pojo类Student
public class Student { //1 数组类型属性 private String[] courses; //2 list 集合类型属性 private List<String> list; //3 map 集合类型属性 private Map<String,String> maps; //4 set 集合类型属性 private Set<String> sets; public void setSets(Set<String> sets) { this.sets = sets; } public void setCourses(String[] courses) { this.courses = courses; } public void setList(List<String> list) { this.list = list; } public void setMaps(Map<String, String> maps) { this.maps = maps; } @Override public String toString() { return "Student{" + "\n\tcourses=" + Arrays.toString(courses) + ", \n\tlist=" + list + ", \n\tmaps=" + maps + ", \n\tsets=" + sets + "\n}"; } }
-
在 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="pojo.Student"> <property name="courses"> <array> <value>计算机网络</value> <value>软件工程</value> <value>数据结构</value> </array> </property> <property name="list"> <list> <value>大学语文</value> <value>高等数学</value> <value>大学物理</value> </list> </property> <property name="sets"> <set> <value>马克思主义原理</value> <value>中国近代史纲要</value> <value>毛泽东思想概论</value> </set> </property> <property name="maps"> <map> <entry key="第一门课" value="C++"></entry> <entry key="第二门课" value="Java"></entry> <entry key="第三门课" value="Python"></entry> </map> </property> </bean> </beans>
-
代码测试
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Student student = context.getBean("student", Student.class); System.out.println(student); } }
-
-
提取公共注入部分
-
在 spring 配置文件中引入名称空间 util
<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
-
使用 util 标签完成 list 集合注入提取
<util:list id="bookList"> <value>大学语文</value> <value>高等数学</value> <value>大学物理</value> </util:list> <bean id="student" class="pojo.Student"> <property name="list" ref="bookList"></property> </bean>
-
2.4.5 工厂Bean
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
工厂Bean的创建方式:
-
创建类,让这个类作为工厂 bean,实现接口 FactoryBean
-
实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Book> { //定义返回 bean @Override public Book getObject() throws Exception { Book book = new Book(); book.setBookName("斗罗大陆"); book.setBookAuthor("唐家三少"); return book; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return false; } }
-
在 spring 配置文件进行配置
<bean id="myBean" class="pojo.MyBean"></bean>
-
代码测试
public class Test1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("myBean", Book.class); System.out.println(book); } }
2.3.6 Bean的作用域
在Spring里面,默认情况下,bean是单实例对象。在spring配置文件 bean 标签里面有属性scope用于设置单实例还是多实例:
- singleton,表示是单实例对象,singleton是默认值。 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象。
- prototype,表示是多实例对象。设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用 getBean 方法时候创建多实例对象。
2.3.7 Bean生命周期
七步版
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
八步版
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 回调Aware接口的方法(BeanNameAware和BeanFactoryAware接口)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
代码演示(七步版):
-
编写一个pojo类,并在其中编写相应代码
public class LifeCycle { private String lfName; public LifeCycle() { System.out.println("第一步 执行无参数构造创建 bean 实例"); } public void setLfName(String lfName) { this.lfName = lfName; System.out.println("第二步 调用 set 方法设置属性值"); } public void initMethod(){ System.out.println("第四步 执行初始化的方法"); } public void destroyMethod(){ System.out.println("第七步 执行销毁的方法"); } @Override public String toString() { return "LifeCycle{" + "lfName='" + lfName + '\'' + '}'; } }
-
在 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="lifeCycle" class="pojo.LifeCycle" init-method="initMethod" destroy-method="destroyMethod"> <property name="lfName" value="初始化lfName"></property> </bean> <!--配置后置处理器--> <bean id="myBeanPostProcessor" class="processor.MyBeanPostProcessor"></bean> </beans>
-
代码测试
public class Test1 { @Test public void test(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); LifeCycle lifeCycle = context.getBean("lifeCycle", LifeCycle.class); System.out.println("第六步 获取创建 bean 实例对象:" + lifeCycle); context.close(); } }
2.3.8 Bean自动装配(重点)
自动装配:根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
实现自动装配 bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值:
- byName:根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
- byType:根据属性类型注入
代码测试:
<?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="employee" class="pojo.Employee" autowire="byName">
<property name="gender" value="男"></property>
<property name="employeeName" value="李四"></property>
</bean>
<bean id="dept" class="pojo.Dept">
<property name="deptName" value="英雄联盟分部"></property>
</bean>
</beans>
public class Test1 {
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Employee employee = context.getBean("employee", Employee.class);
System.out.println(employee);
}
}
2.3.9 引入外部属性文件
-
在Spring配置文件中直接配置Druid连接配置
<?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="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> </beans>
-
引入外部属性文件配置数据库连接池
-
创建外部属性文件,properties 格式文件,写数据库信息
-
引入 context 名称空间,并把外部 properties 属性文件引入到 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: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:druid.properties"/> <!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${driverClassName}"></property> <property name="url" value="${url}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> </bean> </beans>
-
2.4 注解方式Bean管理
2.4.1 什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面
- 使用注解目的:通过Spring进行自动配置,跳过手动的xml配置
Spring 针对 Bean 管理中创建对象提供注解 :
- @Component
- @Service
- @Controller
- @Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例,但约定大于配置,故每个注解应该用于固定层级上
2.4.2 基于注解方式实现对象的创建
-
第一步:引入spring-aop依赖
-
第二步:在xml中开启组件扫描,如果是完全注解开发则需要在配置类中开启组件扫描
<!--开启组件扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
第三步:创建类,在类上面添加创建对象注解
// 在注解里面 value 属性值可以省略不写, // 默认值是类名称,首字母小写 UserService -- userServic @Component public class UserDAOImpl implements UserDAO { @Override public void update() { System.out.println("执行UserDAO的update方法..."); } }
2.4.3 开启组件扫描细节配置
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter: 设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
2.4.4 基于注解方式实现属性注入
-
@Autowired:根据属性类型进行自动装配
@Component(value = "userDAO") public class UserDAOImpl implements UserDAO { @Override public void update() { System.out.println("执行UserDAO的update方法..."); } }
-
@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用
@Service public class UserService implements UserDAO { @Autowired @Qualifier(value = "userDAO") private UserDAO userDAO; public void setUserDAOImpl(UserDAO userDAOImpl) { this.userDAO = userDAOImpl; } public void update() { System.out.println("执行UserService的update方法"); userDAO.update(); } }
-
@Resource:既可以根据类型注入,也可以根据名称注入
@Service public class UserService implements UserDAO { @Resource private UserDAO userDAO; public void setUserDAOImpl(UserDAO userDAOImpl) { this.userDAO = userDAOImpl; } public void update() { System.out.println("执行UserService的update方法"); userDAO.update(); } }
-
@Value:注入普通类型属性
@Component public class Book { @Value(value = "九把刀") private String bookName; @Value(value = "那些年我们一起追过的女孩") private String bookAuthor; public void setBookName(String bookName) { this.bookName = bookName; } public void setBookAuthor(String bookAuthor) { this.bookAuthor = bookAuthor; } @Override public String toString() { return "Book{" + "bookName='" + bookName + '\'' + ", bookAuthor='" + bookAuthor + '\'' + '}'; } }
public class Test1 { @Test public void test(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); Book book = context.getBean("book", Book.class); System.out.println(book); } }
2.4.6 完全注解开发
创建配置类,替代 xml 配置文件
@Configuration
@ComponentScan(value = "atguigu")
public class SpringConfig {
}
代码测试
public class Test1 {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
}