Spring

1. 简介

https://blog.csdn.net/m0_60139894/article/details/121951846

  • Spring:春天----->给软件行业带来了春天!
  • 2002,首次推出了Spring框架的雏形:interface21框架!
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式
    版。
  • Rod Johnson, Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
  • spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!

官网:https://spring.io/projects/spring-framework
官方下载地址:http:l/repo.spring.io/release/org/springframework/spring
GitHub地址:https://github.com/spring-projects/spring-framework
maven地址,在https://mvnrepository.com/搜索使用Spring Web MVC

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.0.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.0.8</version>
</dependency>

1.1 优点

  • Spring是一个开源的免费的框架(容器)!
  • Spring是一个轻量级的、非入侵式的框架!
  • 控制反转 (1OC),面向切面编程 (AOP)!
  • 支持事务的处理,对框架整合的支持!

总结一句话:Spring就是一个轻量级的控制反转 (IOC) 和面向切面编程(AOP)的框架!

1.2 组成

在这里插入图片描述

1.3 拓展

在spring官网有这个介绍:现代化的java开发!其实就是基于Spring的开发!
在这里插入图片描述

  • Spring Boot
    • 一个快速开发的脚手架。
    • 基于SpringBoot可以快速开发单个微服务。
    • 约定大于配置
  • Spring Cloud
    • SpringCloud是基于SpringBoot实现的。

因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!Spring是一个承上启下的作用。
弊端:发张了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱”。

2. IOC理论推导

原来实现一个业务:

  1. UserDao 接口
  2. UserDaoImpl 实现类
  3. UserService 业务接口
  4. UserServiceImpl 业务实现类

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序
代码量十分大,修改一次的成本代价十分昂贵!

我们使用一个Set接口实现.

  • 之前,程序是主动创建对象!控制权在程序员手上!
  • 使用set注入后,程序不再具有主动性,而是变成了被动的接受对象!
    在这里插入图片描述

这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上!这是IOC的原型!

1.2 IoC本质

**控制反转loC(lnversion of Control),是一种设计思想,DI(依赖注入)是实现1oC的一种方法,**也有人认为D只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
在这里插入图片描述
在这里插入图片描述
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)井通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入 (Dependency Injection,Dl)。

3.HelloSpring

思考:没有使用new,为什么会出来结果。
Hello.java

package com.zy.pojo;

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+'\'' +
                '}';
    }
}

IDEA在导入spring以来后,可以自动生成applicationcontext文件,在resource右键new,XML Configuration File,Spring 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--使用Spring来创建对象,在Spring这些都称为bean-->
    <bean id="hello" class="com.zy.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>
</beans>

注意:若是提示没有找到spring中的哪个文件,那就是依赖的问题,可以在项目的文件结构里看看项目的依赖。
在这里插入图片描述

4. IoC创建对象的方式

  1. 使用无参构造创建对象。默认!
package com.zy.pojo;

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);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.zy.pojo.User">
        <property name="name" value="zy"/>
    </bean>
</beans>
  1. 假设我们要使用有参构造创建对象。
    1. 下标赋值
<bean id="user" class="com.zy.pojo.User">
        <!--第一种创建对象的方式,下标赋值-->
        <!--0表示构造方法中的第一个参数-->
        <constructor-arg index="0" value="zy"/>
    </bean>
 2. 类型
<!--第二种方式:通过类型赋值,不建议使用-->
    <bean id="user" class="com.zy.pojo.User">
        <constructor-arg type="java.lang.String" value="zy"/>
    </bean>
3. 参数名
<!--第三种,直接通过参数名来设置-->
    <bean class="com.zy.pojo.User" id="user">
        <constructor-arg name="name" value="zy"/>
    </bean>

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

5. Spring配置

5.1 别名

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

5.2 Bean的配置

id:bean的唯一标识符,也就是相当于对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名,但是可以同时取多个别名。例如:name="user1,u1,u2 他们都指向同一个id

5.3 import

一般用于团队开发,它可以将多个配置文件导入合并为一个。
假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

  • 张三
  • 李四
  • 王五
  • applicationContext.xmI
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

使用的时候,直接使用总的配置就可以了

6. 依赖注入

6.1 构造器注入

前面已经说过了

6.2 Set方式注入【重点】

  • 依赖注入:set注入!
    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象中的所有属性,由容器来注入!

【环境搭建】

  1. 复杂类型
package com.zy.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
  1. 真是测试对象
