Spring

Spring

1. 介绍与使用

1.1 简介

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

  • SSH:Struct2 + Spring + Hibernate
  • SSM: SpringMVC + Spring + Mybatis

官网:(https://spring.io/projects/spring-framework#overview)

官方下载地址:(https://repo.spring.io/libs-release-local/org/springframework/)

GitHub:(https://github.com/spring-projects/spring-framework/releases)

maven:

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.8</version>
</dependency>

1.2 优点

  • 是一个开源的免费的框架(容器)
  • 是一个轻量级的、非入侵的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持
  • 贯彻依赖注入(DI)思想

1.3 组成

img

1.4 拓展

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

2. IOC思想

  • 原来代码的主动性在程序员手上
  • 现在把代码的主动性交给用户

2.1 IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

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

3. HelloSpring

  1. 创建一个pojo

    package com.spring.pojo;
    
    public class HelloSpring {
        private String str;
    
        public HelloSpring(String str) {
            this.str = str;
        }
    
        public HelloSpring() {
    
        }
    
        public String getStr() {
            return str;
        }
    
        public void setStr(String str) {
            this.str = str;
        }
    
        @Override
        public String toString() {
            return "HelloSpring{" +
                "str='" + str + '\'' +
                '}';
        }
    }
    
  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">
    
        <!--    使用spring来创建对象,在spring中这些都称为bean-->
        <!--    id类似对象名  class指向类  被指向的类中一定要有无参构造-->
        <bean id="hello" class="com.spring.pojo.HelloSpring">
            <!--        property 给属性赋值
                  value 具体的值
                  ref 引用spring容器里面创建好的对象id
            -->
            <property name="str" value="Spring"/>
    
        </bean>
    
    
    </beans>
    
  3. 测试

    import com.spring.pojo.HelloSpring;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            //     获取spring的上下文对象   参数传配置文件名称,可以传多个
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //        要使用直接在这个context对象里面取就行  每一个bean代表一个对象
            //        name为bean的id
            HelloSpring hello = (HelloSpring) context.getBean("hello");
    
            System.out.println(hello.getStr());
    
        }
    }
    

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

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

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

4. IOC创建对象的方式

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

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

    1. 下标赋值

      <bean id="user" class="com.spring.pojo.User">
          <!--    使用构造器的方式创建对象  通过下标创建对象-->
          <constructor-arg index="0" value="TzeaoStudent"/>
      </bean>
      
    2. 类型匹配【不建议,两个一样的就不能使用的】

      <bean id="user" class="com.spring.pojo.User">
          <!--通过参数的类型来选择  基本数据类型可以直接写,其他必须全类名-->
          <constructor-arg type="java.lang.String" value="Tzeao1Student"/>
      </bean>
      
    3. 通过参数名创建

      <bean id="user" class="com.spring.pojo.User">
          <!--直接通过参数名称来创建  也可以通过ref引用-->
          <constructor-arg name="name" value="Tzeao1Student"/>
      </bean>
      

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

5. Spring配置

5.1 别名

<!--    name对应要设置别名的id-->
<alias name="user" alias="Tzeao"/>

设置了别名,就可以通过别名来取值

5.2 Bean的配置

<!--    name也是别名,而且对比alias更加高级,可以同时取多个别名,分号,空格,逗号都可以隔开-->
<bean id="user" class="com.spring.pojo.User" name="user2">
    <!--直接通过参数名称来创建  也可以通过ref引用-->
    <constructor-arg name="name" value="Tzeao1Student"/>
    </bea

5.3 import

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

<?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"/>
</beans>

6. DI依赖注入

6.1 构造器注入

看 第4

6.2 Set方式注入

  • 依赖注入:Set注入
    • 依赖:bean对象的创建依赖于容器
    • 注入:bena对象中的所有属性没有容器来注入!

【环境搭建】

  1. 实体类

    package com.spring.pojo;
    
    public class Address {
        private String address;
    
        public Address(String address) {
            this.address = address;
        }
    
        public Address() {
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
    package com.spring.pojo;
    
    import java.time.Period;
    import java.util.*;
    
    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 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 getWife() {
            return wife;
        }
    
        public void setWife(String wife) {
            this.wife = wife;
        }
    
        public Properties getInfo() {
            return info;
        }
    
        public void setInfo(Properties info) {
            this.info = info;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", address=" + address +
                    ", books=" + Arrays.toString(books) +
                    ", hobbys=" + hobbys +
                    ", card=" + card +
                    ", games=" + games +
                    ", wife='" + wife + '\'' +
                    ", info=" + info +
                    '}';
        }
    }
    
  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="student" class="com.spring.pojo.Student">
            <property name="name" value="Tzeao"/>
        </bean>
    </beans>
    
  3. 测试类

    import com.spring.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());
        }
    }
    
  4. 完整的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="com.spring.pojo.Address"/>
        <bean id="student" class="com.spring.pojo.Student">
            <!--        第一种普通注入-->
            <property name="name" value="Tzeao"/>
            <!--        第二种 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>
            <!--map注入-->
            <property name="card">
                <map>
                    <entry key="身份证" value="不给看"/>
                    <entry key="学生证" value="不可以看"/>
                    <entry key="献血证" value="不配看"/>
                </map>
            </property>
            <!--        set-->
            <property name="games" >
                <set>
                    <value>LOL</value>
                    <value>CF</value>
                    <value>CS</value>
                </set>
            </property>
            <!--        null-->
            <property name="wife">
                <null/>
            </property>
            <!--        properties-->
            <property name="info">
                <props>
                    <prop key="学号">201505238</prop>
                    <prop key="性别"></prop>
                </props>
            </property>
        </bean>
    </beans>
    

