《Spring5》学习笔记

写于

狂神,B站,学习时间:2021年7月3日

目录

《Spring5》学习笔记 1 一、Spring 1 1.1 简介 1 1.2 优点 2 1.3 组成 2 1.4 拓展 2 Spring Boot 3 Spring Cloud 3 二、IOC理论推导 3 IOC的本质 6 三、HelloSpring 6 四、IOC创建对象方式 8 1.使用无参构造创建对象,默认方式 8 五、Spring 12 5.1 别名 12 5.2 Bean 13 5.3 import 13 六、依赖注入 13 6.1 构造器注入 13 6.2 Set注入 13 6.3 拓展方式注入 17 P命名空间注入 22 6.4 Bean作用域 25 七、Bean的自动装配 25 7.1 测试 25 7.2 ByName自动装配 28 7.3 ByType自动装配 28 7.4 使用注解实现自动装配 29 八、使用注解开发 30 九、使用Java的方式配置Spring 31 十、代理模式 32 10.1 静态代理 32 10.2 动态代理 37 十一、AOP 40 11.1 什么是AOP 40 11.2 AOP在Spring中的作用 40 11.3 使用Spring实现AOP 41 十二、整合MyBatis 47 12.1 回忆mybatis 48 12.2 Mybatis-spring 48 十三、声明式事务 48 1.回顾事务 48 2.spring中的事务管理 49

一、Spring

1.1 简介

Spring-春天,给软件领域带来春天,简化开发工作 范围:任何Java应用 Spring是一个轻量级的Ioc 2002,首次推出了Spring框架的雏形,interface21框架 2004年3月24日,Spring诞生时间,以interface21为基础 Rod Johnson,Spring Famework创始人,音乐人 轮子理论,不要重复造轮子 Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有框架 SSH:struct2+Spring+Hibernat SSM: SpringMvc+Spring+Mybatic 官网:https://spring.io/projects/spring-framework 下载地址:https://repo.spring.io/artifactory/release/org/springframework/spring/ Spring Web MVC导包

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>5.2.0.RELEASE</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>5.2.0.RELEASE</version>

</dependency>

1.2 优点

开源的免费的框架(容器) 轻量级的、非入侵式的框架 控制反转IOC、面向切面编程AOP 支持事务的处理,对框架整合的支持 总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

1.3 组成

Spring7大模块

image1

1.4 拓展

在Spring的官网有这个介绍:现代化的Java开发,就是基于Spring的开发 image-20250505192414600

Spring Boot

一个快速开发的脚手架 基于SpringBoot可以快速开发单个微服务 约定大于配置

Spring Cloud

SpringCloud是基于SpringBoot实现的 现在大多数公司都在使用Spring Boot进行快速开发,学习Spring Boot的前提是需要完全掌握Spring及SpringMVC,承上启下的作用 弊端:发展时间太久,违背了原来的理念,后期配置十分繁琐

二、IOC理论推导

1.UserDao接口

package dao;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 17:46

* @Description: dao

* @version: 1.0

*/

public interface UserDao {

void getUser();

}

2.UserDaoImpl实现类

package dao;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 17:47

* @Description: dao

* @version: 1.0

*/

public class UserDaoImpl implements UserDao {

public void getUser() {

System.out.println("默认获取用户的数据");

}

}

3.UserService业务接口

package service;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 17:48

* @Description: service

* @version: 1.0

*/

public interface UserService {

void getUser();

}

4.UserServiceImpl业务实现

package service;

import dao.UserDao;

import dao.UserDaoOracleImpl;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 17:49

* @Description: service

* @version: 1.0

*/

public class UserServiceImpl implements UserService {

//private UserDao userDao = new UserDaoImpl();

//private UserDao userDao = new UserDaoMysqlImpl();

private UserDao userDao ;

//利用set实现动态实现值的注入

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

public void getUser() {

userDao.getUser();

}

}

5.测试

import dao.UserDaoImpl;

import dao.UserDaoMysqlImpl;

import dao.UserDaoOracleImpl;

import service.UserServiceImpl;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 17:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

//用户实际调用的是业务层,dao层不需要接触

UserServiceImpl userService = new UserServiceImpl();