package com.zy.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

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 wifi;
    private Properties info;

    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 getWifi() {
        return wifi;
    }

    public void setWifi(String wifi) {
        this.wifi = wifi;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}
  1. 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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.zy.pojo.Student">
        <!--第一种,普通注入,value-->
        <property name="name" value="zy"/>
    </bean>

</beans>
  1. 测试类
import com.zy.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());
    }
}

完善注入信息

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="address" class="com.zy.pojo.Address">
        <property name="address" value="西安"/>
    </bean>
    <bean id="student" class="com.zy.pojo.Student">
        <!--第一种,普通注入,value-->
        <property name="name" value="zy"/>
        <!--第二种,Bean注入,引用类型使用ref-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <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>
        <!--List注入,类似键值对-->
        <property name="card">
            <map>
                <entry key="身份证" value="11111221323123123"/>
                <entry key="银行卡" value="12313123123123123"/>
            </map>
        </property>
        <!--Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>
        <!--null-->
        <property name="wifi">
            <null/>
        </property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="driver">201904</prop>
                <prop key="url">nan</prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>

</beans>

6.3 扩展方式注入

6.3.1 c命名空间注入

给xml文件前面要加入xmlns:p=http://www.springframework.org/scheam/c

<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
<!--
c命名空间注入需要在类里添加有参构造
c命名空间注入,通过构造器注入:construct-args
-->
    <bean id="user2" class="com.zy.pojo.User" c:age="18" c:name="zhaoyang"/>
</beans>
 @Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = (User) context.getBean("user2");
        System.out.println(user);
    }

6.3.2 p命名空间注入

给xml文件前面要加入xmlns:p=http://www.springframework.org/scheam/p

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--p命名空间注入,可以直接注入属性的值,property-->
    <bean id="user" class="com.zy.pojo.User" p:name="zy" p:age="18"/>
</beans>
@Test
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = (User) context.getBean("user");
        System.out.println(user);
    }
}
package com.zy.pojo;

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 +
                '}';
    }
}

6.4 bean的作用域

在这里插入图片描述

  1. singleton:单例模式,只有一个实例,同一个id,默认
    在这里插入图片描述
<bean id="user" class="com.zy.pojo.User" p:name="zy" p:age="18" scope="singleton"/>
  1. 原型模式:每次从容器中get的时候,都会产生一个新对象。
    在这里插入图片描述
<bean id="user" class="com.zy.pojo.User" p:name="zy" p:age="18" scope="prototype"/>
  1. 其余的request、session、application这些只能在web开发中使用到。

7. Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式;
  • Spring会在上下文中自动寻找bean,并自动给bean装配属性。

在Spring中有三种装配方式

  1. 在xml中显示的配置(上面提到的xml文件)
  2. 在Java中显示配置
  3. 隐式的自动装配

7.1 测试

  • 一个人,两个宠物
 <bean id="cat" class="com.zy.pojo.Cat"/>
    <bean id="dag" class="com.zy.pojo.Dog"/>

    <bean id="people" class="com.zy.pojo.People">
        <property name="name" value="zhaoyang"/>
    <!--引用类型用ref-->
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dag"/>
    </bean>

7.3 自动装配Autowire

<!--byName:会在容器上下文中查找,和自己对象set方法后面的值对应的bean id!-->

    <bean id="people" class="com.zy.pojo.People" autowire="byName">
        <property name="name" value="zhaoyang"/>
    </bean>
<!--byType:会在容器上下文中查找,和自己对象属性类型相同的bean!要求就是类型得唯一,所以甚至不需要上面dog和cat的id-->
    <bean id="people" class="com.zy.pojo.People" autowire="byType">
        <property name="name" value="zhaoyang"/>

小结:

  • byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

7.4 使用注解实现自动装配

使用注解须知:

  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"
       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:annotation-config/>

</beans>

@Autowired
直接在属性上使用即可!也可以在set方法上使用!
有了注解可以连set方法都不需要写,前提是你这个自动装配的属性在IoC(Spring)容器中存在,且符合名字byName!

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

小结:
@Resource 和@ Autowired 的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @ Autowired 通过byType的方式实现,而且必须要求这个对象存在!
    【常用】
  • @ Resource 默认通过byname的方式实现,如果找不到名字,则通过byType的方式实现。
    项目开发小技巧,在note里记一些笔记和常用的内容。
    在这里插入图片描述

8. 使用注解开发

在Spring4之后,要使用注解开发,必须保证aop的包导入了
在这里插入图片描述
使用注解需要导入context约束,增加注解的支持!

  1. bean如何用注解实现
  2. 属性如何注入
