1. Spring框架的介绍
Spring框架是一个开源的JavaEE应用程序
主要核心是IOC和AOP两大技术
- IOC:Inversion of Control, 控制反转。它是通过将控制权从应用程序中移出,使得对象的创建和管理由框架来完成,从而降低系统间的耦合度。
- DI:Dependency Injection, 依赖注入。它是一种设计模式,属于控制反转的一部分。其基本思想是将对象的依赖(即它所需要的其他对象)通过外部注入的方式提供,而不是由对象自己创建或查找。这有助于减少类之间的耦合,提高代码的可测试性、可维护性和灵活性。
- AOP:Aspect-Oriented Programming, 面向切面编程。它是一种编程范式,旨在将横切关注点(如日志、事务处理、安全控制等)从主业务逻辑中分离出来,使得这些功能可以独立开发、测试和维护。
Spring官网:spring.io
目前学习的是:Spring Framework
2. Spring的作用
Dao层:
JDBC操作。对应框架:MyBatis
Service层:
Spring框架不是针对service层业务逻辑,service层没哟适合的框架
Controller层:
Servlet(接收请求,响应数据,地址配置,页面转发)
对应框架:Spring MVC
3. Spring框架搭建
3.1 新建一个maven项目
3.2 去MVN Repository官网导入Spring Context依赖坐标
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.1</version>
</dependency>
</dependencies>
3.3 新建service.UserService (Bean类)
package com.gtc.service;
public class UserService {
public void test(){
System.out.println("UserService Test...");
}
}
3.4 在resources文件目录下创建spring.xml配置文件
在Spring框架中,spring.xml
是Spring配置文件的标准格式,通常用于配置Spring的Bean,即对象的创建、初始化、依赖注入等。通过spring.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">
<!--
id: bean标签的唯一标识,一般是对应的JavaBean对象名称的首字母小写
class: JavaBean对象的类路径 (包名+类名)
-->
<bean id="userService" class="com.gtc.service.UserService"></bean>
</beans>
注意:这是Spring框架定义好的格式。
3.5 新建test.Starter01来测试
package com.gtc.test;
import com.gtc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Starter01 {
public static void main(String[] args) {
// 得到Spring的上下文环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 通过id属性值得到指定的bean对象 (传统方法需要自己new)
UserService userService = (UserService) ac.getBean("userService");
// 调用实例化好的javabean对象中的方法
userService.test();
}
}
new UserService()
的过程交给IOC去做了
4. Spring IOC 手动注入
手动实例化与外部引入
示例1:
public class UserService{ UserDao userDao = new UserDao(); }
示例2:
public class UserService{ private UserDao userDao; public UserService(UserDao userDao){ this.userDao = userDao; } }
对比发现:示例二中对于UserDao对象的创建并没有像示例一那样主动的去实例化,而是通过带参方法形式将UserDao传入过来,从而实现UserService对UserDao类的依赖
而实际创建对象的幕后对戏那个即是交给了外部来创建。
4.1 set方法注入
四个文件: UserDao.java, UserService.java, spring.xml, Start01.java
-
UserDao.java: Bean对象
-
UserService: Bean对象
-
spring.xml:配置文件
-
Start01.java: 测试文件
案例需求:UserService类中要使用UserDao类
// UserDao的Bean对象
package com.gtc.dao;
public class UserDao {
public void test(){
System.out.println("UserDao Test...");
}
}
package com.gtc.service;
import com.gtc.dao.UserDao;
import com.sun.deploy.panel.IProperty;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 常用对象String(日期类型)
private String host;
public void setHost(String host) {
this.host = host;
}
// 基本类型
private Integer port;
public void setPort(Integer port) {
this.port = port;
}
// List集合
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
// Set集合
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
// Map对象
private Map<String, Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
// 属性Properties对象
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
System.out.println(host);
System.out.println(port);
// 遍历List集合
for (String a : list){
System.out.println(a);
}
// 遍历Set集合
for (String a : set){
System.out.println(a);
}
// 遍历Map
for (Map.Entry<String, Object> entry : map.entrySet()){
System.out.println("MapKey = " + entry.getKey() + ", MapValue = " + entry.getValue());
}
// 遍历Properties
for (String key : properties.stringPropertyNames()){
System.out.println("ProKey = " + key + ", ProValue = " + properties.getProperty(key));
}
}
}
<?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">
<!--
Set方法注入(通过property属性注入):
name: bean对象中属性字段的名字
ref: 指定bean标签的id属性值
-->
<bean id="userService" class="com.gtc.service.UserService">
<!--JavaBean对象-->
<property name="userDao" ref="userDao"></property>
<!--常用类型String-->
<property name="host" value="127.0.0.1"></property>
<!--基本类型Integer-->
<property name="port" value="8080"></property>
<!--List集合-->
<property name="list">
<list>
<value>List上海</value>
<value>List杭州</value>
<value>List深圳</value>
</list>
</property>
<!--Set集合-->
<property name="set">
<list>
<value>Set上海</value>
<value>Set杭州</value>
<value>Set深圳</value>
</list>
</property>
<!--Map对象-->
<property name="map">
<map>
<entry>
<key><value>周杰伦</value></key>
<value>晴天</value>
</entry>
<entry>
<key><value>林俊杰</value></key>
<value>江南</value>
</entry>
<entry>
<key><value>陈奕迅</value></key>
<value>单车</value>
</entry>
</map>
</property>
<!--Properties属性对象-->
<property name="properties">
<props>
<prop key="bj">properties北京</prop>
<prop key="sh">properties上海</prop>
<prop key="hz">properties杭州</prop>
</props>
</property>
</bean>
<bean id="userDao" class="com.gtc.dao.UserDao"></bean>
</beans>
// Start01.java测试文件
package com.gtc;
import com.gtc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Start01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
得到的输出:
UserService Test...
UserDao Test...
127.0.0.1
8080
List上海
List杭州
List深圳
Set上海
Set杭州
Set深圳
MapKey = 周杰伦, MapValue = 晴天
MapKey = 林俊杰, MapValue = 江南
MapKey = 陈奕迅, MapValue = 单车
ProKey = bj, ProValue = properties北京
ProKey = sh, ProValue = properties上海
ProKey = hz, ProValue = properties杭州
4.2 构造器注入
注:需提供带参构造器
package com.gtc.dao;
public class UserDao02 {
public void test(){
System.out.println("UserDao02 Test...");
}
}
package com.gtc.service;
import com.gtc.dao.UserDao02;
/**
* 构造器方法注入
* 需要提供带参构造
*/
public class UserService02 {
private UserDao02 userDao02;
/* 构造器注入 */
public UserService02(UserDao02 userDao02) {
this.userDao02 = userDao02;
}
public void test(){
System.out.println("UserService02 test...");
userDao02.test();
}
}
<?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">
<!--
构造器注入:
设置构造器所需要的参数
通过constructor-arg标签设置构造器的参数
name: 属性名称
ref: 要注入的bean对象对应的bean标签的id属性值
-->
<bean id="userService02" class="com.gtc.service.UserService02">
<constructor-arg name="userDao02" ref="userDao02"></constructor-arg>
</bean>
<bean id="userDao02" class="com.gtc.dao.UserDao02"></bean>
</beans>
package com.gtc.test;
import com.gtc.service.UserService;
import com.gtc.service.UserService02;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Starter02 {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring02.xml");
UserService02 userService02 = (UserService02) beanFactory.getBean("userService02");
userService02.test();
}
}
4.3 静态工厂注入
将TypeDao类注入到TypeService类中,静态工厂的静态方法创建TypeDao,XML配置静态工厂。
package com.gtc.dao;
public class TypeDao {
public void test(){
System.out.println("TypeDao test...");
}
}
package com.gtc.service;
import com.gtc.dao.TypeDao;
public class TypeService {
private TypeDao typeDao;
public void setTypeDao(TypeDao typeDao) {
this.typeDao = typeDao;
}
public void test(){
System.out.println("TypeService test...");
typeDao.test();
}
}
package com.gtc.factory;
import com.gtc.dao.TypeDao;
/**
* 静态工厂注入
*/
public class StaticFactory {
public static TypeDao createTypeDao(){
return new TypeDao();
}
}
package com.gtc.test;
import com.gtc.service.TypeService;
import com.gtc.service.UserService;
import com.gtc.service.UserService02;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Starter02 {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring02.xml");
TypeService typeService = (TypeService) beanFactory.getBean("typeService");
typeService.test();
}
}
4.4 实例化工厂注入
略
5. Spring IOC 自动注入
注解方式注入Bean
对于bean的注入,除了使用xml配置以外,可以使用注解配置。注解的配置,可以简化配置文件,提高开发速度,使程序看上去更简洁。对于注解的解释,Spring对于注解有专门的解释器,对定义的注解进行解析,实现对应bean对象的注入,通过反射技术实现。
5.1 准备环境
将UserDao注入到UserService中
1. 准备UserDao类
package com.gtc.dao;
public class UserDao {
public void test(){
System.out.println("UserDao test...");
}
}
2. 准备UserService类
package com.gtc.service;
import com.gtc.dao.UserDao;
import javax.annotation.Resource;
public class UserService {
// 注入JavaBean对象
@Resource
private UserDao userDao;
// set方法(也可以不要)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
System.out.println("UserService test...");
userDao.test();
}
}
3. 修改spring.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
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:annotation-config/>
<bean id="userDao" class="com.gtc.dao.UserDao"></bean>
<bean id="userService" class="com.gtc.service.UserService"></bean>
</beans>
4. 测试
package com.gtc.test;
import com.gtc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
5.2 Resource注解
1. 注解默认通过属性字段名称查找对应的bean对象(属性字段名称与bean标签的id属性值一致)
2. 如果属性字段名称不一样,则会通过类型(Class)类型
3. 属性字段可以提供set方法,也可以不提供
4. 注解既可以声明在属性字段,也可以声明在set方法上
5. 可以设置注解的name属性,name属性值要与bean标签的id属性值一致
6. 当注入接口时,如果接口只有一个实现类,则正常实例化;如果接口有多个实现类,则需要使用name属性指定需要被实例化的bean对象
5.3 Autowired注解
1. 注解默认使用Class类型去查找bean对象,与属性字段名称没有关系
2. 属性字段可以提供set方法,也可以不提供
3. 注解可以声明在属性级别,也可以声明在set方法级别
4. 如果想要通过指定名称查找bean对象,需要结合@Qualifier使用,通过value属性值查找bean对象(value属性值必须要设置,且值要与bean标签的id属性值对应)
6. Spring IOC 扫描器
实际的开发中,bean的数量非常多,采用手动配置bean的方式已无法满足生产需要,Spring这时候同样提供了扫描的方式,对扫描的bean对象统一进行管理,简化开发配置,提高开发效率。
6.1 Spring IOC 扫描器的配置
Spring IOC 扫描器
作用:bean对象统一进行管理,简化开发配置,提高开发效率
1. 设置自动化扫描的范围:如果bean对象未在指定包范围,即时声明了注解,也无法实例化
2. 使用指定的注解(声明在类级别)bean对象的id属性默认是类的首字母小写
- Dao层:@Repository
- Service层:@Service
- Controller层:@Controlloer
- 任意类:@Component
(1) 准备TypeDao类
package com.gtc.dao;
import org.springframework.stereotype.Repository;
@Repository
public class TypeDao {
public void test(){
System.out.println("TypeDao test...");
}
}
(2)配置IOC扫描器
<?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">
<!-- Spring IOC 扫描器 -->
<context:component-scan base-package="com.gtc"/>
</beans>
(3) 测试
package com.gtc.test;
import com.gtc.dao.TypeDao;
import com.gtc.service.AccountService;
import com.gtc.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
TypeDao typeDao = (TypeDao) context.getBean("typeDao");
typeDao.test();
}
}
7. Bean的作用域与生命周期
7.1 Bean的作用域
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:
7.1.1 singleton作用域
注意:lazy-init是懒加载,如果等于true时作用是指Spring容器启动的时候不会去实例化这个bean,而是在程序调用才去实例化。默认是false即Spring容器启动时实例化。
默认情况下,被管理的bean只会IOC容器中存在一个实例,对于所有获取该Bean的操作Spring容器将只返回同一个Bean。
容器在启动的情况下 (即获取Spring上下文的情况下) 就实例化所有singleton的bean对象,并缓存于容器中。
7.1.2 prototype作用域
Spring IOC容器在启动时,不会将bean对象实例化到容器中。