userService.setUserDao(new UserDaoImpl());

userService.getUser();

}

}

在之前的业务中,用户的需求可能会影响之前的代码,需要根据用户的需要去修改原代码。 如果代码量大,则修改一次需求的成本则会很昂贵 使用一个Set接口实现,会发生很大的变化

private UserDao userDao ;

//利用set实现动态实现值的注入

public void setUserDao(UserDao userDao) {

this.userDao = userDao;

}

之前,程序是主动创建对象,控制权在程序员手上 使用了set注入后,程序不再具有主动性,而是变成了被动接受对象 这种思想,从本质上解决了问题,程序员不用再去管理对象的创建。系统的耦合性大大降低,可以专注于业务的实现上。这是IOC的原型。

IOC的本质

image-20250505192552448 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类,从面达到了零配置的目的 控制反转是一种通过描述(XML或注释)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)

三、HelloSpring

这个过程就叫控制反转: 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。 反转:程序本身不创建对象,而变成被动的接收对象。 依赖注入:就是利用set方法来进行注入的。 IOC是一种编程思想,由主动的编程变成被动的接收。 可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。 OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配! 出错情况:在配置时,xml文件没放对 Xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<!--使用Spring来创建对象,在Spring这些都称为bean

bean = 对象 new Hello()

-->

<bean id="hello" class="pojo.Hello">

<property name="str" value="Spring"/>

</bean>

</beans>

Hello代码

package pojo;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 20:58

* @Description: pojo

* @version: 1.0

*/

public class Hello {

private String str;

public String getStr() {

return str;

}

public void setStr(String str) {

this.str = str;

}

@Override

public String toString() {

return "Hello{" + "str=" +str +'\'' +'}';

}

}

测试代码

import org.springframework.context.support.ClassPathXmlApplicationContext;

import pojo.Hello;

/

* @Auther: fei

* @Date: 2021/7/3 - 07 - 03 - 21:10

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

//获取Spring的上下文对象

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

//现在对象都在Spring中的管理了,要使用则直接去里面取出来就可以

Hello hello = (Hello) context.getBean("hello");

System.out.println(hello.toString());

}

}

四、IOC创建对象方式

1.使用无参构造创建对象,默认方式

XML文件

1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://www.springframework.org/schema/beans
5. https://www.springframework.org/schema/beans/spring-beans.xsd">
6. <bean id="user" class="User">
7. <property name ="name" value="xiaofei"/>
8. </bean>
9. </beans>

User无参构造创建对象代码