package com.zy.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于<bean id="user" class="com.zy.pojo.User"/> component:组件
@Component
public class User {
    //等价于<property name="name" value="zhaoyang"/>
    @Value("zhaoyang")
    public String name;
}
  1. 衍生的注解
    @Component有几个衍生注解,在web开发中,会按照mvc三层架构分层!以下几个注解的功能和@Component一样,就是各层习惯用哪个注解

    • dao[@Repository]
    • service[@Service]
    • controller[Comtroller]
      但是记着要在xml里添加自动扫描
      这四个注解功能是一样的,都是代表将某个类注册到Spring中,装配Bean
  2. 自动装配

  3. 作用域
    @Scope(“之前提到过的单例或者prototype”)

  4. 小结
    xml 与注解:

  • xml更加万能,适用于任何场合!维护简单方便
  • 注解 不是自己类使用不了,维护相对复杂!
    xml 与注解最佳实践:
  • xml 用来管理bean;
  • 注解只负责完成属性的注入;
  • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
<!--指定要扫描的包,这个包下面的注解就会生效-->
    <context:component-scan base-package="com.zy.pojo"/>
    <context:annotation-config/>

9. 使用Java的方式配置Spring

我们现在完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为一个核心功能!
在一个类上加了@Configuration就类似于bean
在这里插入图片描述
实体类

package com.zy.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

//这里这个注解的意思,就是说明这个累被Spring接管了,注册到容器中
@Component
public class User {
    @Value("yuxin")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

配置文件

package com.zy.config;

import com.zy.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//这个也会被Spring容器托管,注册到容器中,因为进入到configuration它本身就是一个@Component
//@Configuration代表这是一个配置类,就和我们之前看的beans.xml一样
@Configuration
@ComponentScan("com.zy.pojo")
@Import(YuConfig2.class)    //这样就把两个类引成一个类,也相当于导入
public class YuConfig {
    //注册一个bean,就相当于我们之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User getUser() {
        return new User();  //就是返回要注入到bean的对象!
    }
}

测试类

import com.zy.config.YuConfig;
import com.zy.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //如果使用了配置类的方法去做,就只能通过AnnotationConfig上下文来获取容器,通过配置累的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(YuConfig.class);
        User getUser = (User) context.getBean("getUser");
        System.out.println(getUser.getName());
    }
}

这种纯Java的配置方式在SpringBoot中随处可见

10. AOP(面向切面编程)

为什么要学习代理模式?因为这就是SpringAOP的底层!
在这里插入图片描述

10.1 静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,一般会做一下附属操作
  • 客户:访问代理对象的人!

代码步骤

  1. 接口
package com.zy.demo01;

//租房
public interface Rent {
    public void rent();
}
  1. 真实角色
package com.zy.demo01;

//房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房了");
    }
}
  1. 代理角色
package com.zy.demo01;

//代理,租房的中介
public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }


    @Override
    public void rent() {
        host.rent();
        seeHouse();
        heTong();
        fare();
    }

    //看房
    public void seeHouse() {
        System.out.println("中介带你看房子");
    }

    //签合同
    public void heTong() {
        System.out.println("签租赁合同");
    }

    //收中介费
    public void fare() {
        System.out.println("收中介费");
    }
}
  1. 客户端访问代理角色
package com.zy.demo01;

//租客租房
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        //代理,中介帮房东租房子,但是,中介一般会有一些复数操作,比如签合同,看房,收中介费。
        Proxy proxy = new Proxy(host);
        //不用直接面对房东,直接找中介即可
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
    缺点:
  • 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低~

10.2 加深理解

接口

package com.zy.demo02;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

真实角色

package com.zy.demo02;

//真实对象
//这个就是纯粹的执行业务功能,要想增加一个打印日志,就是去代理文件里增加
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("Add a user!");
    }

    @Override
    public void delete() {
        System.out.println("Delete a User!");
    }

    @Override
    public void update() {
        System.out.println("Update a user!");
    }

    @Override
    public void query() {
        System.out.println("Query a user!");
    }
}

代理

package com.zy.demo02;

public class UserServiceProxy implements UserService{
    //代理要代理真实角色,所以引入真实角色
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }   //引入

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    //日志方法
    public void log(String msg) {
        System.out.println("使用了"+msg+"方法!");
    }
}

用户

package com.zy.demo02;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        //用户接下来就只用找代理去做了
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);      //启用代理,代理USI
        proxy.add();
    }
}

在这里插入图片描述

10.3 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是自动生成的,不是我们直接写的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口— JDK动态代理【这里使用】
    • 基于类:cglib
    • Java字节码实现:javasist

