Spring

Spring

1 简介

  • Spring:春天----->给软件行业带来了春天

  • 2002年,首次推出了Spring 框架的雏形:interface21 框架

  • Spring 框架即以 interface21 框架未基础,经过重新设计,并不断丰富其内涵,与2004年3月24日,发布了1.0正式版

  • Rod Johnson,Spring Framework 创始人,著名作者。很难想象Rod Johnson 的学历,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学

  • Spring理念:使现有的技术更加容易使用,本身就是一个大杂烩,整合了现有的技术框架

  • 目的:解决企业应用开发的复杂性

  • SSH:Struct2 + Spring + Hibernate

  • SSM:SpringMVC + Spring + MyBatis

官网☑️:https://spring.io

下载地址☑️:https://repo.spring.io/ui/native/release/org/springframework/spring

GitHub☑️:https://github.com/spring-projects/spring-framework

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

因为现在大多数公司都在使用SpringBoot 进行快速开发,学习SpringBoot的前提,是需要完全掌握Spring 及SpringMVC,承上启下的作用

弊端:发展了太久之后,违背了原来的理念,配置十分繁琐,人称:“配置地狱”

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

优点

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

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

2 IOC理论

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

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

private UserDao userDao;

// 利用set进行动态实现值得注入
public void setUserDao(UserDao userDao){
    this.userDao = userDao
}
  • 之前,程序是主动创建对象,控制权在程序员手上
  • 使用了set 注入后,程序不再具有主动性,而是变成了被动的接受对象

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

2.1 IOC本质

控制反转 IOC (Inversion of Control),是一种设计思想,DI (依赖注入)是实现IOC 的一种方法,也有人认为 DI 只是 IOC 的另一种说法,没有 IOC 的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓的控制反转就是:获得依赖对象的方式反转了。

采用 XML 方式配置 Bean 的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把二者结合为一体,Bean 的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取待定对象的方式,在Spring 中实现控制反转的是IOC 容器,其实现方法是依赖注入(Dependency Injection,DI)

ctrl alter u

ctrl h

ctrl alter v

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

配置元数据(配置 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">

    <!--使用Spring 来创建对象,在Spring 这些都称为Bean
        类型 变量名 = new 类型()
        Hello hello = new Hello()
        bean = 对象   new Hello()
        id = 变量名
        class = new 的对象
        property 相当于给对象中的属性设置一个值
    -->
    <bean id="hello" class="com.amusingoh.entity.Hello">
        <property name="str" value="Spring"/>
    </bean>
</beans>

实例化容器

//获取Spring 的上下文对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

//对象现在都在Spring 中管理了,如果要使用,直接去里面取出来就可以
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);

2.2 小结

  • Hello 对象是谁创建的?

    hello 对象是由Spring创建的

  • Hello对象的属性是怎么设置的?

    hello对象的属性是由Spring容器设置的

这个过程就叫控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring 后,对象是由Spring 来创建的

反转:程序本身不创建对象,而变成被动的接收对象

依赖注入:就是利用set方法来进行注入的

IOC是一种编程思想,由主动的编程变成被动的接收

可以通过newClassPathPathXmlApplicationContext去浏览底层代码

现在,可以彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓 IOC ,一句话:对象由 Spring 来创建,管理,装配

3 IOC 创建对象的方式

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

  2. 假设要使用有参构造创建对象

    1. 下标赋值

      <!--第一种,下标赋值-->
      <bean id="user" class="com.amusingoh.entity.User">
      	<constructor-arg index="0" value="test测试"/>
      </bean>
      
    2. 类型

      <!--第二种方式:通过类型创建,不建议使用-->
      <bean id="user" class="com.amusingoh.entity.User">
      	<constructor-arg type="java.lang.String" value="测试test"
      </bean>
      
    3. 参数名

      <!--第三种,直接通过参数名来设置-->
      <bean id="user" class="com.amusingoh.entity.User">
      	<constructor-arg name="name" value="testtest"
      </bean>
      

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

4 Spring 配置

4.1 别名

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

4.2 Bean的配置

<!--id:beab 的唯一标识符,也就是相当于我们学的对象名
	class:bean 对象所对应的全限定名:包名 + 类型
	name:也是别名,而且name 可以同时去多个别名(可以以 空格、逗号、分号分隔)