/*

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 7:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class User {

private String name;

public User() {

System.out.println("User的无参构造");

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public void show(){

System.out.println("name="+name);

}

}

测试代码

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 7:53

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

User user = (User) context.getBean("user");

user.show();

}

}

2.使用有参构造器创建对象 (1)使用index下标赋值 XML文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="user" class="User">

<constructor-arg index="0" value="小飞的Java学习"/>

</bean>

</beans>

User代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 7:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class User {

private String name;

public User() {

System.out.println("User的无参构造");

}

public User(String name) {

this.name = name;

System.out.println("User的有参构造器");

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public void show(){

System.out.println("name="+name);

}

}

测试代码

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 7:53

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

User user = (User) context.getBean("user");

user.show();

}

}

(2)通过类型创建对象 XML文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<!--不建议使用!-->

<bean id="user" class="User">

<constructor-arg type="java.lang.String" value="小飞的Java学习"/>

</bean>

</beans>

(3)通过参数名来创建对象

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<!--<!–第二种方式:不建设使用!–>-->

<!--<bean id="user" class="User">-->

<!-- <constructor-arg type="java.lang.String" value="小飞的Java学习"/>-->

<!--</bean>-->

<!--第三种方式:直接通过参数名来创建对象!-->

<bean id="user" class="User">

<constructor-arg name="name" value="xiaofei"/>

</bean>

</beans>

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了

五、Spring

5.1 别名

<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias='userNew"/>

5.2 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- id : bean的唯一标识符,也就是相当于我们学的对象名
         class : bean对象所对应的全限定名: 包名 + 类型
         name : 也是别名,而且name可以同时取多个别名 -->
    <bean id="user1" class="com.kuang.pojo.User1" name="user2 u2,u3;u4">
        <property name="name" value="西部开源"/>
    </bean>
</beans>

5.3 import

一般用于团队开发,可以将多个配置文件合并为一个 如果多个人开发,每个人都有自己的配置文件,则可以使用import合并为一个总的配置文件 ApplicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<import resource="beans.xml"/>

<import resource="beans2.xml"/>

<import resource="beans3.xml"/>

</beans>

六、依赖注入

6.1 构造器注入

前面已经介绍过

6.2 Set注入

依赖注入:Set注入 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性,由容器来注入 【环境搭建】 1.复杂类型

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Address {

    private String address;

    public String getAddress() {

        return address;

}

public void setAddress(String address) {

    this.address = address;

}

}

2.真实测试对象

import java.util.*;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Student {

private String name;

private Address address;

private String[] books;

private List<String> hobbys;

private Map<String,String> card;

private Set<String> games;

private String wife;

private Properties properties;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Address getAddress() {

return address;

}

public void setAddress(Address address) {

this.address = address;

}

public String[] getBooks() {

return books;

}

public void setBooks(String[] books) {

this.books = books;

}

public List<String> getHobbys() {

return hobbys;

}

public void setHobbys(List<String> hobbys) {

this.hobbys = hobbys;

}

public Map<String, String> getCard() {

return card;

}

public void setCard(Map<String, String> card) {

this.card = card;

}

public Set<String> getGames() {

return games;

}

public void setGames(Set<String> games) {

this.games = games;

}

public String getWife() {

return wife;

}

public void setWife(String wife) {

this.wife = wife;

}

public Properties getProperties() {

return properties;

}

public void setProperties(Properties properties) {

this.properties = properties;

}

@Override

public String toString() {

return "Student{" +

"name='" + name + '\'' +

", address=" + address +

", books=" + Arrays.toString(books) +

", hobbys=" + hobbys +

", card=" + card +

", games=" + games +

", wife='" + wife + '\'' +

", properties=" + properties +

'}';

}

}

3.beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="student" class="Student">

<!--第一种,以普通方式注入-->

<property name="name" value="小飞"/>

</bean>

</beans>

4.测试类

import com.sun.org.apache.bcel.internal.util.ClassPath;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:58

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Student student = (Student) context.getBean("student");

System.out.println(student.getName());

System.out.println(student.getAddress());

}

}

6.3 拓展方式注入

XML配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="address" class="Address">

<property name="address" value="江西赣州"/>

</bean>

<bean id="student" class="Student">

<!--第一种,普通值注入,value-->

<property name="name" value="小飞"/>

<!--第二种,bean注入,ref-->

<property name="address" ref="address"/>

<!--第三种,数组注入,ref-->

<property name="books">

<array>

<value>"红楼梦"</value>

<value>"西游记"</value>

<value>"水浒传"</value>

<value>"三国演义"</value>

</array>

</property>

<!--list注入-->

<property name="hobbys">

<list>

<value>"听歌"</value>

<value>"音乐"</value>

<value>"看电影"</value>

</list>

</property>

<!--Map注入-->

<property name="card">

<map>

<entry key="身份证" value="1111122365465452100"/>

<entry key="银行卡" value="1111122362100"/>

</map>

</property>

<!--Set注入-->

<property name="games">

<set>

<value>LOL</value>

<value>"王者荣耀"</value>

<value>COC</value>

<value>WOW</value>

</set>

</property>

<!--null注入-->

<property name="wife">

<null/>

</property>

<!--properties-->

<property name="properties">

<props>

<prop key="学号">20190503</prop>

<prop key="性别">女</prop>

<prop key="姓名">小明</prop>

<prop key="username">root</prop>

<prop key="password">123456</prop>

</props>

</property>

</bean>

</beans>

Address代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Address {

private String address;

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

@Override

public String toString() {

return "Address{" +

"address='" + address + '\'' +

'}';

}

}
Student代码

import java.util.*;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:51

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Student {

private String name;

private Address address;

private String[] books;

private List<String> hobbys;

private Map<String,String> card;

private Set<String> games;

private String wife;

private Properties properties;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Address getAddress() {

return address;

}

public void setAddress(Address address) {

this.address = address;

}

public String[] getBooks() {

return books;

}

public void setBooks(String[] books) {

this.books = books;

}

public List<String> getHobbys() {

return hobbys;

}

public void setHobbys(List<String> hobbys) {

this.hobbys = hobbys;

}

public Map<String, String> getCard() {

return card;

}

public void setCard(Map<String, String> card) {

this.card = card;

}

public Set<String> getGames() {

return games;

}

public void setGames(Set<String> games) {

this.games = games;

}

public String getWife() {

return wife;

}

public void setWife(String wife) {

this.wife = wife;

}

public Properties getProperties() {

return properties;

}

public void setProperties(Properties properties) {

this.properties = properties;

}

@Override

public String toString() {

return "Student{" +

"name='" + name + '\'' +

"\n address=" + address.toString() +

"\n books=" + Arrays.toString(books) +

"\n hobbys=" + hobbys +

"\n card=" + card +

"\n games=" + games +

"\n wife='" + wife + '\'' +

"\n properties=" + properties +

'}';

}

}
测试代码

import com.sun.org.apache.bcel.internal.util.ClassPath;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:58

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

Student student = (Student) context.getBean("student");

System.out.println(student.toString());

}

}

P命名空间注入

Userbeans.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<!--p命名空间注入,可以直接注入属性的值-->

<bean id="user" class="User" p:name="小飞" p:age="18"/>

</beans>

User代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 9:56

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class User {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

@Override

public String toString() {

return "User{" +

"name='" + name + '\'' +

", age=" + age +

'}';

}

}

测试代码

import com.sun.org.apache.bcel.internal.util.ClassPath;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 8:58

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

// ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

// Student student = (Student) context.getBean("student");

// System.out.println(student.toString());

test2();

}

public static void test2(){

ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");

User user = (User) context.getBean("user");

System.out.println(user);

}

}

可以使用c命名空间和p命名空间的方式注入 注意点: 1.p命令和c命令注入,不能直接写入,需要导入xml约束 xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4 Bean作用域

Scope Description
singleton (默认)将单个 bean 定义作用域限定为每个 Spring IoC 容器的单个对象实例 。
prototype 将单个 bean 定义作用域限定为任意数量的对象实例。
request 将单个 bean 定义作用域限定为单个 HTTP 请求的生命周期。即,每个 HTTP 请求都有其基于单个 bean 定义创建的自己的 bean 实例。仅在感知 Web 的 Spring ApplicationContext 上下文中有效。
session 将单个 bean 定义作用域限定为一个 HTTP 会话的生命周期。仅在感知 Web 的 Spring ApplicationContext 上下文中有效。
application 将单个 bean 定义作用域限定为一个 ServletContext 的生命周期。仅在感知 Web 的 Spring ApplicationContext 上下文中有效。
websocket 将单个 bean 定义作用域限定为一个 WebSocket 的生命周期。仅在感知 Web 的 Spring ApplicationContext 上下文中有效。

1.单例模式

<bean id="user2" class="User" c:age="18" c:name="xiaofei"  
scope="singleton"  
/>

得到对象一致 2.原型模式:每次从容器中get的时候,都会产生一个新对象

<bean id="user2" class="User" c:age="18" c:name="xiaofei"  
scope="prototype"  
/>

3.其余的request、session、application,这些只能在web开发中使用

七、Bean的自动装配

自动装配:是Spring满足bean依赖的一种方式 Spring会在上下文中自动寻找,并自动给bean装配属性 在Spring中有三种装配方式 1.在xml中显示的配置 2.在java中显示配置 3.隐式的自动装配bean

7.1 测试

1.环境搭建 一个有2个宠物:猫和狗 XML文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="cat" class="Cat"/>

<bean id="dog" class="Dog"/>

<bean id="people" class="People">

<property name="name" value="小飞侠啊"/>

<property name="dog" ref="dog"/>

<property name="cat" ref="cat"/>

</bean>

</beans>

Cat对象代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 10:38

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Cat {

public void shout(){

System.out.println("miao-");

}

}

Dog对象代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 10:38

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class Dog {

public void shout(){

System.out.println("wang-");

}

}

People对象代码

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 10:39

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class People {

private Cat cat;

private Dog dog;

private String name;

public Cat getCat() {

return cat;

}

public void setCat(Cat cat) {

this.cat = cat;

}

public Dog getDog() {

return dog;

}

public void setDog(Dog dog) {

this.dog = dog;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return "People{" +

"cat=" + cat +

", dog=" + dog +

", name='" + name + '\'' +

'}';

}

}

测试代码

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/

* @Auther: fei

* @Date: 2021/7/4 - 07 - 04 - 10:45

* @Description: PACKAGE_NAME

* @version: 1.0

*/

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