16.3 自定义命名空间注入

P命名空间注入:xmlns:p=“http://www.springframework.org/schema/p”

  1. <?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="com.spring.pojo.User" p:age="19" p:name="捞胖"/>
    </beans>
    

C命名空间注入: xmlns:c=“http://www.springframework.org/schema/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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd">




    <bean id="user2" class="com.spring.pojo.User" c:age="19" c:name="好家伙"/>
</beans>

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

6.4 Bean作用域

image-20210623000039590

  1. 单例模式【spring默认机制】

    <bean id="user2" class="com.spring.pojo.User" c:age="19" c:name="好家伙" scope="singleton"/>
    
  2. 原型模式:每次从容器get都会产生新的对象

    <bean id="user2" class="com.spring.pojo.User" c:age="19" c:name="好家伙" scope="prototype"/>
    
  3. 其余 :request\seesion\application:这些只能在web开发使用

7. Bean的自动装配

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

在Spring在三种装配的方式

  1. 在xml中显示的配置
  2. 在java在显示配置
  3. 隐式的自动装配

7.1 使用

<?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="com.spring.pojo.Cat"/>
    <bean id="dog" class="com.spring.pojo.Dog"/>


    <!--    byName:会自动在容器上下文查找和自己对象SET方法后面的值对应的 beanid
    byType:会自动在容器上下文查找和自己对象类型对应的 bean

    -->
    <bean id="people" class="com.spring.pojo.People" autowire="byType">
        <property name="name" value="Tzeao"/>
    </bean>
</beans>

ByName:

<!--    byName:会自动在容器上下文查找和自己对象SET方法后面的值对应的 beanid
byType:会自动在容器上下文查找和自己对象类型对应的 bean

-->
<bean id="people" class="com.spring.pojo.People" autowire="byName">
    <property name="name" value="Tzeao"/>
</bean>

ByType:

<!--    byName:会自动在容器上下文查找和自己对象SET方法后面的值对应的 beanid
byType:会自动在容器上下文查找和自己对象类型对应的 bean

-->
<bean id="people" class="com.spring.pojo.People" autowire="byType">
    <property name="name" value="Tzeao"/>
</bean>

小结:

  • byname:需要保证id唯一,且id名与set方法名称一致
  • bytype:需要保证class唯一,且保证bean的类型月set的类型一致

7.2 注解实现自动装配

