我下面写的笔记, 讲真的,也就注解有点用,因为你开发肯定用spring-boot, 所以你那个知识点不懂, 就从下面笔记中找一找
1.spring系统架构
自底而上进行,上层依赖于下层,首先最底层是Core Container -- 核心容器, 再往上是AOP(面向切面编程)和Aspects(AOP)思想的实现, 我个人的理解是, 它可以在不惊动你原始程序的基础上, 给它增强功能,类似于反射;再往上是数据访问层。
Core Container中包含:1. IoC/DI 2.IoC容器 3.Bean
有人就要问了, 为啥会出现Core Container这个概念呢?
提供两段代码来解释一下?
1. 业务层
public class BookServiceImpl implements BookService{
private BookDao bookDao = new BookDaoImpl();
//父类的接口的引用指向子类的实现 (多态中的向上转型)
public void save(){
bookDao.save();
}
}
2. 数据层实现
public class BookDaoImpl implments BookDao{
//在BookDao接口中定义抽象方法save, 在子类中进行实现
public void save(){
System.out.prinln("book dao save....")
}
}
public class BookDaoImpl2 implments BookDao{
//另一个类去实现BookDao, 重写父接口中的save接口
public void save(){
System.out.prinln("book dao save....2")
}
}
如果你想要替换数据层的实现, 就要去修改业务层的代码
代码书写现状: 耦合度高
解决方案:
使用对象时, 在程序中不要主动使用new产生对象,转换为由外部提供对象, (在业务层内部只去写引用)
IoC -> 控制反转
对象的创建控制权由程序转移到外部, 这种思想称为控制反转
Spring技术对IoC思想进行了实现
-Spring提供了一个容器, 称为IoC容器, 用来充当IoC思想中的"外部", 简单来说这个容器就是来创建对象用的
IoC容器负责对象的创建,初始化等一系列工作, 被创建或被管理的对象在IoC容器中统称为Bean
DI (依赖注入)
如果容器中有两个Bean存在着依赖关系,IoC会自动给你进行绑定
目标: 充分解耦
1. 使用IoC容器管理bean(IOC)
2. 在IoC容器内将依赖关系的bean进行关系绑定(DI)
最终效果
使用对象时不仅可以直接从IOC容器中获取, 并且获取到的bean已经绑定了所有的依赖关系
在IDEA中如何实现呢?
1. 手把手教你创建springboot的工程
file->Module(模版)->spring initializr(初始化)->添加spring web模块->Create创建就好了springboot工程
2. 认识一下pom.xml这个配置文件
<dependencies></dependencies> 所有的依赖(导包-坐标)全写到这个里面, <build></build> 里面是相关的插件
3. 然后在resources中, XML Configuration File中创建Spring Config配置文件applicationContext.xml文件, 一定要在pom.xml中导入spring的坐标spring-context(不用写版本号,spring-boot会自动帮你进行管理版本的)4.在applicationContext.xml中配置想要管理对象的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: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 https://www.springframework.org/schema/util/spring-util.xsd">
<!-- 配置bean-->
<!--
id属性表示给bean取一个名字
加载对应的类(相关类信息),class属性给bean定义类型,
解读:在spring容器中的bean, 是为了创建对象, bean的类型就是对象的类名
-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDapImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServicelmpl">
<!--
1. 配置bookDao和bookService之间的关系
name属性表示配置bookService中的哪一个具体的属性
ref属性表示参照哪一个bean的id(待注入的)
-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
在创建一个App2文件
package com.itheima;
import com.itheima.dao.impl.BookDao;
import com.itheima.service.impl.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SuppressWarnings({"all"})
public class APP2 {
public static void main(String[] args) {
//获取ioc容器, 配置文件当做参数 => 接口 a=new 实现子类() ,经典多态(向下转型)
//接口是不能new对象的, 只有类才可以
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //作用:加载配置文件, 参数为config文件名
//从ioc容器中获取bean, 使用id获取
BookDao bookDao = (BookDao)ctx.getBean("bookDao"); //多态的向下转型
bookDao.save();
BookService bookService = (BookService)ctx.getBean("bookService");
bookService.save();
}
}
删除使用new的方式创建的bookDao对象,同时提供set方法
package com.itheima.service.impl;
import com.itheima.dao.impl.BookDao;
import com.itheima.dao.impl.BookDapImpl;
@SuppressWarnings({"all"})
public class BookServicelmpl implements BookService{
// private BookDao bookDao = new BookDapImpl();
private BookDao bookDao;
public void save(){
System.out.println("book service save....");
bookDao.save();
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}//这个set方法是容器进行调用的
}
//在BookServiceImpl需要一个BookDao对象,IOC容器会负责创建这个对象并将其注入到BookServiceImpl中, 这样就实现了依赖注入
在applicationContext.xml文件中配置依赖, 往Service中注入BookDao的依赖
bean的配置:
功能:定义Spring核心容器管理的对象
格式:
<beans>
<bean/>
<bean></bean>
</beans>
id: bean的id, 使用容器可以通过id值获取对应的bean, 在一个容器中id值唯一
class: bean的类型, 即配置的bean的全路径类名
name: 给bean取别名, 可定义多个, 使用逗号(,) 分号(;) 空格()分隔
<bean id="bookDao" name="bookDao1,bookDao2" class="com.itheima.dao.impl.BookDapImpl"/>后续的话, 就可以使用.getBean(bookDao1)获取对应的实现类
bean的作用范围: 在IOC容器中创建的bean, 默认是单列的(就是你getBean两个同一个实现子类, 他的引用(对象)是一样的), 如果想变成双列的, 就需要添加scope属性
<bean id="bookDao" class="com.itheima.dao.impl.BookDapImpl" scope="singleton"/> => 默认 => 对象一样<bean id="bookDao" class="com.itheima.dao.impl.BookDapImpl" scope="prototype"/>
=> 对象不一样
为什么bean默认是单列的?
我的理解是, 在容器中创建了一个bean, 你想要getBean一个对象, 然后调用相关的方法, 下一次你还想调用方法的话, 可以直接使用先前getBean的对象, 不用再创建一个新的Bean了
bean的实例化
bean本质上就是对象
1.spring创建bean使用构造方法完成
2. 使用(静态)工厂创建bean
3. 使用(实例)工厂创建bean
package com.itheima.dao.impl;
public class BookDapImpl implements BookDao{
private BookDaoImpl(){
System.out.println("book dao constructor is running.....");
}
public void save() {
System.out.println("book dao save.....");
}
}
你使用getBean进行创建对象的时候, 其实会调用BookDaoImpl这个构造方法,
所以就证明了spring底层也是通过new的方式进行创建对象的
注:这里使用了反射的爆破, 所以才会加载private构造方法
注:更准确的是spring底层调用的是无参构造方法
<bean id="bookDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao()"/> //factory-method属性指定工厂中那个方法是进行创建对象
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoIml();
}
}
public class UserDaoFactory{
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
先创建工厂bean
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userFactory" factory-method="getUserDao" factory-bean="UserFactory"/>
bean生命周期
package com.itheima.dao.impl;
public class BookDapImpl implements BookDao{
public void save() {
System.out.println("book dao save.....");
}
public void init(){
System.out.println("init....");
}
public void destory(){
System.out.println("destory....");
}
}
配置文件中:添加init-method/destory-method属性
<bean id="bookDao" class="com.itheima.dao.impl.BookDapImpl" init-method="init" destory-method="destory"/>
注:想在容器中关闭spring容器,
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook(); //如果在关虚拟机之前先把它的容器关了, 动态一点儿
//ctx.close();
接口形式:
public class BookDapImpl implements BookDao,InitalizingBean, DisposableBean{
public void save(){};
public void afterPropertiesSet throw(){};
public void destory(){};
}
bean声明周期
初始化容器
1. 创建对象(内存分配)
2. 执行构造方法
3. 执行属性注入(set操作)
4. 执行bean初始化方法(init-method="init")
使用bean
1. 执行业务操作
关闭/销毁容器
1. 执行bean销毁方法
@Controller
@RequestMapping("/user")
public class A{
@GetMapping("/page")
public B...
@GetMapping("/api")
public Result<List<User>> userApi(@ResponseBody ...){
return Result.success(...)
}
}
@Component
// 将当前类交给IOC容器管理,成为IOC容器中的bean
@Autowired
// 运行时,IOC容器会提供该类型的bean对象, 并赋值给该变量 - 依赖注入
下面是@Component注解的衍生注解,功能一样
@Controller:标注在控制器类上
@Service:标注在业务类上
@Repository: 标注在数据访问类上@RestController = @Controller + @ResponseBody
语义:它明确地宣告:“我这个类是一个控制器,并且我这个类下的所有方法的返回值,默认都是直接写入HTTP响应体的数据,而不是视图名称。”
注解后不加value, 默认bean名为管理的类的类名首字母小写
eg: EmpDaoA -> empDaoA
改的话:@@Repository(value="daoA")
在maven项目中,代码都是放到java目录下的,相关配置文件
eg: xml/yml 都放到resources目录下上面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描
@ComponentScan注解虽然没有显示配置,但是实际上已经包含在启动类声明注解
@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包乱创建文件:
@ComponentScan({"dao,"com.itheima})
@Autowired private EmpService empService , EmpService 有两个实现类,分别是EmpServiceA和EmpServiceB,类前面加@Service IOC容器就会管理这个类的Bean,@Service public class EmpServiceA implements EmpService @Service public class EmpServiceB implements EmpService, 如果声明@Autowired EmpService empService, 在自动注入的时候,会报错,原因不应该是IOC不知道注入上面那个实现类的对象,为啥我看讲解是@Autowired注解,默认是按照类型进行,如果存在多个类型的bean, 将会报错,我的疑问是EmpServiceA 和 EmpServiceB不应该是不同类型吗?
Spring的视角和报错的真正原因:
Spring的 @Autowired 机制比“按类型查找”要更智能一点。它的完整过程可以理解为:先按类型(ByType)查找,如果找到多个相同类型的Bean,再尝试按名称(ByName)进行匹配。让我们分解一下当您写下 @Autowired private EmpService empService; 时,Spring做了什么:
按类型查找(ByType): Spring会去它的IoC容器里寻找所有匹配 EmpService 类型的Bean。
它会找到两个Bean:一个是 EmpServiceA 的实例,另一个是 EmpServiceB 的实例。
因为 EmpServiceA implements EmpService,所以 EmpServiceA 的实例 是 EmpService 类型。
同理,EmpServiceB 的实例 也是 EmpService 类型。
至此,Spring找到了两个类型兼容的Bean。它陷入了困惑:“用户这里只要一个Bean,但我找到了两个,我到底该注入哪一个呢?”
尝试按名称匹配(ByName): 在找到多个相同类型的Bean后,Spring不会立即放弃,它会再看一下变量的名字,也就是 empService,然后尝试在多个候选Bean中找一个名字与之匹配的。
您的两个Bean在容器中的默认名字是什么?
@Service public class EmpServiceA -> 默认Bean名字为 empServiceA
@Service public class EmpServiceB -> 默认Bean名字为 empServiceB
您的变量名是 empService,和 empServiceA 或 empServiceB 都不匹配。
按名称匹配也失败了。
抛出异常: 两种方式都无法确定唯一的一个Bean,所以Spring会抛出 NoUniqueBeanDefinitionException
解决方案
如果同类型的bean存在多个
1. @Primary -> 提升优先级 -> 写到接口的实现类上
2. @Autowired + @Qualifier("bean的名称") -> 类型+名称 -> 跟上面解释的一致
3. @Resource("name="bean的名称") -> 默认按照名称注入
@Slf4j 是一个注解,它来自 SLF4J 这个日志库。它的作用是:在编译时,自动为你所在的类生成一个静态的、最终的日志对象(logger),这个日志对象的名字通常就是 log。
这样,你就可以直接在代码中使用 log.info(), log.debug(), log.error() 等方法输出日志,而无需手动编写创建日志对象的重复代码
2025.9.24
在spring-boot中管理对象:
控制翻转,给UserService层的实现类加了@Component注解后的,在IOC中生成bean, 然后我用的时候,直接写UserService userService, 然后加上@AutoWired注解后,从IOC容器中找对应的对象,不就相当于UserService uservice = new UserServiceImp();
spring中:
在导入了spring-context的依赖后, 我们就可以创建applicationContext.xml配置文件,手动管理Bean, <bean id=" " class=" ">, 见名知意, id属性为bean的名字,class为bean的类型然后,获取IOC容器,同时加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
然后获取bean
注意:IOC容器里面存的是带类型的Bean, 但是当你拿出来,也就是
ctx.getBean("id") 时,类型为Object, 想用什么类型,就自己强转一下
观察下面的代码:
public class service{
private BookDao bookdao = new BookDao();
}
怎么去掉new呢,在service中提供BookDao 的 set方法,
public class service{
private BookDao bookdao ;
public void setBookDao(BookDao ..){
this.bookdao=...
}
}
然后在service的bean中,配置我要用的bookdao对象
<bean id="" class="">
<property name="bookdao" ref="xxx"/> 见名知意, name是属性名 private BookDao bookdao ;
ref是待注入bean的id
</bean>
<bean id="xxx" class=" "/>
给.xml配置文件中的bean起别名,<bean id="xxx" name="xx xxx xxxx"/>, 功能类比id属性
bean的作用范围默认是单例的,如何改成非单例的呢,
<bean id="xxx" name="xxx" class=" " scope="prototype"/>
效果就是每次.getBean("id") 出来的对象不是同一个对象
单例模式就是IOC对象的复用
实例化Bean的三种方式:
1. 无参构造方法
2. 静态工厂
例如:先写一个工厂类
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
OrderDao orderdao = OrderDaoFactory.getOrderDao();
这就创建了一个对象 -> 接口+实现类+静态工厂
使用静态工厂实例化bean
<bean id="orderDao" class="com.xxx.xxx.factory.OrderDaoFactory" factory-method="getOrderDao()"/>
注:除了要指定工厂类名,还要指定工厂类中的那个方法是造对象的
使用的话,还是.getBean("id")
3. 使用实例工厂实例化bean
还是写一个工厂类
public class UserDaoFactory{
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
先把工厂的bean造出来
<bean id="userFactory" class="com.xxxx.factory.UserDaoFactory"/>
在指定工厂类中那个方法是造bean的, 同时指明工厂bean
<bean id=“userDao" factory-method="getUserDao" factory-bean="userFactory">
对第3种进行改良后
创建UserDaoFactoryBean类
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
.... getObject...{
return new UserDaoImpl(); // 得到Bean的实例
}
... getObjectType{
return UserDao.class // 得到Bean的类型的
}
.. boolean isSingleton(){
return true; // true : 指定.getBean创建出的对象是单例的,false: 非单例的
}
}
.xml里面
<bean id=“userDao" class="xxx.factory.UserDaoFactoryBean"/>
bean造出来的对象是getObject里面指定的
2025.9.25
bean生命周期管理
就是bean在创建时和先销毁之前进行的操作
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println("xxxx");
}
public void init(){
System.out.println("init.....");
}
public void destory(){
System.out.println("destory.....");
}
}
然后在xml文件,中指定bean
<bean id="xxx" class="xxx.xxx.BookDaoImpl" init-method="init" destory-method="destory"/>
想执行这个 System.out.println("destory.....");
要关闭容器/设置注册关闭钩子
如果不在bean进行手动配置,就要实现InitalizingBean, DisposableBean这两个接口
依赖的注入的其他几种方式:
1. setter -> 引用:
我想在service类中使用多个引用类型
public class service{
private BookDao bookdao ;
private UserDao userDao;
public void setBookDao(BookDao ..){
this.bookdao=...
}
public void setUserDao(UserDao ..){
this.Userdao=...
}
}
.xml:
<bean id="" class="">
<property name="bookdao" ref="xxx"/> 见名知意, name是属性名 private BookDao bookdao ;
ref是待注入bean的id
<property name="userDao" ref="xx"/>
</bean>
<bean id="xxx" class=" "/>
<bean id="xx" class=" "/>
2. setter -> 普通变量:
public class BookDaoImpl implements BookDao{
private int connection_Num;
private String url;
public void setconnection_Num(){
this.xxx=xxx;
}
public void setUrl(){
this.xxx=xxx;
}
}
.xml
ref: 是做引用bean的,如果想给值用value
<bean id="" class="">
<property name="connection_Num" value="xxx"/> 见名知意, name是属性名
value:属性值
<property name=" url" value="xx"/>
</bean>
2. 构造器注入
引用:
public class BookServiceImpl implements BookService{
private BookDao bookdao;
public BookServiceImpl(BookDao bookdao){
this. bookdao= bookdao;
}
}
.xml
<bean id="bookService" class="xxx.xxx.xxx.BookServiceImpl">
<constructor-arg name="bookdao" ref="bookdao"/> 见名知意, name是构造函数中的形参名
多个引用时:
<constructor-arg name="xxx" ref="xxx"/>
.....
ref是待注入bean的id
</bean>
普通变量:
<bean id="bookdao" class="xxx.xxx.xxx.bookdaoImpl"/>
public class BookDaoImpl implements BookDao{
private int connection_Num;
private String database_Name;
public BookDaoImpl (String database_Name, int connection_Num){
this.database_Name=database_Name;
this.connection_Num=connection_Num;
}
}
.xml
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl">
<constructor-arg name="database_Name" value="mysql"/>
<constructor-arg name="connection_Num" value="10"/>
</bean>
为了解决:如果形参名改变, .xml文件中的name属性, 也要跟着改变的情况
<bean id="bookdao" class="xxx.xxx.xxx.bookdaoImpl"/>
public class BookDaoImpl implements BookDao{
private int connection_Num;
private String database_Name;
public BookDaoImpl (String database_Name, int connection_Num){
this.database_Name=database_Name;
this.connection_Num=connection_Num;
}
}
.xml
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl">
<constructor-arg type="int" value="mysql"/>
<constructor-arg type="java.lang,String" value="10"/>
type: 分别指定构造函数中形参的类型
</bean>
为了解决:构造函数中多个形参类型相同的情况
<bean id="bookdao" class="xxx.xxx.xxx.bookdaoImpl"/>
public class BookDaoImpl implements BookDao{
private int connection_Num;
private String database_Name;
public BookDaoImpl (String database_Name, int connection_Num){
this.database_Name=database_Name;
this.connection_Num=connection_Num;
}
}
.xml
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl">
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="10"/>
index: 分别指定构造函数中形参的索引 -> 第几个
</bean>
自动装配:
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookdao){
this. bookdao= bookdao;
}
}
xml:
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl"/> -> 1
<bean id="bookService" class="xxx.xxx.xxx.BookServiceImpl" autowire="byType"/> - >2
效果上是,把1注入到2中
还是先提供 引用类型的set方法, 然后写bean , 同时添加 autowire="byType"
按类型自动装配
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl"/>
<bean id="bookDao1" class="xxx.xxx.xxx.BookDaoImpl"/>
<bean id="bookService" class="xxx.xxx.xxx.BookServiceImpl" autowire="byType"/>
两个bean,就要按名称进行装配,
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl"/>
<bean id="bookDao1" class="xxx.xxx.xxx.BookDaoImpl"/>
<bean id="bookService" class="xxx.xxx.xxx.BookServiceImpl" autowire="byName"/>
匹配的时候就是,bean 的 id 和 private BookDao bookDao; 这个引用类型变量名bookDao保持一致
注:自动装配用于引用类型依赖注入, 不能对简单类型进行操作
集合注入:
当BookDaoImpl属性中出现list, set, array... 时候
<bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl">
<property name="array">
<array>
<value>100</value>
<value>200<value>
<value>300<value>
<ref bean="beanId"/> 注:给集合添加引用类型
</array>
</property>
<property name="list"> 注:set注入的name属性还是类中属性的变量名
<list>
<value>100</value>
<value>200</value>
<value>300</value>
</list>
</property>
<property name="map"> 注:set注入的name属性还是类中属性的变量名
<map>
<entry key="xxx" value="xx"/>
<entry key="xxx" value="xx"/>
<entry key="xxx" value="xx"/>
</map>
</property>
<property name="properties"> 注:set注入的name属性还是类中属性的变量名
<props>
<prop key="xxx">xx</prop>
<prop key="xxx">xx</prop>
</props>
</property>
<bean/>
管理第三方bean
管理DruidDataSource 对象
注:因为你指定的是基本数据类型,所以property标签中就写value属性
<bean id="dataSource" class="com.xxx.xxx.DruidDataSource">
<property name="driverCalssName" value="com.mysql.jdbc.Driver"/>
<property name=" url" value="jdbc:mysql://localhost:3306/spring_db"/>
<property name=" url" value="root"/>
<property name=" url" value="root"/>
<bean/>
使用jdbc.properties文件替换上面bean中的value值
.xml
1. 开启context命名空间
2. 使用context空间加载properties文件
3. 使用属性占位符${}读取properties文件中的属性
<?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="jdbc.properties" />
// 会自动加载resources中properties文件
<bean class="com.xxx.xxx.DruidDataSource">
<property name="driverCalssName" value="${jdbc.driver}"/>
<property name=" url" value="${jdbc.url}"/>
<property name=" url" value="${jdbc.username}"/>
<property name=" url" value="${jdbc.password}"/>
<bean/>
</beans>
jdbc.properties中
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db
jdbc.username=root
jdbc.password=root
如果想要加载多个配置文件
<context:property-placeholder location="jdbc.properties, jdbc2.properties" />
或者(标准-格式)
<context:property-placeholder location="classpath:*.properties" system.properties-mode="NEVER"/> ---- 1
<context:property-placeholder location="classpath*:*.properties" system.properties-mode="NEVER"/> ---- 2
方式2是可以读取.jar包中的properties文件的
system.properties-mode="NEVER"
上面这个配置的意思是不加载系统属性
容器:
1. ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
除了上面这种创建容器加载配置文件的方法,还可以使用绝对路径进行加载
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\...\\applicationContext.xml");
2, BookService bookService = (BookService)ctx.getBean("bookService");
另一种写法:
BookService bookService = ctx.getBean("bookService",bookService.class);
注:第二个参数指定获取Bean的类型
另一种写法:
BookService bookService = ctx.getBean(BookService.class);
注:按类型找Bean
// 上面按类型查找的写法,Spring会查找所有实现BookService接口的Bean如果Bean实现了该接口或是该类的子类,就匹配成功
2025.9.25
注解开发:
将.xml文件中的bean写到类中
@Component("bookDao")
public class BookDaoImpl implements BookDao{
....
}
上面注解的意思就是:配置了一个名称为bookDao的Bean
等价于 <bean id="bookDao" class="xxx.xxx.xxx.BookDaoImpl"/>
.xml
在xml文件中进行组件扫描的配置
<context:component-scan base-package="com.itheima.dao.impl"/>
base-package 属性 指定的是你扫描的位置
<context:component-scan base-package="com.itheima"/>
上面这种写法,会扫描的com.itheima 这个包下的所有类/子类
另一种写法是:
@Component
public class BookDaoImpl implements BookDao{
....
}
使用按类型访问
BookDao bookdao=ctx.getBean(Bookdao.class);
Spring 提供了@Component 注解的三个衍生注解
1. @Controller: 用于表现层bean定义
2. @Service: 用于业务层bean定义
3. @Repository: 用于数据层bean定义
Spring3.0 升级了纯注解开发模式,使用Java类代替配置文件
配置类如下:
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig{
}
调用的话:
先创建容器加载注解配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
其他和上面一样
@Configuration 注解 用于设定当前类为配置类
@ComponentScan 注解用于设定扫描路径,此注解只能添加一次
@ComponentScan({"com.itheima.service","com.itheima.dao"}) 扫描多个包/类
Bean的作用范围和生命周期:
@Repository
@Scope("prototype") 这个注解是非单例的
@Scope("singleton") 这个注解是单例的
public class BookDaoImpl implements BookDao{
....
}
@Repository
@Scope("prototype") 这个注解是非单例的
public class BookDaoImpl implements BookDao{
....
@PostConstruct 构造方法后
public void init(){
}
@PreDestory 销毁操作前
public void init(){
}
}
注解开发---依赖注入
引用类型
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookdao;
....
}
用了自动装配的@Autowired注解后, 就不用写什么setter/构造器方法了
@Service
public class BookServiceImpl implements BookService{
@Autowired
@Qualifier("bookDao")
使用@Qualifier注解, 指定注入bean的名称
private BookDao bookdao;
....
}
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
public void save(){}
....
}
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao{
public void save(){}
....
}
按名称注入, 注入上面的配置的bean
简单类型
使用@Value注解去给简单类型进行赋值/注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
@Value("itheima");
String name;
public void save(){}
....
}
在配置类SpringConfig中加载properties文件
@Configuration
@ComponentScan("com.itheima")
@PropertySource("jdbc.properties") 属性源注解,指定属性文件
@PropertySource({"jdbc.properties",""jdbc.properties""}) 加载多个配置文件, 使用数组的形式
public class SpringConfig{
}
public class BookDaoImpl implements BookDao{
@Value("${name}");
String name;
public void save(){}
....
}
管理第三方的Bean
在SpringConfig配置类中进行编写
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig{
1. 定义一个方法获得要管理的对象
2. 添加@Bean,表示当前方法的返回值是一个bean, 这个方法返回的对象需要由你管理
相当于XML配置中的
<bean>标签@Bean
public DataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName("com,mysql.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
获取这个Bean, 还是.getBean(DataSource.class)
在一个配置类中导入另一个配置类
@Import("JdbcConfig.class")
@Import({ConfigA.class, ConfigB.class, ServiceImpl.class}) 导入多个用数组形式
public class SpringConfig{
}
public class JdbcConfig{
1. 定义一个方法获得要管理的对象
2. 添加@Bean,表示当前方法的返回值是一个bean, 这个方法返回的对象需要由你管理
相当于XML配置中的
<bean>标签@Bean
public DataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName("com,mysql.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
前提是你在主方法中加载的是SpringConfig配置类
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
配置文件中简单类型的注入public class JdbcConfig{
1. 定义一个方法获得要管理的对象
2. 添加@Bean,表示当前方法的返回值是一个bean, 这个方法返回的对象需要由你管理
相当于XML配置中的
<bean>标签@Value("com.mysql.Driver")
private Sting driver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private Sting url;
@Value("root");
private String userName;
@Value("root");
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
配置文件中引用类型的注入
public DataSource dataSource(BookService bookService){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
引用类型注入只需要为bean定义方法设置形参即可, 容器会根据类型自动装配对象
2025.9.27
Spring整合MyBatis
pom.xml中导入
<artifactId>spring-jdbc</artifactId>
<artifactId>mybatis-spring</artifactId>
config - 配置类
jdbcConfig:
@Configuration
public class JdbcConfig{
1. 定义一个方法获得要管理的对象
2. 添加@Bean,表示当前方法的返回值是一个bean, 这个方法返回的对象需要由你管理
相当于XML配置中的
<bean>标签@Value("${jdbc.driver}")
private Sting driver;
@Value("${jdbc.url}")
private Sting url;
@Value("${jdbc.username}");
private String userName;
@Value("${jdbc.password}");
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
具体的值需要加载propertis文件
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
// 会将 jdbc.properties 文件中的属性加载到 Spring 的 Environment 中
@Import({JdbcConfig.class,MybatisConfig.class})
//会将 JdbcConfig.class 和 MybatisConfig.class 这两个配置类导入到当前 Spring 容器中
//这两个类中定义的 Bean 会被 Spring 管理总结:
1. 只要有一个配置类使用了 @PropertySource 加载了属性文件,整个 Spring 容器中所有的 Bean 都可以使用这些属性资源,无论它们定义在哪个配置类中
2. @PropertySource 会将属性文件加载到 Spring 容器的 Environment 中
3. Environment 是 全局共享 的,所有在同一个 Spring 容器中的 Bean 都可以访问
4. 无论你在哪个配置类上声明 @PropertySource,效果都是全局的
public class SpringConfig{
}
public class MybatisConfig{
@Bean
// 初始化SqlSessionFactory的配置
// SqlSessionFactoryBean 专门用来造SqlSessionFactory的Bean的
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.domain"); 设置类型别名的包
ssfb.setDataSource(dataSource); 设置数据源
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer (){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
扫描指定包下的所有接口, MyBatis 就能自动找到这些接口并生成对应的实现类
return msc;
}
等价于<mapper> <package name="com.itheima.dao"></package> </mapper>
}
Spring 整合 JUnit
pom.xml中导入
<artifactId>juit</artifactId>
<artifactId>sping-test</artifactId>
@RunWith(SpringJUnit4ClassRunner.class) //设置类运行器
@ContextConfiguration(classes = SpringConfig.class) //设置上下文
public class AccountServiceTest{
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
accountService.findById(2);
}
}
AOP:
作用:在不惊动原始设计的基础上为其进行功能增强
1. 导包:
<groupId>org.aspectj</group>
抽离出来的method()方法,定义通知类
@Component 让类变成Spring能控制的Bean
@Aspect 不加这个注解, 会被Spring当不同的Bean处理
public class MyAdvice(){
定义切入点:
@Pointcut("execution(void xxx.xxx.xxx,update())")
private void pt(){};
// 绑定共性功能和切入点
@Before("pt()")
@Before("pt()") 表示当前方法作为前置通知,会在 pt() 匹配的目标方法update()执行前触发,实现共性功能(如参数校验、日志记录)的集中管理
public void method(){
........
}
}
@Aspect :识别类中AOP开发的东西
.....
@EnableAspectJAutoProxy 这个注解的作用是启动了@Aspect这个注解
public class SpringConfig{
}
AOP切入点表达式
对于查询类使用*通配快速描述
AOP通知类型
@Before("pt()")
@After("pt()")
@Around("pt()")
public void aroundSelect(ProceedingJoinPoint pjp){
....
pjp.proceed();
.....
}
AOP 通知获取数据
@Before("pt()")
public void before(JoinPoint jp){
Object[] args=jp.getArgs(); //拿参数的方法
args[0]=666; //修改方法中的参数
}
Spring事务:
我们一般写代码是写在AccountServiceImp 实现类中, 但是要想开启事务,注解一般加在AccountService 接口上
public interface AccountService{@Transactional
public void transfer(String out, String in , Double money);
}
配置事务管理器
为什么会在业务层开启事务呢?事务不应该是数据库中的操作吗?
因为业务层中包含了大量dao层的操作。
@Configuration
public class JdbcConfig{
1. 定义一个方法获得要管理的对象
2. 添加@Bean,表示当前方法的返回值是一个bean, 这个方法返回的对象需要由你管理
相当于XML配置中的
<bean>标签@Value("${jdbc.driver}")
private Sting driver;
@Value("${jdbc.url}")
private Sting url;
@Value("${jdbc.username}");
private String userName;
@Value("${jdbc.password}");
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {/ / jdbc的事务
DataSourceTransactionManager transactionManger = new DataSourceTransactionManager();transactionManger.setDataSource(dataSource);
return transactionManger;
}}
在 SpringConfig添加注解
@Configuration
@EnableTransactionManagement
告诉Spring使用注解事务驱动
public class SpringConfig{
}
一共就是上面的三步
注意:
@Transactional
这个注解写到类/接口上, 这个类/接口的所有方法都开启事务了
public interface AccountService{
public void transfer(String out, String in , Double money);
}
Spring事务角色和事务属性
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Transactional 开启Spring的事务,整合三个事务为一个事务
public void transfer(String out, String in , Double money){
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}
}
事务管理员:发起事务方,在Sping中通常指代业务层开启事务的方法 ,上面的 transfer 方法
事务协调员:加入事务方,在Spring中通常指代数据层的方法/业务层方法 上面的 outMoney 和inMoney
事务属性
报如下两种异常, 就会做事务回滚
1. error - 内存溢出
2. 运行时异常
IOException 不会做事务回滚
在事务注解中单独添加属性
@Transactional(rollbackFor={IOException.class})
事务的传播行为(事务协调员对事务管理员的一种态度):
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountDao accountDao;
@Autowired
private LogService logService;
@Transactional 开启Spring的事务,整合四个事务为一个事务
public void transfer(String out, String in , Double money){
try{
accountDao.outMoney(out,money);
accountDao.inMoney(in,money);
}finally{
logService.log(out,in,money);
}
}
}
目前上面几个事务同属于一个事务,同成功同失败。
要求:日志打印,不管转账成功还是失败, 都进行日志的操作
因此需要将日志事务开辟成一个全新的事务
public interface LogService{
@Transactional(propagation=Propagation.REQUIRES_NEW)
void log(String out, String in , Double money);
}
2025.9.28
SpringMVC
一种做表现层开发的框架技术
Web程序的工作流程:
Web程序通过浏览器访问页面,前端页面使用异步提交的方式发送请求到后端服务器,后端服务器通过表现层, 业务层, 数据层的三层架构的形式进行开发, 页面发送的请求由表现层接收,获取用户的请求参数后,将参数传递到业务层,再由业务层访问数据层,得到用户需要访问的数据后,将数据返回给表现层,表现层拿到数据后,将数据转换成json格式发送给前端页面,前端页面接收数据后解析数据,并组织成用户浏览的最终页面信息交给浏览器
先导入坐标
<artifactId>java.servlet-api</artifactId>
<artifactId>sping-webmvc</artifactId>
表现层:
@Controller // 使用Controller 定义bean
public class UserController{
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save(){
...........
return "{'module':'springmvc'}";
}
}
加载这个上面创建好的bean, 做springMvc的配置 类似于springConfig配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig{
}
创建springMvc的配置文件,加载controller对应的bean
tomcat启动的时候,需要加载springMvc的配置类
定义一个servelt容器启动的配置类,在里面加载spring的配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { // 加载springmvc容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected WebApplicationContext createRootApplicationContext() { return null; 专门用来加载spring的配置类 } // 设置那些请求归属springMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; //所有请求 }
}记得配置tomcat插件
注解:
@Controller : 设定SpringMVC的核心控制器bean
@RequestMapping("\xxx") : 设置当前控制器方法的请求路径
@ResponseBody: 设置你当前的返回值作为响应体反馈给发送请求方的(浏览器)
SpingMVC只加载Controller层的Bean, Sping加载dao,service层的bean,
当然具体是加载各自的配置文件
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { // 加载springmvc容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; } // 设置那些请求归属springMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; //所有请求 }
}也可以继承抽象方法,然后指定配置文件即可
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定根容器的配置类(Spring核心配置)
* 用于加载业务层、数据层等的Bean
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class, DataSourceConfig.class };
}/**
* 指定Web容器的配置类(Spring MVC配置)
* 用于加载控制器、视图解析器等Web相关组件
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}/**
* 指定DispatcherServlet的映射路径
* 通常映射为"/",表示处理所有请求
*/
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}请求与映射
@Controller // 使用Controller 定义bean
@RequestMapping("/user") // 设置当前模版的请求路径前缀
public class UserController{
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save(){
...........
return "{'module':'springmvc'}";
}
@RequestMapping("/delete")
// 设置当前操作的返回值类型
@ResponseBody
public String delete(){
...........
return "{'module':'springmvc'}";
}
}
前端发送一个get/post请求携带一个name参数
http://localhost/commonParam?name=itcast
public class UserController{
@RequestMapping("/commonParam")
// 设置当前操作的返回值类型
@ResponseBody
后端接收name参数 <->方法形参中的参数和请求url中的参数一致可以映射
public String commonParam(String name){
...........
return "{'module':'common param'}";
}
http://localhost/commonParam?name=itcast&age=18 , 接收两个参数
public String commonParam(String name, int age){
...........
return "{'module':'common param'}";
}
}
设置表单提交时中文乱码的情况:
import javax.servlet.Filter;
public class ServiceContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override 添加字符过滤器,能够处理中文
protected Filter[] getServletFilters(){
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}如何解决请求中的参数和处理方法中形参不一致的情况
http://localhost/commonParam?name=itcast
public class UserController{
@RequestMapping("/commonParam")
// 设置当前操作的返回值类型
@ResponseBody
后端接收name参数
@RequestParam{"name"}:把请求参数中的name[值]给到userName
绑定请求参数和形参之间的关系
public String commonParam(@RequestParam{"name"} String userName){
...........
return "{'module':'common param'}";
}
http://localhost/commonParam?name=itcast&age=18 , 接收两个参数
public String commonParam(String name, int age){
...........
return "{'module':'common param'}";
}
}
使用实体类进行传参
@Data
public class User{
private String name;
private int age;
// 实现get/set方法,重写toString, 这里注解替换了
}
如果url中请求的属性名和后面实体类的属性名一样, 他就可以自动把属性给你塞进去
http://localhost/commonParam?name=xxx&age=18
public class UserController{
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){ // 传pojo的
...........
return "{'module':'pojo param'}";
}
}
使用实体类进行传参, 同时实体类中包含引用类型的属性
@Data
public class User{
private String name;
private int age;
private Address address;
// 实现get/set方法,重写toString, 这里注解替换了
}
@Data
public class Address{
private String province;
private String city;
.......
}
实体类中包含对象的url类似于
http://localhost/commonParam?name=xxx&age=18&address.city=beijing&address.province=beijing
表现层没有变化
public class UserController{
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
...........
return "{'module':'pojo param'}";
}
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){ // postman中key值必须为likes
System.out.println("数组参数传递 likes ==>" + Arrays.toString(likes));
return "{'module':'array param'}";
}
@RequestMapping("/listParam")
@ResponseBody
// 通常形参是引用类型,会先构建一个对应引用类型的对象, 然后把url中的请求参数
set进[属性]入,但是现在我们要把url中的请求参数变为list集合中的数据,因此下面的写法就有问题了
public String listParam(List<String> likes){
System.out.println("集合参数传递 likes ==>" + likes);
return "{'module':'list param'}";
}
// 添加@RequestParam注解
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==>" + likes);
return "{'module':'list param'}";
}
}
请求参数(传递json数据)
pom.xml中导入坐标
<artifactId>jackson-databind</artifactId>
前端json传入到表现层的形参list集合
@Configuration
@EnableWebMvc : springMvc开启由json数据转换成对象的功能
public class SpringConfig{
}
public class UserController{
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
...........
return "{'module':'list common for json param'}";
}
// @RequestBody:转换请求体参数,将json数据转换成List<String>集合
// 把请求体中的参数塞到形参[user]中
[
{
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
}
]public String listPojoParamForJson(@RequestBody User user){
...........
return "{'module':'pojo for json param'}";
}
[
{
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
},
{
"id": 2,
"name": "李四",
"email": "lisi@example.com"
},
{
"id": 3,
"name": "王五",
"email": "wangwu@example.com"
}
]public String listParamForJson(@RequestBody List<User> user){ // 集合中保存多个对象
...........
return "{'module':'list pojo for json param'}";
}
}
注:就是不加@RequestBody 接收的是url里面的参数, 请求路径传参,加@RequestBody接收的是前端传递过来的请求体json数据
日期类型参数传递
public class UserController{
@RequestMapping("/dataParm")
@ResponseBody
public String dataParm(Data data, @DataTimeFormat(pattern="yyyy-MM-dd") Data data1 , @DataTimeFormat(pattern="yyyy-MM-dd HH:mm:ss ") Data data2){
...........
return "{'module':'data param'}";
}
}
第一个参数,Data data,打印出来的就是SpringMvc默认格式,
对于日期时间型在接收参数的时候, 使用@DataTimeFormat来指定格式
@EnableWebMvc: 根据类型匹配对应的类型转换器
响应:
public class UserController{
// 响应页面/跳转页面
@RequestMapping("/toJumpPage")
public String toJumpPage(){
return "page.jsp";
}
@RequestMapping("/toText")
@ResponseBody
public String toText(){
return "reponse text"; // 返回字符串
}
// 响应json数据
// 1. 响应POJO对象
@RequestMapping("/toJsonPOJO")
@ResponseBody : springMvc默认返回的是页面,加了@ResponseBody就可以自动根据return 的数据类型,进行转换了,不管是String, 还是json数据
public User toJsonPOJO(){
User user = new User();
user.setName("itcast");
........
return user; // 返回 json数据
}
}
REAT风格:
传统风格资源描述形式
http://localhost/user/getById?id=1
http://localhost/user/saveUser // 保存信息,POST提交表单
REST风格描述形式
http://localhost/user/1
http://localhost/user
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
http://localhost/users 查询全部用户信息 GET(查询)
http://localhost/users/1 查询指定用户信息 GET(查询)
http://localhost/users 添加用户信息 POST(新增/保存)
http://localhost/users 修改用户信息 PUT(修改/更新)
http://localhost/users/1 删除用户信息 DELETE(删除)
路径+请求方式 => 确认一个资源的访问行为
描述模块的名称通常使用复数, 也就是加s的格式描述, 表示此类资源,而非单个资源:例如:
user, books,...
eg: rest风格的增删改查
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.ArrayList;
import java.util.List;
import java.util.Optional;@RestController
@RequestMapping("/api/users") 使用路径前缀优化写法
public class UserController {
// 模拟数据
private List<User> users = new ArrayList<>();
private Long nextId = 1L;// 查询所有用户 - GET
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(users);
}// 根据ID查询用户 - GET
@RequestMapping(value = "/{id}", method = RequestMethod.GET) //访问路径+请求方法
public ResponseEntity<User> getUserById(@PathVariable Long id) { // @PathVariabl 这个变量来自于路径,同时指定【请求参数/路径变量】{id},方法中形参名要和请求参数{ } 保持一致
Optional<User> user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst();
if (user.isPresent()) {
return ResponseEntity.ok(user.get());
} else {
return ResponseEntity.notFound().build();
}
}// 创建用户 - POST
@RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<User> createUser(@RequestBody User user) {
user.setId(nextId++);
users.add(user);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}// 更新用户 - PUT
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getId().equals(id)) {
user.setId(id);
users.set(i, user);
return ResponseEntity.ok(user);
}
}
return ResponseEntity.notFound().build();
}// 删除用户 - DELETE
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean removed = users.removeIf(user -> user.getId().equals(id));
if (removed) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
}// 用户实体类(内部类示例)
public static class User {
private Long id;
private String name;
private String email;// 构造方法
public User() {}public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}// getter 和 setter 方法
public Long getId() {
return id;
}public void setId(Long id) {
this.id = id;
}public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public String getEmail() {
return email;
}public void setEmail(String email) {
this.email = email;
}
}
}
2025.9.29
SSM整合
表现层数据封装
设置统一数据返回结果类
将类定义在Controller包下
public class Result{
private Object data;
private Integer code;
private String msg;
// 添加带msg 和 不带msg的构造方法
public Result(Integer code, Object data){
this.data=data;
this.code=code;
}
public Result(Integer code, Object data, String msg){
this.data=data;
this.code=code;
this.msg=msg;
}
}
将类定义在Controller包下
定义状态码类
public class Code{
//增删改查成功
public static final Integer SAVE_OK=20011;
public static final Integer DELETE_OK=20021;
public static final Integer UPDATE_OK=20031;
public static final Integer GET_OK=20041;
//增删改查失败
public static final Integer SAVE_ERR=20010;
public static final Integer DELETE_ERR=20020;
public static final Integer UPDATE_ERR=20030;
public static final Integer GET_ERR=20040;
}
对Controller层进行添加状态码
eg: 添加成功/失败 判断
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book){
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK : SAVE_ERR, flag);
}
修改成功/失败
@PutMapping
public Result update(@RequestBody Book book){
boolean flag = bookService.save(book);
return new Result(flag ? Code.UPDATE_OK : UPDATE_ERR, flag);
}
查询单条数据成功/失败
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id){
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "数据查询失败,请重试!";
return new Result(code,book,msg);
}
查询全部数据成功/失败
@GetMapping
public Result getAll(){
List<Book> bookList = bookService.getAll();
Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
String msg = bookList != null ? "" : "数据查询失败,请重试!";
return new Result(code,bookList,msg);
}
异常处理器
所有的异常均抛出到表现层进行处理
拦截器:是一种动态拦截方法调用的机制
作用:在指定的方法调用前后执行预先设定好的代码
阻止原始方法的执行
在Interceptor 包中声明 ProjectInterceptor 类 继承 HandlerInterceptor 接口,定义成一个bean
拦截器类如下:
@Component
public class ProjectInterceptor implements ProjectInterceptor{
// 指定的方法调用前
public boolean preHandle(..) throws Exception{
....
return true; // 返回true,才会执行后续的Controller方法
}
//指定的方法调用后
public void postHandle(..) throws Exception{
....
}
public void afterCompletion(..) throws Exception{
....
}
}
然后定义配置类,继承WebMvcConfigurationSupport, 实现addInterceptor方法
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport{
// 添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个
@Autowired
private ProjectInterceptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry){
....
registry.addInterceptor(projectInterceptor).addPathPatterns("/books");
// 第一个参数:你要加那个拦截器,添加对应的拦截器对象
// 第二个参数:你要拦那种请求
}
}
对表现层进行约束的
也可以直接在Config包下整合SpringMvcConfig和SpringMvcSupport和这个拦截器配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig{
// 添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个
@Autowired
private ProjectInterceptor projectInterceptor;
public void addInterceptors(InterceptorRegistry registry){
....
registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
// 第一个参数:你要加那个拦截器,添加对应的拦截器对象
// 第二个参数:你要拦那种请求
}
}
帮助你更好的理解面试八股文
598

被折叠的 条评论
为什么被折叠?