test1();

}

public static void test1(){

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

People people = context.getBean("people", People.class);

people.getDog().shout();

people.getCat().shout();

//people.toString();

}

}

7.2 ByName自动装配

<!--

Byname,:会自动在容器上下文中查找 ,和自己对象set方法后面的值对应的bean id

-->

<bean id="people" class="People" autowire="byName">

<property name="name" value="小飞侠啊"/>

</bean>

7.3 ByType自动装配

<bean class="Cat"/>

<bean class="Dog"/>

<!--

Byname,:会自动在容器上下文中查找 ,和自己对象set方法后面的值对应的bean id

-->

<bean id="people" class="People" autowire="byType">

<property name="name" value="小飞侠啊"/>

</bean>

小结: ByName,需要保证所有的bean的id唯一,并且这个bean需要注入和属性的set方法的值一致 ByType,需要保证所有的bean的class唯一,并且这个bean需要注入和属性的类型一致

7.4 使用注解实现自动装配

JDK1.5支持的注解,Spring2.5就支持了 要使用注解须知: 1.导入约束。Context的约束 2.配置注解的支持:context:annotation-config/ 【重点】

<?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

https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

https://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/aop

https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--开启注解的支持-->

<context:annotation-config/>

<bean id="cat" class="Cat"/>

<bean id="dog" class="Dog"/>