准备工作:

  1. 导入约束【重要】

    context:annotation-config/

  2. 配置注解组成

    <?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
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context
                               https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:annotation-config/>
    
    </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"
           xmlns:context="http://www.springframework.org/schema/context"
           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">
    
        <!--开启注解支持-->
        <context:annotation-config/>
    
        <bean id="cat" class="com.spring.pojo.Cat"/>
        <bean id="dog" class="com.spring.pojo.Dog"/>
        <bean id="people" class="com.spring.pojo.People"/>
    
    
    </beans>
    
    package com.spring.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.lang.Nullable;
    
    public class People {
        @Autowired
        @Qualifier(value = "cat")
        private Cat cat;
        @Autowired
        private Dog dog;
        private String name;
    
        public Cat getCat() {
            return cat;
        }
    
        public void setCat(@Nullable 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 + '\'' +
                '}';
        }
    }
    
  4. @Autowired

    1. 可以在属性上注解使用和set方法上使用
    2. 可以忽略set方法
  5. @Nullable

    表示这个字段可以为空

  6. @Qualifier

    如果自动装配的属性比较赋值,自动装配无法通过一个注解完成的时候,可以通过@Qualifier取指定一个bean来完成注入

    @Autowired
    @Qualifier(value = "cat")
    private Cat cat;
    
  7. @Resource

    也是自动装配的,如果有多个bean可以通过填写nema,@Resource(name = “dog”)

小结:@Autowired和@Resource区别

  • @Resource默认通过Byname实现,如果找不到,就通过bytype实现,都找不到就报错
  • @Autowired通过bytype实现,对象必须存在

8. 使用注解开发

必须导入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
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">

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

</beans>
  1. bean

    1. 放在类上面,就说明这个类被Spring管理了
    //等价于<bean id="user" class="com.spring.pojo.User"/>
    @Component //组件
    
  2. 属性如何注入:也可以在set方法上面使用

    @Value("Tzeao")
    public String name;
    
  3. 衍生的注解

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

    • dao – 【@Resource】
    • service – 【@Service】
    • controller – 【@Controller】
    • 这四个注解都一样,只不过是架构为了区分
  4. 自动装配

  5. 作用域

    @Scope
    
  6. 小结

    1. xml万能,维护简单
    2. 注解维护复杂
    3. 一般情况是xml管理bean,注解负责属性注入

9. 使用java的方式配置spring

  1. 配置类

    package com.spring.TzeaoConfig;
    
    import com.spring.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;
    
    
    @Configuration //这个也会被spring 容器托管,注册到容器中,因为它本身就是一个@Component
    // @Configuration 代表这是一个配置类,和beans.xml一样
    @ComponentScan("com.spring.pojo")
    //扫描包
    @Import(Tzeao2Config.class)
    //引入其他配置类
    public class TzeaoConfig {
        //    注册一个bean  方法名就是id  返回值就是class
        @Bean
        public User getUser() {
            return new User();//这个就是返回要注入到bean的对象
        }
    }
    
  2. 实体类

    package com.spring.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @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 + '\'' +
                '}';
        }
    }
    
  3. 测试类

    import com.spring.TzeaoConfig.TzeaoConfig;
    import com.spring.pojo.User;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
            //         AnnotationConfigApplicationContext注解实现   通过配置类的class来加载
            ApplicationContext context = new AnnotationConfigApplicationContext(TzeaoConfig.class);
            User getUser = context.getBean("getUser", User.class);
            System.out.println(getUser.getName());
    
        }
    }
    

10. 代理模式

分类:

  • 静态代理
  • 动态代理

好处:

  • 可以让这是角色操作更加纯粹,不用取工作其他的业务
  • 公共的就交给代理,实现业务分工
  • 公共业务发送扩展,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会增加

image-20210623184837862

10.1 静态代理

代理一般会有其他附属操作

真实角色:

package com.spring.demo01;

public class Demo01 implements A{


@Override
public void rent() {
System.out.println("租房");
}
}

代理角色:

package com.spring.demo01;

public class Rent implements A {
    private Demo01 demo01;

    public Rent(Demo01 demo01) {
        this.demo01 = demo01;
    }

    public Rent() {
    }

    @Override
    public void rent() {
        demo01.rent();
    }
}

操作接口:

package com.spring.demo01;

public interface A {
public void rent();
}

客户端操作:

package com.spring.demo01;

public class B {
    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();

        Rent rent = new Rent(demo01);

        rent.rent();
    }

}

10.2 动态代理

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

两个类:Proxy 代理 , InvocationHanlder 调用处理程序

好处:

  • 一个动态代理代理的是一个接口,一般对应一类业务
  • 一个动态代理可以代理多个类,只要实现图个接口即可

真实角色类

package com.spring.demo02;

import com.spring.demo02.A;