需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
这相当于万能的模版,修改红框里的类名
在这里插入图片描述
动态代理的好处:

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

11. AOP

11.1 什么是AOP

在这里插入图片描述

11.2 AOP在Spring中的作用

提供声明式事物,允许用户自定义切面。

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,气我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
  • 切面 (ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知 (Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标 (Target):被通知对象。
  • 代理 (Proxy):向目标对象应用通知之后创建的对象。
  • 切入点 (PointCut):切面通知执行的“地点"的定义。
  • 连接点 UointPoint):与切入点匹配的执行点。
  • 在这里插入图片描述

11.3 使用Spring实现AOP

使用AOP,需要导入一个依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.19</version>
    <scope>runtime</scope>
</dependency>

方式一:使用Spring的API接口【主要是SpringAPI接口实现】
实现类

package com.zy.service;

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 select() {
        System.out.println("查询了一个用户");
    }
}

日志作为切入

package com.zy.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()+"被执行了");
    }
}
package com.zy.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    //returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);
    }
}

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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    注册bean-->
    <bean id="userService" class="com.zy.service.UserServiceImpl"/>
    <bean id="log" class="com.zy.log.Log"/>
    <bean id="afterLog" class="com.zy.log.AfterLog"/>

<!--    方式一;使用Spring API接口-->
<!--    配置AOP:需要导入AOP的约束-->
    <aop:config>
<!--        切入点:expression表达式 expression(要执行的位置 * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.zy.service.UserServiceImpl.*(..))"/>
<!--        执行环绕增加-->
<!--        将log这个类切入到pointcut这个切入点-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>

    </aop:config>
</beans>

方式二:自定义类【主要是切面定义】
新加入一个要切入的类

package com.zy.diy;

public class DiyPointCut {
    public void before() {
        System.out.println("=======方法执行前======");
    }
    public void after() {
        System.out.println("=======方法执行后======");
    }
}

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
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.zy.service.UserServiceImpl"/>
    <bean id="log" class="com.zy.log.Log"/>
    <bean id="afterLog" class="com.zy.log.AfterLog"/>

    <!--方式二:自定义类-->
    <bean id="diy" class="com.zy.diy.DiyPointCut"/>
    
    <aop:config>
        <!--自定义切面,ref是要引用的类-->
        <aop:aspect ref="diy">
        <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.zy.service.UserServiceImpl.*(..))"/>
        <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

</beans>

方式三:使用注解实现!

package com.zy.diy;

//方式三:使用注解实现AOP

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.zy.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("=======方法执行前======");
    }

    @After("execution(* com.zy.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("=======方法执行后======");
    }

    @Around("execution(* com.zy.service.UserServiceImpl.*(..))")
    public void round(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        //执行方法
        Object proceed = jp.proceed();

        System.out.println("环绕后");
    }
}
<!--方式三:使用注解-->
    <bean id="annotationPointCut" class="com.zy.diy.AnnotationPointCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

12. 整合MyBatis

步骤:

  1. 导入相关jar包
    • junit
    • mybatis
    • mysql数据库
    • spring
    • aop织入
    • mybatis-spring
<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.0.9</version>
        </dependency>
        <!--Spring操作数据库的话,需要一个spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.9</version>
        </dependency>
        <!--AOP织入包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.19</version>
            <scope>runtime</scope>
        </dependency>
        <!--mybatis整合包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.2</version>
        </dependency>
  1. 编写配置文件
  2. 测试

12.1 回忆mybatis

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试
    详细看mybatis笔记

12.2 Mybatis-Spring

什么是mybatis-spring

mybatis-spring会帮助你将mybatis代码无缝的整合到spring中。

文档链接:http://mybatis.org/spring/zh/index.html

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>

12.2.1 整合实现一:

  1. 引入spring配置文件spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
  1. 配置数据源替换mybatis的数据源