<bean id="people" class="People"/>

</beans>

@Auto-wired 1.直接在属性上使用即可,也可以在set方法上使用 2.使用Autowired,可以不用再编写set方法了,前提是这个自动装配的属性在IOC(Spring)容器中存在,且符号名字byname @Nullable,字段标识了这个注解, 测试代码

public class People {

//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空

@Autowired(required = false)

private Cat cat;

@Autowired

private Dog dog;

private String name;

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入 @Resource注解

public class People {

//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空

@Resource

private Cat cat;

@Resource

private Dog dog;

@Resource注解和@Autowired的区别: 1.都是用来自动装配的,都可以放在属性字段上

@Autowired通过byname的方式实现,而且要求这个对象必须存在 3.@Resource默认通过通过byname的方式实现,如果找不到名字,则通过byType实现,如果两个都找不到,则会报错 执行顺序不同:@Autowired通过byType的方式实现

八、使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入 使用注解需要导入context的约束,增加注解的支持 1.bean 2.属性如何注入

@Component

public class User {

public String name;

@Value("小飞")

public void setName(String name) {

this.name = name;

}

}

3.衍生的注解

@Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层

@Service

@Controller

这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean 4.自动装配置 5.作用域 @Scope 6.小结 Xml与注解 Xml更加万能,适合于任何场合,维护简单方便 注解,不是自己类使用不了,维护相对复杂 最佳实践:xml与注解相结合 Xml用来管理bean 注解只负责完成属性的注入 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

九、使用Java的方式配置Spring

完全不使用Spring的XML配置,全权交给Java来做 javaConfig是Spring的一个子项目,在Spring4之后成了一个核心功能

十、代理模式

为什么要学代理模式? 因为这是SpringAOP的底层。SpringAOP和SpringMVC 代理模式:静态代理、动态代理

image7

10.1 静态代理

角色分析 抽象角色:一般会使用接口或抽象类来解决 真实角色:被代理的角色 代理角色:代理真实角后,后做一些附属属性 客户:访问代理对象的人 代码步骤 1.接口

package client;

//租房接口

public interface Rent {

public void rent();

}

2.真实角色

package client;

public class Host implements Rent {

public void rent(){

System.out.println("房东要出租房子");

}

}

3.代理角色

package client;

/

* @Auther: fei

* @Date: 2021/7/6 - 07 - 06 - 7:19

* @Description: client

* @version: 1.0

*/

public class Proxy implements Rent {

private Host host;

public Proxy() {

}

public Proxy(Host host) {

this.host = host;

}

public void rent(){

host.rent();

}

public void seeHouse(){

System.out.println("中介带你看房");

}

public void fare(){

System.out.println("收中介费");

}

public void contract(){

System.out.println("签合同");

}

}

4.客户端访问代理角色

package client;

public class Client {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

Host host = new Host();

//代理,,中介帮忙租房子,但中介会有一些附属操作

Proxy proxy = new Proxy(host); //注意:host不要少了

//不用面对房东,直接找中介就可

proxy.rent();

}

}

代理模式的好处: 1.可以使真实角色的操作更加纯粹,不用去关注一些公共业务的事 2.公共就交给了代理角色,实现了业务的分工 3.公共业务发生扩展的时候,方便集中管理 缺点: 1.一个真实角色就会产生一个代理角色 ,代码量会翻倍,开发效率变低 代码 1.接口

package demo02;

public interface UserService {

public void add();

public void delete();

public void update();

public void query();

}

2.UserServiceImpl

package demo02;

/

* @Auther: fei

* @Date: 2021/7/6 - 07 - 06 - 7:44

* @Description: demo02

* @version: 1.0

*/

public class UserServiceImpl implements UserService {

public void add(){

System.out.println("增加了一个用户");

}

public void delete(){

System.out.println("删除了一个用户");

}

public void update(){

System.out.println("更新了一个用户");

}

public void query(){

System.out.println("查询了一个用户");

}

}

3.UserServiceProxy

package demo02;

/

* @Auther: fei

* @Date: 2021/7/7 - 07 - 07 - 7:22

* @Description: demo02

* @version: 1.0

*/

public class UserServiceProxy implements UserService {

private UserServiceImpl userService;

public void setUserService(UserServiceImpl userService) {

this.userService = userService;

}

public void add() {

log("add");

userService.add();

}

public void delete() {

log("delete");

userService.delete();

}

public void update() {

log("update");

userService.update();

}

public void query() {

log("query");

userService.query();

}

public void log(String msg){

System.out.println("【debug】使用了"+msg+"方法");

}

}

4.Client

package demo02;

import client.Proxy;

/

* @Auther: fei

* @Date: 2021/7/6 - 07 - 06 - 7:45

* @Description: demo02

* @version: 1.0

*/

public class Client {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();

proxy.setUserService(userService);

proxy.delete();

}

}

