SSM框架学习

我下面写的笔记, 讲真的,也就注解有点用,因为你开发肯定用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/*");

                // 第一个参数:你要加那个拦截器,添加对应的拦截器对象

                // 第二个参数:你要拦那种请求

        }

}

帮助你更好的理解面试八股文 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值