Spring框架学习笔记详细

1.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://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#spring-core

官网: https://spring.io/projects/spring-framework#overview
官方下载地址: http://repo.spring.io/release/org/springframework/spring
GitHub: https://github.com/spring-projects/spring-framework

2.优点

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

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

3.组成

在这里插入图片描述

4.拓展

在这里插入图片描述

  • Spring Boot

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

    • SpringCloud是基于SpringBoot实现的

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

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

2.IOC理论推导

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

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

我们使用一个set接口实现。已经发生了革命性得变革!

    private UserDao userDao;

    //利用set实现动态值得注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public static void main(String[] args) {

        UserService service = new UserServiceImpl();

        ((UserServiceImpl)service).setUserDao(new UserOracleImpl());

        service.getUser();
    }
  • 之前程序是主动创建对象!控制权在程序员手上!
  • 使用了set注入后,程序不再具有主动性,变成了被动接受对象!

这种思想从本质上解决了问题,我们不需要管理对象的创建,耦合性大大降低,可以专注在业务实现上!这就是IOC原型!

在这里插入图片描述
在这里插入图片描述

3.I0C本质

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

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

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

4.第一个HelloSpring程序

环境:

  • idea集成工具
  • Maven仓库

搭建步骤:

  • 新建一个空白的Maven工程

在这里插入图片描述

接着自定义一个groupid和artifactid

在这里插入图片描述

点击下一步就创建完成

在这里插入图片描述

此时的项目结构为下图:

在这里插入图片描述

  1. Maven依赖文件

    配置pom.xml:

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    
    <!--https://mvnrepository.com/artifact/org. springframework/spring-webmvc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.0.RELEASE</version>
    </dependency>
    
  2. 编写实现类

    package com.zhang.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 + '\'' +
                    '}';
        }
    }
    
    
  3. 配置beans.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
            <!--使用Spring来创建对象,在Spring这些都称为Bean
            bean=对象  new Hello()
            类型  变量名 = new 类型()
            id = 变量名
            class = new 的对象
            property  相当于给对象属性设值
            -->
            <bean id="hello" class="com.zhang.pojo.Hello">
                <property name="str" value="Spring"/>
            </bean>
    
    
    </beans>
    

    此时的项目结构为下图:

在这里插入图片描述

  1. 测试

        public static void main(String[] args) {
            //获取Spring的上下文对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            //我们的对象现在都在Spring中管理,要使用,直接取出来
    
            Hello hello = (Hello) context.getBean("hello");
    
            System.out.println(hello);
        }
    

思考问题?

●Hello对象是谁创建的?
hello对象是由Spring创建的
●Hello 对象的属性是怎么设置的?
hello对象的属性是由Spring容器设置的,

这个过程就叫控制反转:
**控制:**谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创
建的.

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

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

**I0C是一种编程思想,**由主动的编程变成被动的接收.可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

OK ,到了现在,我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的
IoC,-句话搞定:对象由Spring来创建,管理, 装配!

5.IOC创建对象的方式

在获取容器的时候就已经将配置文件beans.xml中的bean注册对象创建完毕

 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
  1. 使用无参构造创建对象,默认实现

  2. 假设我们要是有有参构造实现

    1. 下标赋值

          <!--有参构造方法
              1.下标赋值
          -->
          <bean id="user" class="com.zhang.pojo.User">
              <constructor-arg index="0" value="这是一个姓名"/>
          </bean>
      
    2. 参数类型

          <!--2.通过参数类型创建有参构造
          不建议使用
          -->
          <bean id="user" class="com.zhang.pojo.User">
              <constructor-arg type="java.lang.String" value="这是一个名字"/>
          </bean>
      
    3. 参数名类型(掌握)

          <!--3.通过参数名创建有参构造-->
          <bean id="user" class="com.zhang.pojo.User">
              <constructor-arg name="name" value="ahah"/>
          </bean>
      

6.Spring配置

6.1别名

   <!--别名,如果添加了别名,我们可以使用别名获取这个对象
        注意:原id仍然有效
    -->
    <alias name="user" alias="user2"/>

6.2Bean的配置

    <!--
    id : bean 的唯一标识,相当于对象名
    class : bean 对象所对应的全限定名 : 包名 + 类名
    name : 也是别名,而且name 可以同时取多个别名
    -->
    <bean id="user1" class="com.zhang.pojo.User" name="user3,u1">
        <constructor-arg name="name" value="sss"/>
    </bean>