image8

10.2 动态代理

动态代理和静态代理角色一样 动态代理的代理类是动态生成,不是直接写好的 动态代理分为两大类:基于接口的动态代理,基于类的动态代理 基于接口---JDK动态代理 基于类:cglib Java字节码实现:javasist 需要了解两个类:Proxy代理,InvocationHandler调用处理程序 代码 1.接口

package demo04;

public interface Rent {

public void rent();

}

2.Host代码

package demo04;

public class Host implements Rent {

public void rent() {

System.out.println("房东要出租房子!");

}

}

3.动态代理

package demo04;

import demo02.UserServiceImpl;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/

* @Auther: xiaofei

* @Date: 2021/7/7 - 07 - 07 - 9:42

* @Description: demo04

* @version: 1.0

*/

public class ProxyInvocationHandler implements InvocationHandler {

//被代理的接口

private Object target;

public void setTarget(Object target) {

this.target = target;

}

//生成得到代理类

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(),

target.getClass().getInterfaces(), this);

}

//处理代理实例,并返回结果

public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {

//动态处理的本质,就是使用反射机制实现!

log(method.getName());

Object result = method.invoke(target, args);

return result;

}

private void log(String msg) {

System.out.println("执行了"+msg+"方法");

}

}

4.Client代码

package demo04;

import demo02.UserService;

import demo02.UserServiceImpl;

/

* @Auther: xiaofei

* @Date: 2021/7/7 - 07 - 07 - 9:21

* @Description: demo03

* @version: 1.0

*/