-->
<bean id="user" class="com.amusingoh.entity.User" name="user1 u1,user;User1">
      <property name="name" value="13243"/>
</bean>

4.3 import

一般用于团队开发,它可以将多个配置文件,导入合并为一个

假设,一个项目中有多个人开发,这个几个人负责不同的类开发,不同的类需要注册在不同的Bean 中,我们可以利用 import 将所有人的 配置文件(xml文件)合并为一个总的

  • applicationContext.xml

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beabs3.xml"/>
    

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

5 依赖注入

5.1 构造器注入

5.2 Set方式注入【重点】

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

【环境搭建】

  1. 复杂环境

    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  2. 真实测试对象

    public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbies;
        private Map<String, String> card;
        private Set<String> games;
        private String thing;
        private Properties info;
    }
    
  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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="student" class="com.amusingoh.entity.Student">
             <!--第一种,普通值注入,value-->
            <property name="name" value="amusing测试"/>
        </bean>
        
    </beans>
    
  4. 测试

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
    Object student = context.getBean("student");
    System.out.println(student);
    
  5. 完善注入信息

  6. <bean id="address" class="com.amusingoh.entity.Address">
        <property name="address" value="lllllll"/>
    </bean>
    
    <bean id="student" class="com.amusingoh.entity.Student">
        <!--普通值注入,value-->
        <property name="name" value="amusing测试"/>
        <!--Bean注入,ref-->
        <property name="address" ref="address"/>
    
        <!--数组-->
        <property name="books">
            <array>
                <value></value>
                <value></value>
                <value></value>
            </array>
        </property>
    
        <!--List-->
        <property name="hobbies">
            <list>
                <value></value>
                <value></value>
                <value></value>
            </list>
        </property>
    
        <!--Map-->
        <property name="card">
            <map>
                <entry key="一" value="1111111111111"/>
                <entry key="二" value="2222222222222"/>
            </map>
        </property>
    
        <!--Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>DNF</value>
                <value>CSGO</value>
            </set>
        </property>
    
        <!--null-->
        <property name="thing">
            <null></null>
        </property>
        <!--Properties-->
        <property name="info">
            <props>
                <prop key="学号">123456</prop>
                <prop key="性别"></prop>
            </props>
        </property>
    </bean>
    

5.3 拓展方式注入

可以使用 p 命名空间和 c 命名空间进行注入

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

    <!--p命名空间注入,可以直接注入属性的值:property-->
    <bean name="user" class="com.amusingoh.entity.User" p:name="amusing" p:age="20"/>

    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean name="user1" class="com.amusingoh.entity.User" p:name="Amusing" p:age="22"/>
</beans>

注意点:p 命名空间和 c 命名空间不能直接使用,需要导入 xml 约束

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

5.4 Bean的作用域