6.3import

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

假设现在项目有多个人开发,这三个人负责不同的类开发。不同的类需要注册在不同的beans中。我们可以用import将所有人的beans.xml合并为一个总的。

使用时直接使用总的配置!

applicationContext.xml

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

    <import resource="beans.xml"/>
    <import resource="beans1.xml"/>

</beans>

此时的配置文件如下如:
在这里插入图片描述

7.DI依赖注入

7.1构造器注入

前面p5已经说了

###7.2Set方式注入【重点】

  • 依赖注入:Set注入!

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

    【环境搭建】

    1.复杂类型

public class Address {
private String address;

  public String getAddress() {
      return address;
  }

  public void setAddress(String address) {
      this.address = address;
  }

}




2.真实测试对象

```java
public class Student {

    private String name;

    private Address address;

    private String[] book;

    private List<String> hobbys;

    private Map<String,String> card;

    private Set<String> games;

    private String wife;

    private Properties info;
}

3.applicationContext.xml

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


    <bean id="student" class="com.zhang.pojo.Student">
        <!--方式一:普通值注入,value-->
        <property name="name" value="晶莹"/>
    </bean>


</beans>

4.测试类

public class MyTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

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

        System.out.println(student);
    }
}

完善注入信息

   <bean id="address" class="com.zhang.pojo.Address">
        <property name="address" value="武汉"/>
    </bean>
    <bean id="student" class="com.zhang.pojo.Student">
        <!--方式一:普通值注入,value-->
        <property name="name" value="晶莹"/>
        <!-- 第二种: Bean注入,value -->
        <property name="address" ref="address"/>
        <!-- 数组注入 ref -->
        <property name="book">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
        <!-- list注入 ref -->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>编程</value>
                <value>看剧</value>
            </list>
        </property>
        <!-- Map注入 ref -->
        <property name="card">
            <map>
                <entry key="身份征" value="123456789012345678"/>
                <entry key="银行卡" value="123456789012345678"/>
            </map>
        </property>
        <!-- Set注入 -->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CF</value>
            </set>
        </property>
        <!-- null值注入 -->
        <property name="wife">
            <null></null>
        </property>
        <!-- Properties注入 -->
        <property name="info">
            <props>
                <prop key="driver">mysql</prop>
                <prop key="url">http:/sss</prop>
                <prop key="username">root</prop>
                <prop key="password">root</prop>
            </props>
        </property>
    </bean>

7.3拓展方式注入

我们可以使用p命名空间,或者是c命名空间进行注入

官方解释如下图:

在这里插入图片描述

在这里插入图片描述

使用c或者p命名空间,需要先导两个标签

p和c的命名空间标签约束

      xmlns:p="http://www.springframework.org/schema/p"
       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:p="http://www.springframework.org/schema/p"
       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">

    <!-- p命名空间注入 可以直接注入属性的值 -->
    <bean id="user" class="com.zhang.pojo.User" p:name="哈哈" p:age="16"/>

    <!-- c命名空间注入, 可以通过构造器注入 construct-args-->
    <bean id="user2" class="com.zhang.pojo.User" c:age="18" c:name="弟弟"/>

</beans>

测试:

    @Test
    public void test2(){
        ApplicationContext context = new  ClassPathXmlApplicationContext("userbeans.xml");

        User user = context.getBean("user2", User.class);

        System.out.println(user);
    }

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

7.4bean的作用域

官方原文:
在这里插入图片描述

翻译后:

在这里插入图片描述

1.单例模式(spring默认机制)

每次从容器中取出bean,都是同一个对象

 <bean id="user2" class="com.zhang.pojo.User" c:age="18" c:name="弟弟" scope="singleton"/>

2.原型模式

每次从容器中取出bean,都是一个新的对象

    <bean id="user2" class="com.zhang.pojo.User" c:age="18" c:name="弟弟" scope="prototype"/>

3.其余request,session,application,只能在web开发中使用!

8.bean的自动装配

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

在Spring中有三种自动装配的方式

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

8.1测试

ByName自动装配

    <!--
     byName : 会自动在容器上下文中查找,有没有和id对应的set方法
     -->
    <bean id="people" class="com.zhang.pojo.People" autowire="byName">
        <property name="name" value="大侠"/>
    </bean>

ByType自动装配

    <bean id="cat" class="com.zhang.pojo.Cat"/>
    <bean id="dog11" class="com.zhang.pojo.Dog"/>
    <!--
     byName : 会自动在容器上下文中查找,有没有和id对应方法的bean
     byType : 会自动在容器上下文中查找,有没有和class类型对应的bean
     -->
    <bean id="people" class="com.zhang.pojo.People" autowire="byType">
        <property name="name" value="大侠"/>
    </bean>

小结:byname的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法一致!

bytype的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性类型一致!

8.2使用注解自动装配

jdk1.5支持注解,Spring2.5就支持注解!

要使用注解须知:

  1. 导入约束:

xmlns:context=“http://www.springframework.org/schema/context”

  1. 配置注解的支持:

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

@Autowried

直接在属性上使用即可!也可以在set方式上使用!

使用Autowried我们可以不用编写set方法,前提是你这个自动装配的属性在IOC容器中存在且符合名字byname!

科普:

@Nullable  字段标记了这个注解,说明这个字段可以为null

测试代码:

    //如果显示定义了Autowried的required属性为false,说明这个对象可以为null,否则不允许为空
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;

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

    @Autowired
    @Qualifier(value = "cat22")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog22")
    private Dog dog;
    private String name;

    <bean id="cat" class="com.zhang.pojo.Cat"/>
    <bean id="cat22" class="com.zhang.pojo.Cat"/>
    <bean id="dog" class="com.zhang.pojo.Dog"/>
    <bean id="dog22" class="com.zhang.pojo.Dog"/>
    <bean id="people" class="com.zhang.pojo.People"/>

@Resource注解

public class People {

    @Resource(name = "cat22")
    private Cat cat;
    @Resource
    private Dog dog;

小结:

@Autowried和**@Resource注解**的区别:

  • 都是自动装配使用

  • @Autowried通过bytype方式实现【常用】

  • @Resource默认通过byname方式,如果找不到就通过bytype,如果都找不到,就报错!【常用】

9.使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop包导入!
在idea右上角查看自己的maven仓库,看看依赖是否导入!
在这里插入图片描述
使用注解需要导入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:annotation-config/>

</beans>

1.bean

2.属性如何注入

//等价于 <bean id="user" class="com.zhang.pojo.User"/>
//@Component  组件 装配的id为类名小写
@Component
public class User {

    //相当于<property name = "name" value="哈哈哈哈"/>
    @Value("哈哈哈哈")
    public String name;
}

//等价于 <bean id="user" class="com.zhang.pojo.User"/>
//@Component  组件
@Component
public class User {

    //相当于<property name = "name" value="哈哈哈哈"/>
//    @Value("哈哈哈哈")
    public String name;
    
    //相当于<property name = "name" value="哈哈哈哈"/>
    @Value("哈哈哈哈")
    public void setName(String name) {
        this.name = name;
    }
}

​ 测试:

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user.name);
    }

3.衍生的注解

@Component有几个衍生注解(下面三个注解功能和@Component一样),在web开发中,会按照mvc三层架构分层。

dao【@Repository】

service【@Service】

controller【@Controller】

@Component,@Repository,@Service,@Controller这四个注解功能一样,都是将某个类注册到spring,装配到bean中

4.自动装配

-@Autowired :自动装配,通过类型,名字
    如果Autowired不能唯一自动装配,我们可以使用@Qualifier(value = "xxx")p配合@Autowired使用,指定唯一的bean注入!
-@Resource : 自动装配,通过名字,类型
-@Nullable 字段标记了这个注解,说明这个字段可以为null

5.作用域

单例

@Component
@Scope("singleton")//单例
public class User {

}

6.小结

xml与注解:

​ xml更万能,使用任何场合

​ 注解不是自己的类使用不了,维护相对复杂!

xml与注解的最佳实践:

​ xml用来管理bean

​ 注解用来注入属性值

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

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

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

</beans>

10.使用java的方式配置Spring

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

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

在这里插入图片描述

配置类

package com.zhang.config;

import com.zhang.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;

//这是一个配置类,和beans.xml一样
//他会被注册到spring容器中,他本来也是一个Component
@Configuration
@ComponentScan("com.zhang.pojo")
@Import(ZhangConfig2.class)
public class ZhangConfig {

    //注册一个bean,就相当于之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id
    @Bean
    public User user(){
        return new User();//返回要注入的对象
    }
}

实体类

package com.zhang.pojo;

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

//注解,说明这个类被spring注册到容器中
@Component
public class User {

    private String name;

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

    public String getName() {
        return name;
    }

    @Value("王老五")
    public void setName(String name) {
        this.name = name;
    }
}

测试类

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类方式,我们就只能通过AnnotationConfigApplicationContext上下文来获取容器,通过class类加载
        ApplicationContext context = new AnnotationConfigApplicationContext(ZhangConfig.class);

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

        System.out.println(user);

    }
}

这种纯java的配置方式,在SpringBoot中随处可见!

11.代理模式

为什么要学代理模式?

因为这就是springAOP的底层!【SpringAOP】【SpringMVC】

代理模式分类

  • 静态代理
  • 动态代理

在这里插入图片描述

11.1静态代理

角色分析:

  • 抽象角色:一般会使用接口或着抽象类实现
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理后一般会做一些附属操作
  • 客户:访问代理对象的人

代码步骤:

1.接口


//租房
public interface Rent {

    public void rent();
}

2.真实角色

//房东
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房!");
    }
}

3.代理角色

public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

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


    @Override
    public void rent() {
        setHouse();
        host.rent();
        hetong();
        fare();
    }

    public void setHouse(){
        System.out.println("中介带你看房!");
    }

    public void fare(){
        System.out.println("收中介费!");
    }
    public void hetong(){
        System.out.println("签租赁合同");
    }
}

4.客户端访问代理角色

ic class Cilent {
    public static void main(String[] args) {
        Host host=new Host();
        host.rent();
        //代理,代理一半会有一些附属操作
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }

代理模式好处:

  • 可以是真实角色的操作更加存粹。不再关注公共的业务
  • 公共业务就交给了代理,实现业务分工
  • 公共业务发生扩展的时候。方便集中管理!

缺点:

一个真实角色产生一个代理角色,类爆炸!

聊聊aop:面向切面编程

在这里插入图片描述

11.2动态代理

动态代理和静态代理角色一样

动态代理的类是动态生成的,不是我们直接写好的

动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口:JDK动态代理【我们在这里使用这个】
  • 基于类:cglib
  • java字节码实现:javasist

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

动态代理的模板:

package com.zhang.demo04;

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

//我们用这个类生成代理
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 {
        //动态代理的本质,就是使用反射
        Object result = method.invoke(target, args);
        return result;
    }
}

测试:

public class Client {

    public static void main(String[] args) {
        //真实角色
        UserServiceImpl service = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        //设置代理对象
        pih.setTarget(service);
        //动态生成获取代理角色
        UserService proxy = (UserService) pih.getProxy();

        proxy.add();
    }

}

动态代理好处:

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

12.AOP

12.1什么是AOP

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

在这里插入图片描述

12.2Aop在spring中的作用

提供声明式事务;允许用户自定义切面
●横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…

●切面(ASPECT) :横切关注点被模块化的特殊对象。即,它是一一个类。

●通知(Advice) :切面必须要完成的工作。即,它是类中的一一个方法。
●目标(Target) :被通知对象。

●代理(Proxy) :向目标对象应用通知之后创建的对象。

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

●连接点UointPoint) :与切入点匹配的执行点。

在这里插入图片描述
在这里插入图片描述

12.3使用Spring实现Aop

【重点】使用AOP织入,需要导入一个包到pom.xml中

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

方式一:使用spring的api接口

配置:

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

    <bean id="userservice" class="com.zhang.service.UserServiceImpl"/>
    <bean id="log1" class="com.zhang.log.AfterLog"/>
    <bean id="log" class="com.zhang.log.Log"/>

    <!--使用原生的spring api接口-->
    <!-- 配置aop 需要导入约束-->
    <aop:config>
        <!-- 需要切入点 :expression表达式  execution要执行的位置-->
        <aop:pointcut id="pointcut" expression="execution(* com.zhang.service.UserServiceImpl.*(..))"/>
        <!--执行环绕增强-->
        <aop:advisor advice-ref="log1" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试类

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理的是接口
        UserService userservice = (UserService) context.getBean("userservice");

        userservice.add();

    }

方法二:使用自定义类

public class DiyPointCut {

    public void before(){
        System.out.println("--------方法执行前-------");
    }

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

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

方式三:使用注解实现

配置

    <!--方式三:使用注解-->
    <bean id="annotationPointcut" class="com.zhang.diy.AnnotationPointcut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

java类

//使用注解方式实现aop
@Aspect//标注切面类
public class AnnotationPointcut {
    @Before("execution(* com.zhang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前-----------------");
    }

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

    /**
     * 在环绕增强中,我们可以给定一个参数,代表我们要获取处理的切入点
     */
    @Around("execution(* com.zhang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        Object proceed = jp.proceed();

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

13.整合Mybatis

步骤:

1.导入相关jar包

  • junit
  • mybatis
  • mysql数据库
  • spring相关
  • aop织入
  • mybatis-spring【新知识】

pom.xml依赖:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--连接spring操作数据库,还要一个spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--aop织入-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!--mybatis-spring整合包-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
    </dependencies>

2.编写配置文件

3.测试

13.1回忆mybatis

1.编写实体类

2.编写核心配置文件

3.编写接口

4.编写Mapper.xml

5.测试

13.2Mybatis-spring

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTemplate

4.需要给接口加实现类

5.将自己写的实现类,注册到spring中

6.测试使用

结构:

spring-dao:

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

        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>

        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/zhang/mapper/*.xml"/>
        </bean>

        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </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>

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <typeAliases>
        <package name="com.zhang.pojo"/>
    </typeAliases>

</configuration>

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

    <import resource="spring-dao.xml"/>

    <bean id="userMapper" class="com.zhang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

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

        <!--DateSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
        我们这里使用spring提供的JDBC:springframework.jdbc.datasource
        -->

        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>

<!--        sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--绑定mybtais配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <!--注册mapper-->
            <property name="mapperLocations" value="classpath:com/zhang/mapper/*.xml"/>
        </bean>

        <!--SqlSessionTemplate就是我们使用的sqlsession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--只能使用构造器注入sqlsessionFactory-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>


        <bean id="userMapper" class="com.zhang.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>

</beans>

注意:

使用此配置时,需要删除中文注释!!

14.声明式事务

1.回顾事务

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

事物的ACID原则:

  • 原子性

  • 一致性

  • 隔离性

    多个业务可能操作同一个资源,防止数据损坏。

  • 持久性

    事务一旦提交我,无论系统发生什么问题,结果都不会被影响,被持久化到存储器中!

2.spring的事务管理

  • 声明式事务 : AOP

    配置声明式事务:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/tx
            https://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/zhang/mapper/*.xml"/>
        </bean>
    
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <!--配置声明式事务-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!--结合aop实现事务的织入-->
        <!--配置事务通知-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!--给那些方法配置事务-->
            <!--配置事务的传播特性: new 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.zhang.mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
        </aop:config>
    </beans>
    
  • 编程式事务 :需要在代码中进行事务的管理

思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况!
  • 如果我们不在spring中去声明事务,我们需要在代码中手动配置
  • 事务再项目开发中十分重要,保证了数据的一致性和完整性!

rg.mybatis.spring.SqlSessionFactoryBean">



  <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
      <constructor-arg index="0" ref="sqlSessionFactory"/>
  </bean>

  <!--配置声明式事务-->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
  </bean>

  <!--结合aop实现事务的织入-->
  <!--配置事务通知-->
  <tx:advice id="txAdvice" transaction-manager="transactionManager">
      <!--给那些方法配置事务-->
      <!--配置事务的传播特性: new 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.zhang.mapper.*.*(..))"/>
      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
  </aop:config>
```
  • 编程式事务 :需要在代码中进行事务的管理

思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况!
  • 如果我们不在spring中去声明事务,我们需要在代码中手动配置
  • 事务再项目开发中十分重要,保证了数据的一致性和完整性!

总结:

这是博主学习spring的大致学习流程,如需学习过程的源码,可私信博主!另附上学习的资源视频地址:lhttps://www.bilibili.com/video/BV1WE411d7Dv.
如果整理过程中博主有理解不对的地方,欢迎各位小伙伴留言指正,博主一定虚心接受并改正!
如果觉得博主的学习笔记对你有所帮助,可以给博主点一个赞👍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章