public class Client {

public static void main(String[] args) {

UserServiceImpl userService = new UserServiceImpl();

ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setTarget(userService);

UserService proxy = (UserService) pih.getProxy();

proxy.add();

}

}

动态代理的好处: 1.可以使真实角色的操作更加纯粹,不用去关注一些公共业务的事 2.公共就交给了代理角色,实现了业务的分工 3.公共业务发生扩展的时候,方便集中管理 4.一个动态代理类代理的是一个接口,一般就是对应的一类业务 5.一个动态代理类可以代理多个类,只要是实现了同一个接口

十一、AOP

11.1 什么是AOP

Aspet Oriented Programming,面向切面编程

AOP(Aspect Oriented Programming)即面向切面编程 。它是通过预编译方式和运行期动态代理,实现对程序功能进行统一维护的技术。AOP 是 OOP(面向对象编程)的延续,是软件开发热点,也是 Spring 框架重要内容,属于函数式编程的衍生范型。利用 AOP 能够隔离业务逻辑的各个部分,降低业务逻辑各部分间耦合度,提高程序可重用性与开发效率。

image-20250505195527591

11.2 AOP在Spring中的作用

提供声明式事务:允许用户自定义切面

AOP 关键概念:

横切关注点

在大型应用程序中,像日志记录、安全校验、缓存处理、事务管理等功能,并非核心业务逻辑,但在多个模块中都会涉及。比如一个电商系统,无论商品展示、下单、支付等哪个模块,都可能需要记录操作日志,这些通用功能就属于横切关注点 。

切面(ASPECT)

它把横切关注点进行模块化封装。例如,将系统中所有的日志记录相关操作封装到一个 LoggingAspect 类中,这个类包含了日志记录的相关逻辑,就构成了一个切面。

通知(Advice)

是切面中具体要执行的动作。比如在 LoggingAspect 类中,有一个 logBefore 方法,在目标方法执行前记录日志,这个 logBefore 方法就是一个通知。常见的通知类型有前置通知(Before)、后置通知(After)、环绕通知(Around)等 。

目标(Target)

是被通知影响的对象。假设 UserService 类负责用户相关业务逻辑,LoggingAspect 要对 UserService 中的方法进行日志记录,那么 UserService 实例就是目标对象。

代理(Proxy)

Spring 通过动态代理技术,在目标对象基础上创建代理对象。代理对象会在调用目标对象方法前后,根据切面定义的通知执行相应逻辑。比如上述 UserService 有了代理对象后,调用其方法时,代理对象会先执行日志记录相关通知,再调用目标方法。

切入点(PointCut)

用于定义通知在哪些方法上执行。比如可以定义一个切入点表达式,指定只对 UserService 类中所有以 get 开头的方法执行日志记录通知,像 execution(* com.example.service.UserService.get*(..)) 这样的表达式就是切入点定义 。

连接点(JointPoint)

程序执行过程中具体的执行点,比如方法调用、异常抛出等。切入点是对连接点的筛选和定义,符合切入点规则的连接点才会触发通知执行。例如 UserService 中 getUserById 方法调用就是一个连接点,如果它匹配了定义的切入点,就会触发相关通知 。

image-20250505195628248

通知类型 连接点 实现接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor

11.3 使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包

<dependencies>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.9.4</version>

</dependency>

</dependencies>

方式一:使用Spring的API接口【主要SpringAPI接口实现】 方式二:自定义来实现AOP【主要是切面定义】 pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<artifactId>spring</artifactId>

<groupId>xiaofei</groupId>

<version>1.0-SNAPSHOT</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>spring-09-aop</artifactId>

<dependencies>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.9.4</version>

</dependency>

</dependencies>

</project>

ApplicatioContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/aop

https://www.springframework.org/schema/aop/spring-aop.xsd ">

<!--注册bean -->

<bean id="userService" class="service.UserServiceImpl"/>

<bean id="log" class="log.Log"/>

<bean id="afterLog" class="log.AfterLog"/>

<!-- 方式一:使用原生的spring api接口-->

<!--配置AOP:需要导入aop的约束 -->

<!-- <aop:config>-->

<!-- <!– 切入点:expression:表达式:execution –>-->

<!-- <aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>-->