public class Demo01 implements A {


    @Override
    public void rent() {
        System.out.println("租房");
    }
}

代理实现类

package com.spring.demo02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//会自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //    被代理接口
    private A a;


    public void setA(A a) {
        this.a = a;
    }

    //生成得到代理类
    public Object getProxy() {
        //        代理类Proxy
        return   Proxy.newProxyInstance(this.getClass().getClassLoader(),a.getClass().getInterfaces(),this);
    }

    //    处理代理实例,并返回代理结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //        执行接口的方法,本质就是执行反射机制
        Object invoke1 = method.invoke(a, args);
        System.out.println(a);
        return invoke1;
    }
}

接口

package com.spring.demo02;

public interface A {
   public void rent();
}

客户端

package com.spring.demo02;

public class Client {
    public static void main(String[] args) {
        //        真实角色
        Demo01 demo01 = new Demo01();
        //        动态生成代理
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //调用程序处理
        pih.setA(demo01);
        //        获取返回的代理对象
        A proxy = (A) pih.getProxy();
        //执行
        proxy.rent();


    }
}

11. AOP

11.1 介绍

AOP(Aspect Oriented Programming)面向切面编程:通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的核心原理之一,是函数式编程的一种衍生范型。设计模式孜孜不倦追求的时调用者和被调用者之间的解耦,AOP可以说是这种目标的一种实现。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

11.2 AOP在Spring框架中的重要作用:

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

横切关注点:跨越应用程序多个模块的方法或功能,即那些与我们业务逻辑无关的公共业务,这些业务虽然与我们的核心业务逻辑无关,但却依然需要我们给予关注,这部分公共业务就是横切关注点,如日志,安全,缓存,事务等等…

  • 切面(Aspect):横切关注点被模块化的特殊对象,是一个类,可以理解为一个动态代理类。

  • 通知(Advice):切面必须要完成的工作,即切面中的方法,也可以理解为动态代理类中的方法。

  • 目标(target):被通知的对象,即被代理的对象。

  • 代理(Proxy):向目标对象应用通知后创建的对象,可以理解为动态代理类中注入目标对象后获取的代理实例。

  • 切入点(PointCut):切面通知执行的”地点”的定义

  • 连接点(JointPoint):与切入点匹配的执行点。

img

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

img

11.3 使用

导入依赖包

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

方式一:使用Spring的API接口

方法前执行:前置通知MethodBeforeAdvice

package com.spring.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

//
public class Log implements MethodBeforeAdvice {


//    method :要执行目标对象的方法
//    objects: 参数
//    o:目标对象

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

方法后执行:后置通知AfterReturningAdvice

package com.spring.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

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

接口

package com.spring.service;

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

实现

package com.spring.service;

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void select() {
        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
                           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 id="userService" class="com.spring.service.UserServiceImpl"/>
    <bean id="log" class="com.spring.log.Log"/>
    <bean id="afterLog" class="com.spring.log.AfterLog"/>

    <!--    配置aop:需要导入aop的约束-->
    <aop:config>
<!--        切入点  expression表达式:execution(要执行的位置 * * * *  (..)表示执行所有)-->
        <aop:pointcut id="pointcut" expression="execution(* com.spring.service.UserServiceImpl.*(..))"></aop:pointcut>

<!--        执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

方式二:自定义实现AOP

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 id="userService" class="com.spring.service.UserServiceImpl"/>
    <bean id="log" class="com.spring.log.Log"/>
    <bean id="afterLog" class="com.spring.log.AfterLog"/>

    <!--    方式二 自定义类-->
    <bean id="diy" class="com.spring.diy.DiyPoinyCut"/>
    <aop:config>
        <!--            切面:说白了就是一个类  ref要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.spring.service.UserServiceImpl.*(..))"/>
            <!--                通知-->
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

自定义AOP类

package com.spring.diy;

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

其余不变,测试也不变

方式三:注解实现AOP

切面注解@Aspect

通知注解@Before(“表达式”)

package com.spring.diy;

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;

import javax.xml.ws.Action;

@Aspect//标注这个类为切面
public class AnnotationPointCut {

    @Before("execution(* com.spring.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("执行前");
    }


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