Scope Description
singleton (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype 将单个 bean 定义的作用域限定为任意数量的对象实例。
request 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session 将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application 将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket 将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
  1. singleton 单例模式(Spring默认机制)

    <bean id="user" class="com.amusingoh.entity.User" c:name="amusing" c:age="20" scope="singleton"/>
    
  2. prototype 原型模式:每次从容器中 get 的时候,都会产生一个新对象

    <bean id="user1" class="com.amusingoh.entity.User" c:name="amusingoh" c:age="22" scop="prototype"/>
    
  3. 其余的 request、session、application、websocket 这些只能在 web 开发中使用到

6 Bean的自动装配

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

在Spring中有三种装配方式

  1. 在 xml 中显示的配置
  2. 在 Java 中显示的配置
  3. 隐式的自动装配 bean【重点】

6.1 ByName自动装配

<bean id="cat" class="com.amusingoh.entity.Hello">
	<property name="str" value="Spring"/>
</bean>
<bean id="dog" class="com.amusingoh.entity.Address">
	<property name="address" value="guangxi"/>
</bean>

<!--byName:会自动在容器上下文中查找,和自己对象set 方法后后面的值对应的 beanid-->
<bean id="people" class="com.amusingoh.entity.People" autowire="byName">
	<property name="name" value="amusing"/>
</bean>

6.2 ByType自动装配

<bean class="com.amusingoh.entity.Hello"/>
<bean class="com.amusingoh.entity.Address"/>

<!--byName:会自动在容器上下文中查找,和自己对象set 方法后面的值对应的 beanid
	byType:会自动在容器上下文中查找,和自己对象属性类型相同的 bean
-->
<bean id="people" class="com.amusingoh.entity.People" autowire="byType">
	<property name="name1" value="amsuingoh"/>
</bean>

小结:

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

6.3 使用注解实现自动装配

jdk 1.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"
        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 方法上使用

使用 @Autowired 可以不用编写 Set 方法了,前提是这个自动装配的属性在 IOC(Spring)容器中存在,且符合名字 byName

使用了 @Nullable 说明这个字段可以为 null

public @interface Autowired {
	boolean required() default true;
}

测试

public class People {
	//如果显示的定义了 @Autowired 的required 属性为 false,说明这个对象可以为 null,否则不允许为空
    @Autowired(required = false)
    private String name;
    @Autowired
    private Cat cat;
}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired 完成时,

可以使用@Qualifier(value="") 去配和@Autowired 的使用,指定一个唯一的bean 对象注入

public class People {
    @Autowired
    private String name;
    
	@Autowired
    @Qualifier(value="cat1")
    private Cat cat;
}

@Resource 注解

public class People {
    @Resource
    private String name;
    
    @Resource(name="cat2")
    private Cat cat;
}

小结:

@Resource@Autowired 的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 通过 byType 的方式实现,且必须要求这个对象存在 【常用】
  • @Resource 默认通过 byName 的方式实现,如果名字找不到,这通过 byType 实现,如果两个都找不到的情况下,就报错
  • 执行顺序不同:@Autowired 通过 byType的方式实现,@Resource 默认通过 byName 的方式实现

7 使用注解开发

在 Spring4 之后,要使用注解开发,必须导入 aop的包

使用注解需要到人 context 约束,增加注解的支持

<?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:component-scan base-package="com.amusingoh"/>
    <context:annotation-config/>

</beans>
  1. bean

  2. 属性如何注入

    @Component
    public class User {
        public String name;
        
        //相当于 <property name="name" value="amsuing"/>
        @Value("amusing")
        public void setName(String name) {
            this.name=name;
        }
    }
    
  3. 衍生的注解

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

    • mapper @Repository
    • service @Service
    • controller @Controller

    这四个注解功能都是一样的,都是代表将某个类注册到Spring 走,装配Bean

  4. 自动装配

    • @Autowired通过 byType 的方式实现,且必须要求这个对象存在

    • 自动装配无法通过一个注解@Autowired 完成时,

      可以使用@Qualifier(value="") 去配和@Autowired 的使用

    • @Nullable 说明这个字段可以为 null

    • @Resource 默认通过 byName 的方式实现,如果名字找不到,这通过 byType 实现,如果两个都找不到的情况下,就报错

  5. 作用域

    @Component
    @Scope("prototype")
    public class User {
        private String name;  
    }
    
  6. 小结

    xml 与注解:

    • xml 更加万能,适用于任何场合,维护简单方便
    • 注解不是自己的类是使用不了,维护相对复杂

    xml 与注解最佳实践:

    • xml 用来管理 bean

    • 注解只负责完成属性的注入

    • 在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

      <!--指定要扫描的包,这个包下的注解就会生效-->
      <context:component-scan base-package="com.amusingoh"/>
      <context:annotation-config/>
      

8 使用Java的方式配置Spring

现在要完全不使用Spring 的xml 配置,全权交给 Java 来做

JavaConfig 是Spring 的一个子项目,在 Spring4 之后,它成为了一个核心功能

实体类

//这个注解是说明,这个类被Spring 接管了,注册到了容器中
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("顶瘾喔")   //属性注入值
    public void setName(String name) {
        this.name = name;
    }

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

配置类

//这个也会被Spring容器托管,注册到容器中,因为它本身就是一个 @Component
// @Configuration 代表这是一个配置类,就和我们的 beans.xml
@Configuration
@ComponentScan("com.amusingoh.entity")
//@Import(config.class) //导入其他配置类
public class MyConfig {
    //注册一个bean,就相当于写一个bean标签
    //这个方法的名字,就相当于bean 标签中的id 属性
    //这个方法的返回值,就相当于 bean标签中的class 属性
    @Bean
    public User user() {
        return new User();  //就是返回要注入的bean的对象
    }
}

测试

//如果完全使用了配置类方式去做,就只能通过 AnnotationConfig 上下文来回去容器,通过配置类的 class对象加载
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user);

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