<!-- <!– 执行环绕增加! –>-->

<!-- <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>-->

<!-- <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>-->

<!-- </aop:config>-->

<!-- 方式二:自定义类-->

<bean id="diy" class="diy.DiyPointCut"/>

<aop:config>

<!-- 自定义切面 ,ref要引用的类 -->

<aop:aspect ref="diy">

<!-- 切入点 -->

<aop:pointcut id="point" expression="execution(* service.UserServiceImpl.*(..))"/>

<!-- 通知 -->

<aop:before method="before" pointcut-ref="point"/>

<aop:after method="after" pointcut-ref="point"/>

</aop:aspect>

</aop:config>

</beans>

接口代码

package service;

public interface UserService {

public void add();

public void delete();

public void update();

public void select();

}

Log代码

package log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {

//method: 要执行的目标对象的方法

//args:参数

//target:目标对象

public void before(Method method, Object[] args, Object target) throws Throwable {

System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");

}

}

AfterLog代码

package log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/

* @Auther: fei

* @Date: 2021/7/7 - 07 - 07 - 17:18

* @Description: log

* @version: 1.0

*/

public class AfterLog implements AfterReturningAdvice {

public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);

}

}

Diy

package diy;

/

* @Auther: fei

* @Date: 2021/7/7 - 07 - 07 - 17:43

* @Description: diy

* @version: 1.0

*/

public class DiyPointCut {

public void before(){

System.out.println("======方法执行前=====");

}

public void after(){

System.out.println("======方法执行后=====");

}

}

MyTest

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import service.UserService;

import service.UserServiceImpl;

public class MyTest {

//这是一个main方法,是程序的入口:

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

//动态代理代理是接口

UserService userService = (UserService)context.getBean("userService");

userService.add();

}

}

方式三:使用注解实现

<!-- 方式三: -->_
<bean id="annotationPointCut" class="diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>

代码

package diy;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.Signature;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

//使用注解方式实现AOP

@Aspect //标注这个类是一个切面

public class AnnotationPointCut {

@Before("execution(* service.UserServiceImpl.*(..))")

public void before(){

System.out.println("======方法执行前=====");

}

@After("execution(* service.UserServiceImpl.*(..))")

public void after(){

System.out.println("======方法执行后=====");

}

//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点

@Around("execution(* service.UserServiceImpl.*(..))")

public void around(ProceedingJoinPoint jp) throws Throwable {

System.out.println("环绕前");

Signature signature = jp.getSignature();

System.out.println("signnature:"+signature);

Object proceed = jp.proceed();

System.out.println("环绕后");

System.out.println(proceed);

}

}

运行结果: 环绕前 signnature:void service.UserService.add() ======方法执行前===== 增加了一个用户 环绕后 null ======方法执行后=====

十二、整合MyBatis

步骤: 1.导入相关Jar包:junit、myBatis、MySQL数据库、spring相关、aop织入、myBatis-spring 2.编写配置文件 3.测试 基本环境搭建

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<parent>

<artifactId>spring</artifactId>

<groupId>xiaofei</groupId>

<version>1.0-SNAPSHOT</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>spring-10-mybatis</artifactId>

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.47</version>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.5.2</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>5.3.7</version>

</dependency>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.8.13</version>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>2.0.2</version>

</dependency>

</dependencies>

</project>

12.1 回忆mybatis

1.编写实体类 2.编写核心配置文件 3.编写接口 4.编写Mapper.xml

12.2 Mybatis-spring

1.编写数据源配置 2.sqlSessionFactory 3.sqlSessionTemplate 4.需要给接口加实现类 5.将自己写的实现类,注入到spring中 6.测试使用

十三、声明式事务

1.回顾事务

1.把一组业务当成一个业务来做;要么都成功,要么都失败 2.完整性、一致性 事务ACID原则:原子性、一致性、隔离性、持久性

2.spring中的事务管理

1.声明式事务 2.编程式事务:需要在代码中进行事务的管理 思考:为什么需要事务? 1、如果不配置事务,可能存在数据提交不一致的情况 2.如果不在spring中配置声明事务,就需要在代码中手动配置事务 3.事务在项目的开发中非常重要,涉及到数据一致性和完整性