Spring
Spring简介
spring是分层的JavaSE及JavaEE应用于全栈的轻量级开源框架,以IoC(Inverse Of Control:控制反转/反转控制)和AOP(Aspact Oriented Programming:面向切面编程)为核心,提供了表现层SpringMVC和持久层Spring
JDBC以及业务层事务管理等众多模块的企业级应用技术,还能整合开源世界中众多著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架。
SSH(struts2 spring hibernate)
SSM(springmvc spring mybatis)
Spring的本质是管理软件中的对象,即创建对象和维护对象之间的关系
Spring的优势(特点)
- (1)方便解耦,简化开发
- (2)AOP编程的支持
- (3)声明式事务的支持
- (4)方便程序的测试
- (5)方便集成各种优秀框架
- (6)降低JavaEEAPI的使用难度
- (7)Spring框架原码是经典学习范例
spring的架构
Spring 最初的目标就是要整合一切优秀资源,然后对外提供一个统一的服务。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如下图所示:
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
模块 | 说明 | |
---|---|---|
核心容器Spring Core | 核心容器,提供Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式,将应用程序的配置和依赖性规范与实际的应用程序代码分开。 | |
Spring Context | Spring上下文,是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 | |
Spring AOP | 通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。可以很容易地使 Spring框架管理的任何对象支持AOP。Spring AOP模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,就可以将声明性事务管理集成到应用程序中。 | |
Spring DAO | JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。 | |
Spring ORM | Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 | |
Spring Web | Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 | |
Spring MVC框架 | MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。 |
程序的耦合
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差(降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
总结:在软件工程中,耦合指的就是指对象之间的依赖关系。对象之间的依赖程度越高,耦合度就越高。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。
耦合降低程序之间的依赖程度,即降低程序之间的耦合度的过程就叫做解耦。
三层结构的耦合性问题
代码实现
public class UserController {
public static void main(String[] args) throws Exception {
UserService userService = new UserServiceImpl();
User user = userService.selectUserById(1);
System.out.println("user = " + user);
}
}
public class UserServiceImpl implements UserService {
public User selectUserById(int userId) throws Exception {
UserMapper userMapper = new UserMapperImpl();
return userMapper.selectUserById(userId);
}
}
public class UserMapperImpl implements UserMapper {
public User selectUserById(int userId) throws Exception {
return new User(userId, "zhangsan", "zhangsan");
}
}
- 存在问题
- ①在UserController中创建UserService对象,如果UserService类的构造器发生变化,UserController也会随着变化;
- ②通过new方式创建对象需要导入正确的包路径,否则编译不通过就报错。
- 解决方案
- ①使用工厂模式;
- ②使用反射、XML配置文件
工厂模式解耦
- 解耦程序编写步骤:
- 1、 创建一个Maven的Java工程
- 2、创建持久层接口和接口实现类
- 3、创建表现层测试程序并运行测试程序
- 4、创建表现层测试程序(com.tedu.controller.EmpController)并运行测试程序
- 5、通过工程+配置文件+接口(已有)方式解耦
- 6、使用工厂获取service接口和dao接口的实例,替换使用new的方式获取接口的实例。
代码实现
public class MyApplicationContext {
public Object getBean(String beanName) {
if (beanName.equals("userService")) {
return new UserServiceImpl();
} else if (beanName.equals("userMapper")) {
return new UserMapperImpl();
} else if (beanName.equals("user")) {
return new User(1, "zhangsan", "zhangsan");
}
return null;
}
}
public class UserController {
public static void main(String[] args) throws Exception {
UserService userService = (UserService) new MyApplicationContext()
.getBean("userService");
User user = userService.selectUserById(1);
System.out.println("user = " + user);
}
}```
```bash
public class UserServiceImpl implements UserService {
public User selectUserById(int userId) throws Exception {
UserMapper userMapper = (UserMapper) new MyApplicationContext()
.getBean("userMapper");
return userMapper.selectUserById(userId);
}
}
public class UserMapperImpl implements UserMapper {
public User selectUserById(int userId) throws Exception {
return (User) new MyApplicationContext()
.getBean("user");
}
}
存在问题
- if…else…if太多了,难以维护;
- 通过new方式创建对象需要导入正确的包路径,否则编译不通过就报错。
解耦方案使用反射
- 代码实现
public class MyApplicationContext {
public Object getBean(String className) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
public class UserController {
public static void main(String[] args) throws Exception {
UserService userService = (UserService) new MyApplicationContext()
.getBean("com.atguigu.service.impl.UserServiceImpl");
User user = userService.selectUserById(1);
System.out.println("user = " + user);
}
}
public class UserServiceImpl implements UserService {
public User selectUserById(int userId) throws Exception {
UserMapper userMapper = (UserMapper) new MyApplicationContext()
.getBean("com.atguigu.mapper.impl.UserMapperImpl");
return userMapper.selectUserById(userId);
}
}
public class UserMapperImpl implements UserMapper {
public User selectUserById(int userId) throws Exception {
return (User) new MyApplicationContext()
.getBean("com.atguigu.pojo.User");
}
}
存在问题
- 存在大量的字符串的硬编码
解耦方案使用xml配置
- 代码实现
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!--UserServiceImpl对象,id=userService-->
<bean id="userService" class="com.atguigu.service.impl.UserServiceImpl"></bean>
<!--UserMapperImpl对象,id=userMapper-->
<bean id="userMapper" class="com.atguigu.mapper.impl.UserMapperImpl"></bean>
<!--User对象,id=user-->
<bean id="user" class="com.atguigu.pojo.User"></bean>
</beans>
public class MyApplicationContext {
private Map<String, Object> map = new HashMap<String, Object>();
public MyApplicationContext() {
parseXML();
}
/**
* 解析xml文件
*/
private void parseXML() {
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader
.read(MyApplicationContext.class.getClassLoader().getResourceAsStream("beans.xml"));
Element rootElement = document.getRootElement();
List<Element> beanEles = rootElement.elements("bean");
for (Element beanEle : beanEles) {
String id = beanEle.attribute("id").getValue();
String className = beanEle.attributeValue("class");
Object instance = Class.forName(className).newInstance();
map.put(id, instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(String beanName) {
return map.get(beanName);
}
}
public class UserController {
public static void main(String[] args) throws Exception {
UserService userService = (UserService) new MyApplicationContext()
.getBean("userService");
User user = userService.selectUserById(1);
System.out.println("user = " + user);
}
}
public class UserServiceImpl implements UserService {
public User selectUserById(int userId) throws Exception {
UserMapper userMapper = (UserMapper) new MyApplicationContext()
.getBean("userMapper");
return userMapper.selectUserById(userId);
}
}
public class UserMapperImpl implements UserMapper {
public User selectUserById(int userId) throws Exception {
return (User) new MyApplicationContext()
.getBean("user");
}
}
Spring入门案例
- 开发步骤
- 四个核心包(依赖):Spring-core、Spring-beans、Spring-context、Spring-expression
- ①引入依赖
- ②编写spring-core.xml
- ③代码测试
- 初始化Spring容器
①引入Spring相关依赖
<properties>
<spring.version>5.3.13</spring.version>
</properties>
<dependencies>
<!--spring start-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring end-->
</dependencies>
②编写spring-core.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.atguigu.pojo.User"></bean>
</beans>
③代码测试
public class IOCTest {
@Test
public void test1(){
//初始化Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
User user = (User) applicationContext.getBean("user");
System.out.println("user = " + user);
}
}
IOC(控制反转)
IOC(inversion of )控制反转,即,把创建对象的权利交给框架。
也就是指将对象的创建、对象的存储、对象的管理交给了spring容器。(spring容器是spring中的一个核心模块,用于管理对象,底层可以理解为是一个map集合)
- -
Spring使用构造器IOC对象
- 概述
- 标签默认使用类的无参构造器
- 代码实现
<bean id="user" class="com.atguigu.pojo.User"></bean>
注意事项
- 如果类没有无参构造器,报错"NoSuchMethodException: com.atguigu.pojo.User.()"
Spring使用静态工厂IOC对象
代码实现
public class Bean01 {
private String beanName = "a";
}
public class StaticBeanFactory {
public static Bean01 getBean(){
return new Bean01();
}
}
<!--Spring使用静态工厂IOC对象-->
<bean id="bean01" factory-method="getBean" class="com.atguigu.factory.StaticBeanFactory"></bean>
Spring使用动态(实例)工厂IOC对象
代码实现
public class Bean02 {
private String beanName = "bean02";
}
public class DynamicBeanFactory {
public Bean02 getBean(){
return new Bean02();
}
}
<!--Spring使用实例工厂IOC对象-->
<bean id="bean02" factory-method="getBean" factory-bean="dynamicBeanFactory"></bean>
<bean id="dynamicBeanFactory" class="com.atguigu.factory.DynamicBeanFactory"></bean>
FactoryBean机制
- 概述
- 类似于静态工厂IOC对象,
- FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置 一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类 的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创 建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
- Spring整合MyBatis就会使用到FactoryBean机制。
- 开发步骤
- ①定义类实现FactoryBean接口
- 重写方法
- ②将定义类IOC到Spring容器
- ①定义类实现FactoryBean接口
- ①定义类实现FactoryBean接口
public class Bean03FactoryBean implements FactoryBean<Bean03> {
public Bean03 getObject() throws Exception {
return new Bean03();
}
public Class<?> getObjectType() {
return Bean03.class;
}
public boolean isSingleton() {
return true;
}
}
②将定义类IOC到Spring容器
<!--FactoryBean机制-->
<bean id="bean03" class="com.atguigu.factorybean.Bean03FactoryBean"></bean>
bean标签的属性
语法
<bean id="bean唯一标识" name="bean别名" class="bean全限定类名"></bean>
代码实现
<!--bean标签的属性-->
<bean id="user2" name="myUser,myUser2,myUser3" class="com.atguigu.pojo.User"></bean>
@Test
public void test5(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
Object myUser3 = applicationContext.getBean("myUser3");
System.out.println("myUser3 = " + myUser3);
}
Spring的团队开发
- 概述
- 在开发中,Spring的配置文件会有很多个,比如:spring-core.xml、spring-mvc.xml、spring-security.xml等等
- ①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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--团队开发-->
<bean id="user1" class="com.atguigu.pojo.User"></bean>
</beans>
<?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="user2" class="com.atguigu.pojo.User"></bean>
</beans>
/**
* ①Spring团队开发
* Spring容器初始化时加载多个xml配置文件
*/
@Test
public void test6(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring1.xml","spring2.xml");
Object user1 = applicationContext.getBean("user1");
System.out.println("user1 = " + user1);
Object user2 = applicationContext.getBean("user2");
System.out.println("user2 = " + user2);
}
②在主配置文件中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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--团队开发-->
<bean id="user1" class="com.atguigu.pojo.User"></bean>
<import resource="spring2.xml"></import>
</beans>
<?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="user2" class="com.atguigu.pojo.User"></bean>
</beans>
/**
* ②Spring团队开发
* 在主配置文件中import其他的配置文件
*/
@Test
public void test7(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring1.xml");
Object user1 = applicationContext.getBean("user1");
System.out.println("user1 = " + user1);
Object user2 = applicationContext.getBean("user2");
System.out.println("user2 = " + user2);
Object user3 = applicationContext.getBean("user3");
System.out.println("user3 = " + user3);
}
- 注意事项
- 如果在不同的spring配置文件中有相同的bean,遵守后配置后加载的覆盖原则。
Spring引入logback
- 常见日志框架
- jul : jdk自带的日志功能
- log4j : 由Ceki Gulcu创立,后被apache收购
- lo4j2 : apache收购log4j之后,对log4j的升级
- logback: Ceki Gulcu看lo4j2不爽,重新搞的日志框架
- 常见日志门面
- slf4j : 由Ceki Gulcu创立
- jcl : Jakarta Commons Logging,是Apache提供的一个通用日志API
- 日志级别
- error > warn > info > debug > trace
- 比如:日志级别是debug,error、 warn 、 info 、 debug的信息都会打印
- 为什么?
- ①System.out.println()是线程安全,效率极其低下!!!
- ②在项目开发阶段,需要将程序的运行信息打印到控制台,在项目生产阶段,不需要将程序的运行信息打印到控制台,所以就需要关闭日志打印
- ③在项目生产阶段,需要将日志记录到日志文件、数据库中。
- 开发步骤
- ①引入依赖
- slf4j、logback
- ②编写logback.xml
- ③使用日志记录器
- ①引入依赖
- ①引入依赖
<!--logback start-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--logback end-->
- ②编写logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="info">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT"/>
</root>
<logger name="com.atguigu.test.LogbackTest">
<level value="off"/>
</logger>
</configuration>
- ③使用日志记录器
public class LogbackTest {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
@Test
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
Object user = applicationContext.getBean("user");
System.out.println("user = " + user);
logger.debug("debug ... user : " + user);
logger.info("info ... user : " + user);
logger.warn("warn ... user : " + user);
logger.error("error ... user : " + user);
}
}
根据类型获取bean场景一
- 需求
- IOC容器中同类型的bean只有一个
- IOC容器中同类型的bean有多个
- 代码实现
<!--根据类型获取bean场景一-->
<!--同类型的bean只有一个-->
<bean id="bean04" class="com.atguigu.pojo.Bean04"></bean>
<!--同类型的bean有多个-->
<bean id="myBean04" class="com.atguigu.pojo.Bean04"></bean>
@Test
public void test8() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
Bean04 bean = applicationContext.getBean(Bean04.class);
logger.debug(bean + "");
}
总结
- IOC容器中同类型的bean只有一个
- 正常获取
- IOC容器中同类型的bean有多个
NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.pojo.Bean04' available: expected single matching bean but found 2: bean04,myBean04
报错
根据类型获取bean场景二
- 需求
- 有一个接口和一个接口的实现子类,将实现子类放入到IOC容器
- 根据接口类型获取bean
- 根据实现子类类型获取bean
- 代码实现
<bean id="userService" class="com.atguigu.service.impl.UserServiceImpl"></bean>
@Test
public void test9() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
//根据接口类型获取bean
UserService bean = applicationContext.getBean(UserService.class);
logger.debug(bean + "");
//根据实现子类类型获取bean
UserServiceImpl bean2 = applicationContext.getBean(UserServiceImpl.class);
logger.debug(bean2 + "");
}
- 总结
- 根据接口类型获取bean
- 正常获取
- 根据实现子类类型获取bean
- 正常获取
- 根据接口类型获取bean
根据类型获取bean场景三
- 需求
- 声明一个接口,接口有多个实现子类,接口所有实现子类都放入IOC容器
- 根据接口类型获取bean
- 根据实现子类类型获取bean
- 代码实现
<bean id="userService1" class="com.atguigu.service.impl.UserServiceImpl"></bean>
<bean id="userService2" class="com.atguigu.service.impl.UserServiceImpl2"></bean>
@Test
public void test10() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");
//根据接口类型获取bean
UserService bean = (UserService) applicationContext.getBean(UserService.class);
logger.debug(bean + "");
//根据实现子类类型获取bean
UserService bean2 = applicationContext.getBean(UserServiceImpl2.class);
logger.debug(bean2 + "");
}
总结
- 根据接口类型获取bean
NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.service.UserService' available: expected single matching bean but found 2: userService1,userService2
-
- 报错
- 根据实现子类类型获取bean
- 正常获取
bean的生命周期(scope属性)
- scope
- singleton : 默认值,单例
- prototype : 多例
- request
- session
- 代码实现
<bean id="user" class="com.atguigu.pojo.User" scope="singleton"></bean>
public class LifeTest {
private ApplicationContext applicationContext = null;
@Before
public void init() {
applicationContext = new ClassPathXmlApplicationContext("spring-core2.xml");
}
@Test
public void test1() {
Object user = applicationContext.getBean("user");
Object user2 = applicationContext.getBean("user");
System.out.println(user == user2);
}
}
bean的生命周期
- 概述
- 监听bean对象在Spring容器中的创建和销毁
- scope=singleton
- 创建:Spring容器初始化就创建
- 销毁:Spring容器关闭就销毁
- scope=prototype
- 创建:使用时就创建
- 销毁:交给JVM的垃圾回收机制
- 代码实现
public class User {
private Integer userId;
private String userName;
private String userPwd;
public void init(){//监听User对象的创建
System.out.println("User init");
}
public void destroy(){//监听User对象的销毁
System.out.println("User destroy");
}
}