    //    环绕增强,可以给定一个参数,代表获取处理切入的点
    @Around("execution(* com.spring.service.UserServiceImpl.*(..))")
    public void a(ProceedingJoinPoint pj){
        System.out.println("环绕前");
        try {
            pj.proceed();//执行方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        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
                           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 id="userService" class="com.spring.service.UserServiceImpl"/>
    <bean id="log" class="com.spring.log.Log"/>
    <bean id="afterLog" class="com.spring.log.AfterLog"/>


    <!--    方式三-->
    <bean id="annotationPointCut" class="com.spring.diy.AnnotationPointCut"/>
    <!--    开启注解支持-->
    <aop:aspectj-autoproxy/>
</beans>

12. 整合MyBatis

12.1 导入的依赖或jar包

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.8</version>
</dependency>

<!--    spring操作数据库的话-spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.8</version>
</dependency>

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

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


12.2 Mybatis-Spring

官方网站:http://mybatis.org/spring/zh/

  1. 配置XML

    <?xml version="1.0" encoding="GBK"?>
    <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">
    
        <!--    获取的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/mybatis?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>
    
        <!--    创建sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--        绑定Mybatis配置文件 这样就可以直接再这里配置Mybatis文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <!--        注册映射器-->
            <property name="mapperLocations" value="classpath:com/spring/mapper/*.xml"/>
        </bean>
    
        <!--    SqlSessionTemplate :就是sqlSession-->
    
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--        只能使用构造器注入sqlSessionFactory,因为没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="userMapper" class="com.spring.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    </beans>
    
  2. 实现接口

    package com.spring.mapper;
    
    import com.spring.pojo.User;
    import org.mybatis.spring.SqlSessionTemplate;
    
    import java.util.List;
    
    public class UserMapperImpl implements UserMapper{
        //  现在使用SqlSessionTemplate
    
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public List<User> queryUser() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.queryUser();
        }
    }
    
  3. 测试

    import com.spring.mapper.UserMapper;
    import com.spring.pojo.User;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.util.List;
    
    public class MyTest {
        public static void main(String[] args) {
    
    
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
            UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
            List<User> users = userMapper.queryUser();
            for (User user : users) {
                System.out.println(user );
            }
    
        }
    }
    
    
  4. 还需要mybatis核心配置文件

    <?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>
            <typeAlias type="com.spring.pojo.User" alias="user"/>
        </typeAliases>
    </configuration>
    
  5. 接口的配置文件

第二种:

xml:

<?xml version="1.0" encoding="GBK"?>
<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">

    <!--    获取的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/mybatis?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--    创建sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--        绑定Mybatis配置文件 这样就可以直接再这里配置Mybatis文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--        注册映射器-->
        <property name="mapperLocations" value="classpath:com/spring/mapper/*.xml"/>
    </bean>


<!--    省略配置SqlSession-->
    <bean id="userMapper2" class="com.spring.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>

package com.spring.mapper;

import com.spring.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

// 第二种方法  继承SqlSessionDaoSupport   可以省略注入的过程
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> queryUser() {
        SqlSession sqlSession = getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.queryUser();
    }
}

测试类没变

13. 声明式事务

  1. 事务:要么都成功,要么都失败
  2. ACID:原子性,一致性,隔离性,持久性

13.1 Spring的事务管理

  • 声明式事务:AOP
  • 编程式事务:在代码中就行事务管理
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
                           http://www.springframework.org/schema/tx
                           https://www.springframework.org/schema/tx/spring-tx.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/mybatis?useSSL=true&amp;userUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--    创建sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--        绑定Mybatis配置文件 这样就可以直接再这里配置Mybatis文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--        注册映射器-->
        <property name="mapperLocations" value="classpath:com/spring/mapper/*.xml"/>
    </bean>

    <!--    SqlSessionTemplate :就是sqlSession-->

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--        只能使用构造器注入sqlSessionFactory,因为没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!--    配置声明式事务-->

    <!--    1. 开启Spring的事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--    2. 结合AOP实现事务的织入 需要导入依赖aspectj-->
    <!--    配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--        给哪些方法配置事物-->
<!--        配置事物的传播特性 propagation-->
        <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.spring.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>

结合AOP实现事务的注入

propagation的七大事务配置:

REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。

REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。

NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

posted @ 2022-03-30 19:04  Tzeao  阅读(20)  评论(0)    收藏  举报