<?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">

    <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
    我们这里使用Spring提供的JDBC:org.springframework.jdbc.datasource
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
  1. 配置SQLSessionFactory,关联mybatis
    <!--配置SQLSessionFactory,关联mybatis-->

    <!--配置SQLSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--绑定Mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/zy/mapper/*xml"/>
    </bean>
  1. 注册sqlsessionTemplate,关联SQLSessionFactory
    <!--注册sqlsessionTemplate,关联SQLSessionFactory-->
    <!--SqlSessionTemplate:就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
  1. 需要UserMapper接口的UserMapperImpl实现类,私有化sqlsessionTemplate,实现类就干这一件事情
public class UserMapperImpl implements UserMapper{
    //所有的操作,在原来,都是用sqlSession来执行,现在我们都使用sqlSessionTemplate;
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
  1. 将自己写的实现类,注入的spring配置中
    <bean id="userMapper" class="com.zy.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
  1. 测试使用即可!
    @Test
    public void selectUser() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

可以使用applicationContext.xml,导入spring-dao.xml,这就可以更方便的整合。

12.2.2 整合实现二

在这里插入图片描述
将我们上面写的UserMapperImpl修改一下

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    @Override
    public List<User> selectUser() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

注入到Spring配置文件中。

    <bean id="userMapper2" class="com.zy.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

测试

    @Test
    public void selectUser() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

13 声明式事务

13.1 回顾事务

  • 把一组业务当作一个业务来做,要么都成功,要么都失败!
  • 事务在项目开发中十分重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性!

事务的ACID原则:

  • 原子性:
    • 事务是原子性操作,要么同时全部完成,要么完全不起作用。
  • 一致性:
    • 一旦所有事务动作完成,事务就要被提交,数据和资源处于一种满足业务规格的一致性状态
    • 事务提交前和提交后,数据的完整性不变
  • 隔离性:
  • 多个事务同时处理相同的数据,每个事务都应该与其他事务隔离开来,防止数据损坏
  • 持久性
    • 事务一旦完成,无论系统发生什么错误,结果都不会收到影响,通常情况下,事务一旦被提交,就会被持久化到数据库中。

测试:

将上面的代码拷贝到一个新项目中
在之前的案例中,我们给userMapper接口新增两个方法,删除和增加用户;

    //添加一个用户
    public int addUser(User user);

    //删除一个用户
    public int deleteUser(int id);

UserMapper文件,我们故意把 deletes 写错,测试!

    <insert id="addUser" parameterType="user">
        insert into mybatis.user(id, name, pwd)
        values (#{id},#{name},#{pwd});
    </insert>
    <delete id="deleteUser" parameterType="int">
        deletes from mybatis.user
        where id=#{id};
    </delete>

编写接口的UserMapperImpl实现类,在实现类中,我们去操作一波

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
    @Override
    public List<User> selectUser() {

        User user = new User(5, "小王", "123456");

        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(user);
        mapper.deleteUser(5);
        return mapper.selectUser();
    }

测试

@Test
    public void selectUser() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

报错:sql异常,delete写错了

结果 :数据库结果显示插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

以前我们都需要自己手动管理事务,十分麻烦!

但是Spring给我们提供了事务管理,我们只需要配置即可;

13.2 Spring中的事务管理

spring在不同的事务管理API之上定义了一个抽象层,是的开发人员不必了解底层的事务管理API就可以使用spring的事务管理机制。spring支持编程式事务管理和声明式的事务管理

编程式事务管理

  • 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚

  • 缺点:必须在每个事务的操作业务逻辑中包含额外的事务管理代码
    声明式事务管理

  • 一般情况下比编程式事务管理好用

  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理

  • 将事务管理作为横切关注点,通过aop方法模块化。spring中通过spring aop框架支持声明式事务管理

  1. 使用spring管理实务,注意头文件的约束导入:tx
<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
  1. JDBC事务
    <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
  1. 配置好事务管理器后,我们需要去配置事务的通知
<!--结合AOP,实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给那些方法配置事务-->
        <!--配置事务的传播特性:new propagation-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delect" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
  1. 配置AOP,导入aop的头文件
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.zy.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用的时候,事务如何在这些方法间传播。spring支持7中事务传播行为:

  • propagation_requierd:如果没有当前事务,就新建一个事务,如果已存在一个事务中,加入这个事务;
  • propagation_support:支持当前事务,如果没有当前事务,就以非事务方法执行
  • propagation_mandatory:支持当前事务,如果没有当前事务就抛出异常
  • propagation_requierd_new:不支持当前事务,新建事务,如果当前存在任务,就把当前任务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行,如果当前存在事务,就抛出异常
  • propagation_nested:如果当前存在事务,就在嵌套事务内执行,如果当前没有事务,则执行与propagation_required类似的操作。
  • spring默认的事务传播行为是;propagation_required,它适用于绝大多数的情况。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务中。

  1. 删掉刚才插入的数据,再次测试
    @Test
    public void selectUser() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }
    }

这次直接报错,也不会只有插入成功而删除不成功的现象。

13.3 思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况;
  • 如果我们不在spring中配置声明式事务,我们就需要在代码中手动配置事务
  • 事务在项目开发中十分重要,涉及到数据的一致性和完整性的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值