目录
一、为什么学Spring?🍭
-
简化开发:Spring框架提供了许多功能和模块,可以大大简化企业级应用程序的开发过程。它提供了一套成熟的解决方案,涵盖了从依赖注入到面向切面编程等多个方面,使开发人员能够更专注于业务逻辑而非底层技术细节。
-
提高可维护性:Spring框架鼓励使用面向接口的编程方式,通过依赖注入和面向切面编程来实现松耦合的组件之间的交互。这种设计模式使代码更加模块化、可复用和易于维护。
-
便于集成:Spring框架具有广泛的集成能力,可以与各种第三方库、开发工具和框架无缝集成。它支持各种数据访问技术、Web框架、消息传递、安全性等,使开发人员能够灵活地选择适合自己需求的组件,同时降低了集成的复杂性。
-
提升测试效率:Spring框架鼓励编写可测试的代码。通过依赖注入,开发人员可以轻松地替换依赖的组件,使用Mock对象进行单元测试。此外,Spring还提供了一套测试支持类和工具,简化了集成测试和系统测试的过程。
-
支持企业级需求:Spring框架是为构建复杂的企业级应用程序而设计的。它提供了丰富的功能,如事务管理、安全性、缓存、调度等。这些功能使得开发人员能够轻松地处理企业级需求,并确保应用程序的可靠性和性能。
-
社区支持:Spring框架拥有庞大的用户社区和活跃的维护团队。这意味着您可以从其他开发人员的经验中获取帮助和支持,找到解决问题的最佳实践,并及时获得更新和修复bug的版本。
二、Sprin是什么?
官方文档🧁
Spring | Why Springhttps://spring.io/why-spring
Spring概述🍭
Spring框架是一个用于构建企业级Java应用程序的开源框架。它提供了一套全面的解决方案,使开发人员能够更轻松地创建可维护、灵活和可扩展的应用程序。
特点和功能:
-
轻量级:Spring框架采用了基于POJO(Plain Old Java Object)的开发模式,避免了对特定的容器或框架的依赖,从而使应用程序更加轻量级。
-
依赖注入(Dependency Injection):通过依赖注入,Spring框架能够管理对象之间的依赖关系。这种方式可以降低组件之间的耦合度,并使测试变得更加容易。
-
面向切面编程(Aspect-Oriented Programming):Spring支持面向切面编程,使开发人员能够将与业务逻辑无关的功能(例如日志记录、安全性、事务管理等)从应用程序主体中分离出来。
-
容器:Spring框架提供了一个容器(ApplicationContext),该容器负责创建和管理应用程序中的对象。它还支持各种配置选项,如XML配置、Java注解和基于Java的配置。
-
数据访问:Spring提供了对各种数据访问技术的集成支持,包括JDBC、ORM框架(如Hibernate和MyBatis)以及NoSQL数据库。
-
Web开发:Spring框架还提供了用于构建Web应用程序的模块,如Spring MVC。它简化了开发过程,并提供了灵活的配置选项。
-
测试:Spring框架鼓励编写可测试的代码,它提供了一套测试支持类和工具,如Mock对象、测试注解和集成测试支持。
注:Spring的核心是IoC(控制反转)和AOP(面向切面编程)!!!
Ⅰ、控制反转 (IoC) 和依赖注入 (DI)
在之前,开发人员需要通过new来创建对象的生命周期,每new一次对象就会重新开辟一个空间。导致代码与对象的创建和依赖关系紧密耦合,难以维护和拓展,还很浪费内存。
package domain;
import org.junit.Test;
public class UserTest {
@Test
public void TestDemo(){
//创建第一个对象
User user=new User();
user.setId(1);
user.setName("张三");
//创建第二个对象
User user1=new User();
user1.setName("李四");
//判断这两个对象是否是同一个对象
System.out.println(user1==user);
//输出
System.out.println(user.toString());
System.out.println(user1.toString());
}
}
使用我们的控制反转 (IoC) 和依赖注入 (DI) 就能很好的解决这个问题!
控制反转(IoC,Inversion of Control):将创建对象交给Spring框架负责,而不是开发人员手动管理。
依赖注入(DI,Dependency Injection):是控制反转的一种实现方式,通过将对象的依赖关系注入到对象中,使对象能够获取所需的依赖,而无需自己创建和管理依赖对象。依赖注入通过配置文件或注解的方式,将对象之间的依赖关系描述清楚,并由框架负责解析和注入。
如何使用控制反转和依赖注入?
可以通过三种方式实现依赖注入:① get、set 注入 ② 构造方法注入 ③ p命名空间注入 ④注解注入。
1、先导入jar包 (用的Maven项目)
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<!-- 1.Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 2.Spring dao依赖-->
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!-- 3.Spring web依赖 -->
<!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
① get、set注入方式:
- 一般用于对象,编写实体类
package com.cskt.domain;
import lombok.Data;
/**
* domain————实体类
*/
@Data
public class User {
private int id;
private String name;
private String content;
public void print(){
System.out.println(name+":“"+content+"“");
}
}
- 配置文件 编写 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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 通过id属性为该实例指定一个名称,class放的是全路径(反射模式) -->
<bean id="user" class="com.cskt.domain.User">
<property name="name" value="张三"></property>
<property name="id" value="2"></property>
</bean>
</beans>
- 测试
package domain;
import com.cskt.domain.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
@Test
public void Test(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
User user =(User) context.getBean("user");
System.out.println(user);
}
}
② 构造注入方式
- 构造注入的方式一般用于 dao 层和service 层的
package com.cskt.service.impl;
import com.cskt.domain.User;
import com.cskt.mapper.UserMapper;
import com.cskt.service.UserService;
public class UserServiceImpl implements UserService {
//实例化所依赖的UserMapper对象
//重复的new对象 构造注入 就是解决Service 和 dao 的问题
private UserMapper userMapper;
//添加构造函数
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public int insert(User sysUser) {
int count = userMapper.insert(sysUser);
return count;
}
}
- class属性放的是daoImpl的全路径,不是dao层
<!-- 构造注入 1、先把dooImpl 注入进来 2、在service 写一个构造函数依赖注入 -->
<bean id="userMapper" class="com.cskt.mapper.impl.UserMapperImpl"></bean>
<bean id="userService" class="com.cskt.service.impl.UserServiceImpl">
<constructor-arg ref="userMapper"></constructor-arg>
</bean>
- 测试类只需要把之前new serviceImpl() 换成 context.getBean(""),从IoC容器拿
@Test
public void insert() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService =(UserService) context.getBean("userService");
userService.insert(new User());
}
③ 注解(重点)
注: 注解是我们项目中常用的,所以我们重点记住注解。但是前面哪几种还是要了解的!!!
-
@Component:实现Bean组件的定义
-
@Repository:用于标注DAO类
-
@Service:用于标注业务类
-
@Controller:用于标注控制器类
( 他是通过这四个注解来实现的 )
- 因为需要操作的类,只有MapperImpl和serviceImpl实现类。所以我们只需要修改dao和service的实现类就可以了。给他们加上相应的注解。
package com.cskt.mapper.impl;
import com.cskt.domain.User;
import com.cskt.mapper.UserMapper;
import org.springframework.stereotype.Repository;
@Repository //等效于 <bean id="userMapper" class="com.cskt.mapper.impl.UserMapperImpl" />
//如果变量名规范的话就可以不用加括号 ("userMapper")
public class UserMapperImpl implements UserMapper {
@Override
public int insert(User sysUser) {
//这里未实现完整的数据库操作,仅为说明原因
System.out.println("执行了新增的方法。。。。");
return 0;
}
}
package com.cskt.service.impl;
import com.cskt.domain.User;
import com.cskt.mapper.UserMapper;
import com.cskt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service("userService") /*等效于 <bean id="userService" class="com.cskt.service.impl.UserServiceImpl">
<constructor-arg ref="userMapper"></constructor-arg>
</bean> */
public class UserServiceImpl implements UserService {
//实例化所依赖的SysUserMapper对象
//重复的new对象 构造注入 就是解决Service 和 dao 的问题
@Autowired //自动装配 只要命名规范 就会自动装配
//@Qualifier("userMapper") //如果获取不到, 如果指定装配哪一个
private UserMapper userMapper;
@Override
public int insert(User sysUser) {
int count = userMapper.insert(sysUser);
return count;
}
}
- 给MapperImpl和serviceImpl添加好了注解。一定一定要记得添加扫描,不然会注入不进入。开启就代表着自动帮你识别放入包中的所有注解。没开启扫描的话,那我们的注解是没有用的!!!!
<!-- 扫描包中注解标注的类,如果不加的话会导致注入不进去 -->
<context:component-scan base-package="com.cskt.mapper,com.cskt.service" />
测试类就跟构造注入的测试类一样,并不需要改。
Ⅱ、AOP (面向切面编程)🧁
AOP全称Aspect Oriented Programming,也叫面向切面编程。AOP是在方法中找到共同的代码提取出来,编成一个切面。通过动态代理的方式,把抽离出来的共性代码 “织入”到业务中。
AOP相关术语
- Aspect(切面)
- Advice(增强处理)
- Pointcut(切入点)
- Join Point(连接点)
- Target Object(目标对象)
- AOP proxy(AOP 代理)
- Weaving(织入)
AOP的增强处理类型及特点
( 这里也是通过注解的方式 )
- 前置增强:前置增强是在目标方法执行之前执行的增强。它可以用于在目标方法执行前进行一些预处理操作,例如验证输入参数、记录日志或者进行安全性检查。前置增强没有能力改变目标方法的执行结果。
/** * 前置通知 */ @Before("execution(* com.cskt.service..*.*(..)))") public void before(){ System.out.println("前置通知.... 在方法之前要執行的公共代码放在这里"); }
- 后置增强:后置增强是在目标方法执行之后执行的增强。它可以用于在目标方法执行完毕后进行一些操作,例如记录日志、清理资源或者转换返回值等。后置增强不能修改目标方法的执行结果。
/** * 后置通知 * returnVal,切点方法执行后的返回值 */ @AfterReturning(value="execution(* com.cskt.service..*.*(..)))",returning = "returnVal") public void AfterReturning(Object returnVal){ System.out.println("后置通知...."+returnVal); }
- 异常抛出增强:异常增强是在目标方法抛出异常后执行的增强。它可以用于捕获目标方法抛出的特定异常并采取相应的处理措施,例如记录日志、发送警报或者进行回滚操作。
/** * 抛出通知 * @param e */ @AfterThrowing(value="execution(* com.cskt.service..*.*(..))",throwing = "e") public void afterThrowable(Throwable e){ System.out.println("出现异常:msg="+e.getMessage()); }
- 最终增强:最终增强是在目标方法执行之后(无论是否抛出异常)都会执行。最终增强通常用于释放资源、清理操作或者进行一些必要的收尾工作。
/** * 无论什么情况下都会执行的方法 */ @After(value="execution(* com.cskt.service..*.*(..))") public void after(){ //方法的对象的关闭 //方法的回滚 System.out.println("最终通知...."); }
- 环绕增强:环绕增强是最强大和最灵活的增强类型。它可以在目标方法执行前后都执行自定义逻辑,并且可以完全控制目标方法的调用。环绕增强可以决定是否调用目标方法、如何处理输入参数、错误处理等。它还可以修改目标方法的返回值。
/**
* 环绕通知
* @param joinPoint 可用于执行切点的类
* 具体什么业务
* @return
* @throws Throwable
*/
@Around("execution(* com.cskt.service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知前....");
Object obj= (Object) joinPoint.proceed();
System.out.println("环绕通知后....");
return obj;
}