9 代理模式

学习代理模式,这是 SpringAOP 的底层 [ SpringAOP 和 SpringMVC ]

代理模式分类:

  • 静态代理
  • 动态代理

9.1 静态代理

角色分析:

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

代码步骤:

  1. 接口

    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    public class Landlord implements Rent {
        @Override
        public void rent() {
            System.out.println("Landlord want rent out host");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent{
        private Landlord landlord;
    
        public Proxy() {
        }
    
        public Proxy(Landlord landlord) {
            this.landlord = landlord;
        }
    
        @Override
        public void rent() {
            seeHouse();
            landlord.rent();
            signContract();
            fare();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("proxy show you the house");
        }
    
        //签合同
        public void signContract() {
            System.out.println("签合同");
        }
    
        //收费
        public void fare() {
            System.out.println("收费");
        }
    }
    
  4. 客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要出租
            Landlord landlord = new Landlord();
    
            //代理,中介帮房东出租,但是代理角色一般会有一些附属操作
            Proxy proxy = new Proxy(landlord);
    
            //客户直接租房
            proxy.rent();
        }
    }
    

代理模式的好处

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务就交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实的角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

面向切面编程

面向切面

9.2 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是直接写好的
  • 动态代理分为两大类,基于接口的动态代理,基于类的动态代理
    • 基于接口——JDK 动态代理
    • 基于类:cglib
    • Java字节码实现:javasist

需要了解两个类:Proxy 代理,InvocationHandler 调用处理程序

自动生成代理类

//自动生成代理类
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);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg) {
        System.out.println("run"+msg+"方法");
    }
}

动态代理好处:

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

10 AOP

10.1 什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能得统一维护得一种技术,AOP是OOP得延续,是软件开发中得一个热点,也是Spring框架中得一个重要内容,是函数式编程得一种衍生泛型,利用AOP可以对业务逻辑得各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

10.2 AOP在Spring中的作用

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

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

AOP在不改变原有代码的情况下,去增加新的功能

10.3 使用Spring实现AOP

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

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version> 
</dependency>

方式一:使用 Spring 的 API 接口 [主要 SpringAPI 接口实现]

接口

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

实现接口

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add");
    }
    @Override
    public void delete() {
        System.out.println("delete");
    }
    @Override
    public void update() {
        System.out.println("update");
    }
    @Override
    public void select() {
        System.out.println("select");
    }
}

通知

public class Before implements MethodBeforeAdvice {
    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}
public class After implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);
    }
}

配置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 id="userService" class="com.amusingoh.aop.UserServiceImpl"/>
    <bean id="before" class="com.amusingoh.aop.Before"/>
    <bean id="after" class="com.amusingoh.aop.After"/>

    <!--配置AOP-->
    <aop:config>
        <!--切入点: expression:表达式  execution(要执行的位置  * * * * *)-->
        <aop:pointcut id="pointcut" expression="execution(* com.amusingoh.aop.UserServiceImpl.*(..))"/>
        
        <!--执行环绕增加-->
        <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试

@Test
public void testPointcut() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    //动态代理,代理的是接口
    UserService userService = context.getBean("userService", UserService.class);

    userService.select();
}

方式二:自定义来实现AOP [主要是切面定义]

接口

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

接口实现类

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add");
    }
    @Override
    public void delete() {
        System.out.println("delete");
    }
    @Override
    public void update() {
        System.out.println("update");
    }
    @Override
    public void select() {
        System.out.println("select");
    }
}

自定义切面

public class MyPointcut {
    public void before() {
        System.out.println("执行前");
    }

    public void after() {
        System.out.println("执行后");
    }
}

配置

<?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 id="userService" class="com.amusingoh.aop.UserServiceImpl"/>

    <bean id="myPointcut" class="com.amusingoh.aop.MyPointcut"/>
    <aop:config>
        <!--自定义切面,ref 要引用的类-->
        <aop:aspect ref="myPointcut">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.amusingoh.aop.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

测试

@Test
public void testPointcut() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    //动态代理,代理的是接口
    UserService userService = context.getBean("userService", UserService.class);

    userService.select();
}

方式三:使用注解实现

@Aspect //标注这是一个切面类
public class AnnotationPointcut {
    @Before("execution(* com.amusingoh.aop.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("---执行前---");
    }

    @After("execution(* com.amusingoh.aop.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("---执行后---");
    }
}

配置xml

<!--注解实现AOP-->
<bean id="annotationPointcut" class="com.amusingoh.aop.AnnotationPointcut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>

11 整合MyBatis

步骤:

  1. 导入相关jar 包
    • Junit
    • MyBatis
    • MySQL 数据库
    • Spring相关的
    • AOP织入
    • MyBatis-Spring
  2. 编写配置文件
  3. 测试

11.1 Mybatis步骤

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

11.2 MyBatis-Spring

方式一

  1. 编写数据源配置

    • spring-dao.xml
    <?xml version="1.0" encoding="UTF8"?>
    <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
        -->
        <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/test?serverTimezone=Asia/Shanghai"/>
            <property name="username" value="test"/>
            <property name="password" value="123456"/>
        </bean>
    
        <!--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/amusingoh/mapper/xml/*.xml"/>
        </bean>
    
        <!--SqlSessionTemplate: 就是我们使用的sqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="mybatisMapper" class="com.amusingoh.mapper.MybatisMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    </beans>
    
    • mybatis-config.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <package name="com.amusingoh.entity"/>
        </typeAliases>
    </configuration>
    
  2. sqlSessionFactory

    <!--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/amusingoh/mapper/xml/*.xml"/>
    </bean>
    
  3. sqlSessionTemplate

    <!--SqlSessionTemplate: 就是我们使用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    
  4. 需要给接口加上实现类

    public class MybatisMapperImpl implements MybatisMapper{
    
        //在原来所有的操作,都是用sqlSession来执行,现在都使用SqlSessionTemplate
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession=sqlSession;
        }
        @Override
        public List<Mybatis> queryMybatis() {
            MybatisMapper mapper = sqlSession.getMapper(MybatisMapper.class);
            return mapper.queryMybatis();
        }
    }
    
  5. 将实现类注入到Spring 中

  6. 测试

    @Test
    public void testMybatis() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        MybatisMapper mybatisMapper = context.getBean("mybatisMapper", MybatisMapper.class);
        for (Mybatis mybatis : mybatisMapper.queryMybatis()) {
            System.out.println(mybatis);
        }
    }
    

方式二

  1. 编写数据源配置

    • spring-dao.xml

      <?xml version="1.0" encoding="UTF8"?>
      <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
          -->
          <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/test?serverTimezone=Asia/Shanghai"/>
              <property name="username" value="test"/>
              <property name="password" value="123456"/>
          </bean>
      
          <!--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/amusingoh/mapper/xml/*.xml"/>
          </bean>
      
          <bean id="mybatisMapper2" class="com.amusingoh.mapper.MybatisMapper2">
              <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
          </bean>
      </beans>
      
  2. sqlSessionFactory

    <!--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/amusingoh/mapper/xml/*.xml"/>
        </bean>
    
  3. 接口实现类

    public class MybatisMapper2 extends SqlSessionDaoSupport implements MybatisMapper {
        @Override
        public List<Mybatis> queryMybatis() {
            return getSqlSession().getMapper(MybatisMapper.class).queryMybatis();
        }
    }
    
  4. 测试

    @Test
    public void testMybatis() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        MybatisMapper mybatisMapper = context.getBean("mybatisMapper2", MybatisMapper.class);
        for (Mybatis mybatis : mybatisMapper.queryMybatis()) {
            System.out.println(mybatis);
        }
    }
    

12 声明式事务

12.1 回顾事务

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

事务 ACID 原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会在被影响,被持久化的写入到磁盘中

12.2 Spring中的事务管理

  • 声明式事务:AOP

    <!--结合AOP实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManger">
        <!--给那些方法配置事务-->
        <!--配置事务的传播特性-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置事务织入-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.amusingoh.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
    
  • 编程式事务:需要再代码中,进行事务的管理

为什么需要事务

  • 如果不配置事务,可能存在数据提交不一致的情况
  • 如果不在Spring 中去配置声明式事务,就需要再代码中手动配置事务
  • 事务再项目的开发中十分重要,设计到数据的一致性和完整性的问题
posted @   顶瘾喔  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示