您的位置:首页 > 编程语言 > Java开发

Spring5系列完整学习笔记以及相关代码(含Spring基本配置,IoC,AOP思想,依赖注入,注解开发Spring,静态/动态代理模式,MyBatis整合以及相关代码案例)

2020-06-04 05:16 896 查看

本篇学习笔记总结自bilibiliup主【狂神说Java】系列视频:【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
视频作者公众号:狂神说

1、简介

1.1、历史与maven配置

  • 2002,首次推出了Spring的雏形:interface 21
  • Rod Johnson, Spring Framework创始人
  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
  • SSH:struct2 + Spring + Hibernate
  • SSM:SpringMVC + Spring + Mybatis

官网文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core

官方下载地址:https://repo.spring.io/release/org/springframework/spring

Github:https://github.com/spring-projects/spring-framework

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

1.2、优点

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

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

1.3、组成

1.4、拓展

现代化的Java开发,就是基于Spring的开发

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

    大多数公司都基于SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC。

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

    2、IoC控制反转

    2.1、理论推导

    原来的项目构建流程:

    1. UserDao 接口

      package com.neil.dao;
      public interface UserDao {
      void getUser();
      }
    2. UserDaoImpl

      package com.neil.dao;
      public class UserDaoImpl implements UserDao {
      public void getUser() {
      System.out.println("默认获取用户的数据");
      }
      }
      package com.neil.dao;
      public class UserDaoMySQLImpl implements UserDao {
      @Override
      public void getUser() {
      System.out.println("MySQL获取数据");
      }
      }
      package com.neil.dao;
      public class UserDaoOracleImpl implements UserDao {
      @Override
      public void getUser() {
      System.out.println("Oracle获取用户数据");
      }
      }
    3. UserService 业务接口

      package com.neil.service;
      public interface UserService {
      void getUser();
      }
    4. UserServiceImpl 业务实现类

      package com.neil.service;
      import com.neil.dao.UserDao;
      import com.neil.dao.UserDaoImpl;
      import com.neil.dao.UserDaoMySQLImpl;
      import com.neil.dao.UserDaoOracleImpl;
      
      public class UserServiceImpl implements UserService {
      //创建哪一种UserDao的实现类,在代码中写死了,如果需要变更则需要改变源代码
      private UserDao userDao = new UserDaoMySQLImpl();
      public void getUser() {
      userDao.getUser();
      }
      }

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

    我们使用一个Set接口, 发生了革命性的变化!

    public class UserServiceImpl implements UserService {
    private UserDao userDao;
    //利用set进行动态实现值的注入
    public void setUserDao(UserDao userDao){
    this.userDao = userDao;
    }
    public void getUser() {
    userDao.getUser();
    }
    }
    • 之前程序是主动创建对象!控制权在程序员手上,用户的每一个需求都要程序员修改源代码
    • 使用了set注入后,程序不再具有主动性,而是变成了被动接收对象,外部只要传入 一个任意的UserDao的实现类,就可以创建出对应的对象
    • 原来的是程序控制,变成了客户控制
    • 这种思想从本质上解决了问题,程序员不用主动地去管理对象的创建了。系统的耦合性大大降低,可以更加专注在业务的实现上。
    • 这是IoC的原型

    2.2、IoC本质

    • 之前的设计思想

    • 如今的设计思想:

    • 控制反转(inversion of control)是一种设计思想,DI(dependency injection)依赖注入是一种实现IoC的方式。

    • 在没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖完全硬编码在程序中,对象的创建由自己控制,控制反转后将对象的创建转移到了第三方

    • IoC是Spring框架的核心内容,使用多种方式完美实现了IoC,可以使用XML配置,也可以使用注解。新版本的Spring也可以零配置实现IoC

    • Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器,程序使用时再从IoC容器中取出所需要的对象

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

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

    2.3、HelloSpring

    pojo

    package com.neil.pojo;
    
    public class Hello {
    private String name;
    
    @Override
    public String toString() {
    return "Hello{" +
    "name='" + name + '\'' +
    '}';
    }
    
    public String getName() {
    return name;
    }
    
    public void setName(String name) {
    this.name = name;
    }
    }

    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
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--使用Spring创建对象,在Spring中这些都称为Bean-->
    <!--bean = 对象
    Hello hello = new Hello();
    
    id= 变量名
    class= 要new的对象
    property 相当于给对象中的属性设置一个值
    -->
    <bean id="hello" class="com.neil.pojo.Hello">
    <property name="name" value="String"/>
    </bean>
    
    </beans>

    Test

    import com.neil.pojo.Hello;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
    @Test
    public void ApplicationContextTest(){
    //获取Spring的上下文对象
    ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    //我们的对象现在都在Spring中管理,我么要使用,直接从里面取出来(参数为id)
    
    Hello hello =(Hello) context.getBean("hello");
    System.out.println(hello.toString());
    
    }
    }

    这个过程就要控制反转:

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

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

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

    2.4、IoC创建对象的方式

    1. 使用无参构造方法创建对象(默认实现)

    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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <bean id = "user" class="com.neil.pojo.User">
      <constructor-arg index="0" value="neill"/>
      </bean>
      </beans>
    3. 类型赋值(不建议使用,可能有多个属性有相同类型)

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <bean id = "user" class="com.neil.pojo.User">
      <constructor-arg type="java.lang.String" value="neill"/>
      </bean>
      </beans>
    4. 直接通过参数名来命名

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <bean id = "user" class="com.neil.pojo.User">
      <constructor-arg name="name" value="neil"/>
      </bean></beans>

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

    3、Spring配置

    3.1、别名

    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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id = "user" class="com.neil.pojo.User">
    <constructor-arg name="name" value="neil"/>
    </bean>
    <alias name="user" alias="user2"/>
    </beans>

    Test:

    import com.neil.pojo.User;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
    @Test
    public void Test(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    //使用别名获取对象
    User user = (User) applicationContext.getBean("user2");
    user.show();
    }
    }

    3.2、Bean的配置

    <bean id="userN" class="com.neil.pojo.UserN" name="userN2,userN3;userN4 userN5">
    <property name="name" value="Jackson"/>
    </bean>
    • id:bean的唯一标识符,也就是我们学的对象名
    • class:bean的全限定名字,包名加类型
    • name:也是别名,也可以同时取多个别名进行分割

    3.3、import

    一般用于团队开发使用,可以将多个配置文件合并成一个

    1. 在resource目录下创建bean1.xml
    2. ApplicationContext.xml
      中import
    <import resource="bean1.xml"/>

    4、依赖注入

    4.1、构造器注入

    <bean id = "user" class="com.neil.pojo.User">
    <constructor-arg name="name" value="neil"/>
    </bean>

    4.1、Set方法注入

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

    【环境搭建】

    1. com.neil.pojo
      包下创建两个类

      package com.neil.pojo;
      
      public class Address {
      private String address;
      
      public String getAddress() {
      return address;
      }
      
      public void setAddress(String address) {
      this.address = address;
      }
      }
      package com.neil.pojo;
      
      import java.util.*;
      //该类已经忽略toString以及getter/setter
      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 Properties info;
      private String wife;
      }
    2. 在resources下建立

      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 http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <bean id ="address" class="com.neil.pojo.Address">
      <property name="address" value="火星"/>
      </bean>
      
      <bean id="student" class="com.neil.pojo.Student">
      <!--普通值注入,直接使用value-->
      <property name="name" value="Neil"/>
      <!--Bean注入,使用ref-->
      <property name="address" ref="address"/>
      <!--数组注入-->
      <property name="books">
      <array value-type="java.lang.String">
      <value>红楼梦</value>
      <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="253235235123"/>
      <entry key="银行卡" value="123124365685"/>
      </map>
      </property>
      <!--set注入-->
      <property name="games">
      <set>
      <value>Dota2</value>
      <value>csgo</value>
      <value>部落冲突</value>
      </set>
      </property>
      <!--空值注入-->
      <property name="wife">
      <null/>
      </property>
      <!--properties注入-->
      <property name="info">
      <props>
      <!--key value形式,写在尖括号中间,和map不太一样-->
      <prop key="driver">jdbc.mysql.Driver</prop>
      <prop key="username">root</prop>
      <prop key="pwd">123456</prop>
      </props>
      </property>
      
      </bean>
      </beans>
    3. 测试类

      import com.neil.pojo.Student;
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class MyTest {
      @Test
      public void test(){
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
      Student student = (Student) applicationContext.getBean("student");
      System.out.println(student.getName());
      }
      }
    4. 打印结果

      Student{name='Neil',
      address=Address{address='null'},
      books=[红楼梦, 水浒传, 三国演义, 西游记],
      hobbies=[看电影, 撸猫, 写代码],
      card={身份证=253235235123, 银行卡=123124365685},
      games=[Dota2, csgo, 部落冲突],
      info={driver=jdbc.mysql.Driver, pwd=123456, username=root},
      wife='null'
      }

    4.3、P命名空间注入

    1. Pojo :已经忽略get/set方法

      package com.neil.pojo;
      
      public class User {
      private String userName;
      private int age;
      }
    2. ApplicationContext.xml
      :头文件需要加上p标签的引用

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!--p命名空间注入,可以直接注入属性的值:property-->
      <bean id="user" class="com.neil.pojo.User" p:age="29" p:userName="123"/>
      
      </beans>

    4.4、C命名空间注入

    1. Pojo需要添加有参构造方法

      public class User {
      private String userName;
      private int age;
      
      public User() {
      }
      
      public User(String userName, int age) {
      this.userName = userName;
      this.age = age;
      }
      }
    2. ApplicationContext.xml
      :头文件需要加上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 http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!--p命名空间注入,可以直接注入属性的值:property-->
      <bean id="user" class="com.neil.pojo.User" p:age="29" p:userName="123"/>
      <!--c命名空间注入,通过构造器注入:constructor-->
      <bean id="user2" class="com.neil.pojo.User" c:age="18" c:userName="Neil"/>
      </beans>

    5、Bean作用域

    Scope Description
    singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
    prototype Scopes a single bean definition to any number of object instances.
    request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring
    ApplicationContext
    .
    session Scopes a single bean definition to the lifecycle of an HTTP
    Session
    . Only valid in the context of a web-aware Spring
    ApplicationContext
    .
    application Scopes a single bean definition to the lifecycle of a
    ServletContext
    . Only valid in the context of a web-aware Spring
    ApplicationContext
    .
    websocket Scopes a single bean definition to the lifecycle of a
    WebSocket
    . Only valid in the context of a web-aware Spring
    ApplicationContext
    .

    5.1、单例作用域

    Only one shared instance of a singleton bean is managed, and all requests for beans with an ID or IDs that match that bean definition result in that one specific bean instance being returned by the Spring container.

    To put it another way, when you define a bean definition and it is scoped as a singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object. The following image shows how the singleton scope works:


    也可以显示增加作用域

    <!-- the following is equivalent, though redundant (singleton scope is the default) -->
    <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

    5.2、原型模式

    The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a

    getBean()
    method call on the container. As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

    The following diagram illustrates the Spring prototype scope:

    每次从容器中getBean的时候都会产生一个新的对象

    <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

    5.3、Request、Session、application

    只能在web开发中使用

    6、Bean的自动装配

    • 自动装配是Spring满足并且依赖的一种方式

    • Spring会在上下文中自动寻找并且自动给bean装配属性

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

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

    6.1、测试环境

    1. Pojo:

      public class Person {
      private Cat cat;
      private Dog dog;
      private String name;
      }
      public class Dog {
      public void shout(){
      System.out.println("wang~");
      }
      }
      public class Cat {
      public void shout(){
      System.out.println("miao~");
      }
      }
    2. 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 http://www.springframework.org/schema/beans/spring-beans.xsd">
      <bean id="cat" class="com.neil.pojo.Cat"/>
      <bean id="dog" class="com.neil.pojo.Dog"/>
      
      <bean id="person" class="com.neil.pojo.Person">
      <property name="name"  value="Neil"/>
      <property name="dog" ref="dog"/>
      <property name="cat" ref="cat"/>
      </bean>
      </beans>
    3. Test类

      import com.neil.pojo.Person;
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class MyTest {
      @Test
      public void Test(){
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
      Person person = applicationContext.getBean("person", Person.class);
      person.getDog().shout();
      person.getCat().shout();
      }
      }

    6.2、通过name自动装配

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="cat" class="com.neil.pojo.Cat"/>
    <bean id="dog" class="com.neil.pojo.Dog"/>
    <!--byName:会自动在容器上下文中查找,和自己对象set方法属性对应名称相同的的bean ID-->
    <bean id="person" class="com.neil.pojo.Person" autowire="byName">
    <property name="name"  value="Neil"/>
    </bean>
    </beans>

    6.3、通过Type自动装配

    <?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="cat" class="com.neil.pojo.Cat"/>
    <bean id="dog" class="com.neil.pojo.Dog"/>
    <!--byType: 会自动在容器上下文中查找,和自己对象属性类型对应的Bean-->】
    <!--需要保证这个类型全局唯一-->
    <bean id="person" class="com.neil.pojo.Person" autowire="byType">
    <property name="name"  value="Neil"/>
    </bean>
    </beans>

    6.4、小结

    • byName:需要保证所有Bean的Id唯一,会自动在容器上下文中查找,和自己对象set方法属性对应名称相同的的bean ID
    • byType:需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致

    6.5、使用注解开发

    Jdk1.5支持注解,spring2.5就支持注解了

    要使用注解须知:

    1. 导入约束:context约束, beans头文件

    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/>
      <!--指定扫描的包,这个包下面的注解就会生效-->
      <context:component-scan base-package="com.neil.pojo"/></beans>
    • @Autowired
      :直接在属性上使用即可!也可以在set方法上使用。使用Autowired我们可以不用编写Set方法,前提是自动装配的属性在IoC(Spring)容器中存在,且装配方式为byType

    • @Autowired
      :默认按照ByType进行实现,找不到就用ByName

    • @Autowired = (required = false)
      :表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。

    • @Qualifier(value="dog")
      :表示显示注册哪个Bean,如果自动装配环境复杂,需要配合自动装配注解一起使用

    • @Resource
      :是java的注解,用的比较少,效果等于以上两个的集合,可以使用
      @Resource(name = 'cat2')
      指定装配

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

    public class Person {
    @Autowired
    @Qualifier(value = "cat")
    private Cat cat;
    @Autowired = (required = false)
    private Dog dog;
    private String name;
    }

    7、使用注解开发

    在Spring4之后,要使用注解开发,必须要保证AOP的包已经导入

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

    7.1、Bean

    • @Component
      :等价于
      <bean id="user" class="com.neil.pojo.User"/>

      import org.springframework.stereotype.Component;
      //等价于  <bean id="user" class="com.neil.pojo.User"/>
      @Component
      public class User {
      public String userName = "Neil";
      }
    • @Value
      :在属性或者set上注入,与上面等同,也等同于
      <property name="userName" value="Neil"/>

      public class User {
      @Value("Neil")
      public String userName;
      }

    7.2、衍生的注解

    • @Component
      有几个衍射注解,我们在web开发中,会按照mvc三层架构分层,功能和
      @Component
      类似

      Dao:使用
      @Repository
      注解
    • Service:使用
      @Service
      注解
    • controller:使用
      @Controller
      注解

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

    7.3、自动装配

    • @Autowired
    • @Resource
    • @Nullable

    7.4、作用域

    • @Scope
      : 可以设置在类上,设置value值,包括Singleton,prototype等

    7.5、小结

    xml与注解:

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

    xml与注解最佳实践:

    • xml用来管理Bean
    • 注解只用来负责管理属性的注入
    • 我们在使用过程中,只需要注意一个问题:如果必须让注解生效,就需要开启注解的支持
    <!--开启注解的支持-->
    <context:annotation-config/>
    <!--指定扫描的包,这个包下面的注解就会生效-->
    <context:component-scan base-package="com.neil.pojo"/>

    8、使用Java的方式配置Spring

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

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

    1. 配置config类:

      @Configuration
      本来是一个
      @Conponent
      组件,代表这是一个配置类,和
      ApplicationContext.xml
      一样

      import com.neil.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
      @ComponentScan("com.neil.pojo")  //开启扫描包
      @Import(MyConfig2.class) //引入其他配置类
      public class MyConfig {
      
      /*注册的Bean,相当于我们之前写的Bean
      这个方法的名字,相当于原来Bean标签的Id属性
      这个方法的返回值,相当于bean标签中的class属性*/
      @Bean
      public User getUser(){
      return new User();
      }
      }
    2. pojo

      package com.neil.pojo;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.stereotype.Component;
      
      public class User {
      private String userName;
      
      @Override
      public String toString() {
      return "User{" +
      "userName='" + userName + '\'' +
      '}';
      }
      
      public String getUserName() {
      return userName;
      }
      
      @Value("Neil")
      public void setUserName(String userName) {
      this.userName = userName;
      }
      }
    3. 使用

      AnnotationConfigApplicationContext
      获取配置类

      public class MyTest {
      @Test
      public void JavaConfigTest(){
      //使用Java类获取上下文
      ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
      User user = applicationCon
      4000
      text.getBean("getUser", User.class);
      System.out.println(user.getUserName());
      }
      }

    9、代理模式

    学什么要学习代理模式?因为这是SpringAOP的底层!【SpringAOP和SpringMVC】

    代理模式原型:

    9.1、静态代理

    角色分析:

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

    代码实现:

    • 接口:

      public interface Rent{
      public void rent();
      }
    • 真实角色:

      public class Landlord implements Rent(){
      public void rent(){
      System.out.Println("我是房东我有个房子需要出租")
      }
      }
    • 代理角色

      public class Proxy implements Rent(){
      private Landlord landlord;
      
      public Proxy(){
      }
      
      public Proxy(Landlord landlord){
      this.landlord = landlord;
      }
      
      public void rent(){
      inspection();
      landlord.rent();
      signContract();
      getFee();
      }
      
      public void insepection(){
      System.out.println("中介带你看房子");
      }
      
      public void signContract(){
      System.out.println("和中介签合同了");
      }
      
      public void getFee(){
      System.out.println("收中介费");
      }
      }
    • 客户端访问代理角色

      public class Client(){
      public static void main(String[] args){
      Landlord landlord = new Landlord();
      //房东注入到中介中
      Proxy proxy = new Proxy(landlord);
      //你不用面对房东,直接找中介租房即可
      proxy.rent();
      }
      }

    代理模式的好处:

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

    缺点:

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

    9.2、AOP的实现机制

    9.3、动态代理

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

    需要了解两个类:

    • Proxy:调用其静态方法
      newProxyInstance()
      生成实例
    • InvocationHandler:调用处理程序

    InvocationHandler

    java.lang.reflect
    下的一个接口

    1. 动态代理类:

      public class ProxyInvocationHandler implements InvocationHandler{
      //被代理的接口
      private Object target;
      
      public void setTarget(Object target){
      this.target = target;
      }
      
      //生成得到代理类
      public Object getProxy(){
      //需要传入类加载器,代理的接口和InvocationHandler
      return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
      }
      
      //处理代理实例,并返回结果
      public Object invoke(Object proxy, Method method,Object[] args) throw Throwable{
      log(method.getName());
      Object result = method.invoke(target, args);
      return result;
      }
      
      public void log(String msg){
      System.out.Println("执行了" + msg + "方法");
      }
      }
    2. 实现类:

      public class Client{
      public static void main(String[] args){
      //真实角色
      UserServiceImpl userService = new UserServiceImpl();
      //代理角色,不存在
      ProxyInvocationHandler pih = new ProxyInvocationHandler();
      //设置要代理的对象
      pih.setTarget(userService);
      //动态生成代理类
      UserService proxy = (UserSErvice)pih.getProxy();
      proxy.query();
      }
      }

    动态代理的好处:

    • 一个动态代理类代理的是一个接口,一般对应的就是一类业务
    • 一个动态代理类可以多个类,子要是实现了同一个接口

    10、AOP

    10.1、什么是AOP

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

    10.2、Aop在Spring中的作用

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

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

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

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

    10.3、使用Spring实现Aop

    环境配置:

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

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

      public class UserServiceImpl implements UserService {
      @Override
      public void add() {
      System.out.println("inserted one user");
      }
      @Override
      public void delete() {
      System.out.println("delete one user");
      }
      @Override
      public void query() {
      System.out.println("select one user");
      }
      @Override
      public void update() {
      System.out.println("update one user");
      }
      }
    3. 继承某种通知类,重写方法

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

    10.3.1、使用Spring接口【主要是Spring接口实现】

    ApplicationContext.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册Bean-->
    <bean id="userService" class="com.neil.service.UserServiceImpl"/>
    <bean id="log" class="com.neil.log.Log"/>
    <bean id="afterLog" class="com.neil.log.AfterLog"/>
    <!--方式一:使用原声Spring API接口-->
    <!--配置AOP:需要在头文件导入aop的约束-->
    <aop:config>
    <!--切入点 execution(要执行的位置)-->
    <aop:pointcut id="pointcut" expression="execution(* com.neil.service.UserServiceImpl.*(..))"/>
    <!--执行环绕增加-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
    </beans>


    execution 完整参数:(修饰符 返回值 包名.类名/接口名.方法名(参数列表))

    (…)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型.

    10.3.2、使用自定义类实现AOP【主要是切面定义】

    1. 自定义类

      public class DiyPointCut {
      public void diyBefore(){
      System.out.println("自定义方法执行前");
      }
      public void diyAfter(){
      System.out.println("自定义方法执行后");
      }
      }
    2. ApplicationContext.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">
      <!--注册Bean-->
      <bean id="userService" class="com.neil.service.UserServiceImpl"/>
      <bean id="log" class="com.neil.log.Log"/>
      <bean id="afterLog" class="com.neil.log.AfterLog"/>
      
      <!--方式二:自定义类,注册Bean-->
      <bean id="diyPointCut" class="com.neil.diy.DiyPointCut"/>
      <aop:config>
      <!--自定义切面,引用自定义类-->
      <aop:aspect ref="diyPointCut">
      <!--切入点-->
      <aop:pointcut id="pointCut" expression="execution(* com.neil.service.UserServiceImpl.*(..))"/>
      <!--通知-->
      <aop:before method="diyBefore" pointcut-ref="pointCut"/>
      <aop:after method="diyAfter" pointcut-ref="pointCut"/>
      </aop:aspect>
      </aop:config>
      </beans>

    10.3.3、使用注解实现

    1. 新建一个注解增强类

      //使用方式三:使用注解方式实现AOP
      @Aspect  //标注这个类是一个切面
      public class AnnotationCut {
      @Before("execution(* com.neil.service.UserServiceImpl.*(..))")
      public void annotationBefore(){
      System.out.println("Annotation方法执行前");
      }
      @After("execution(* com.neil.service.UserServiceImpl.*(..))")
      public void annotationAfter(){
      System.out.println("Annotation方法执行后");
      }
      //在环绕增强中,我们可以给定一个参数,到表我们要获取切入的点
      @Around("execution(* com.neil.service.UserServiceImpl.*(..))")
      public void annotationAround(ProceedingJoinPoint joinPoint) throws Throwable {
      System.out.println("Annotation环绕前");
      System.out.println("获得签名" + joinPoint.getSignature());
      //执行方法
      Object proceed = joinPoint.proceed();
      System.out.println("Annotation环绕后");
      }
      }
    2. ApplicationContext.xml注册Bean并且开始支持

      <!--方式三:注册Bean-->
      <bean id="annotationCut" class="com.neil.Annotation.AnnotationCut"/>
      <!--开启注解支持 JDK(默认proxy-target-class="false") cglib(proxy-target-class="true")-->
      <aop:aspectj-autoproxy />

    11、整合MyBatis

    11.1、环境配置

    步骤:

    1. 导入相关jar包

        Junit
      • MyBatis
      • MySQL数据库
      • Spring相关
      • AOP织入
      • MyBatis-spring【新包】
      <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.9.4</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. 测试

    11.2、回忆MyBatis

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

    11.3、MyBatis-spring

    11.3.1、什么是MyBatis-Spring

    MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和

    SqlSession
    并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的
    DataAccessException
    。最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。

    11.3.2、环境配置

    1. ApplicationContext.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd">
      
      <!--DataSource:使用Spring的数据源替换MyBatis的配置 c3p0 dbcp druid-->
      <!--这里使用Spring提供的jdbc-->
      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
      <property name="username" value="root"/>
      <property name="password" value="123456"/>
      </bean>
      
      <!--sqlSessionFactory 可以替换所有的mybatis-config.xml里面的配置-->
      <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="UserMapper.xml"/>
      </bean>
      
      <!--SqlSessionTemplate:就是我们使用的sqlSession-->
      <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
      <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
      <constructor-arg index="0" ref="sqlSessionFactory"/>
      </bean>
      
      <bean id="userMapperImpl" class="com.neil.mapper.UserMapperImpl">
      <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
      </bean>
      
      <bean id="userMapperImpl2" class="com.neil.mapper.UserMapperImpl2">
      <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
      </bean>
      </beans>
    2. UserMapper

      package com.neil.mapper;
      
      import com.neil.pojo.User;
      
      import java.util.List;
      
      public interface UserMapper {
      public List<User> selectUser();
      }
    3. UserMapperImpl

      package com.neil.mapper;
      
      import com.neil.pojo.User;
      import org.mybatis.spring.SqlSessionTemplate;
      
      import java.util.List;
      
      public class UserMapperImpl implements UserMapper {
      //我们的所有操作与拿来都使用sqlSession
      //现在我们都使用sqlSessionTemplete
      private SqlSessionTemplate sqlSessionTemplate;
      //使用构造器方法注入
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
      this.sqlSessionTemplate = sqlSessionTemplate;
      }
      
      @Override
      public List<User> selectUser() {
      UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
      return mapper.selectUser();
      }
      }
    4. UserMapper.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <!--namespace:绑定一个对应的Dao(Mapper)接口-->
      <mapper namespace="com.neil.mapper.UserMapper">
      <!--id对应对应Mapper接口的方法名字-->
      <select id="selectUser" resultType="com.neil.pojo.User" resultMap="UserMap">
      select * from mybatis.user
      </select>
      
      <resultMap id="UserMap" type="User">
      <!--column表示字段,property表示实体类属性-->
      <result column="user_id" property="userId"/>
      <result column="user_name" property="userName"/>
      <result column="user_pwd" property="userPwd"/>
      </resultMap>
      </mapper>
    5. 测试

      import com.neil.mapper.UserMapper;
      import com.neil.pojo.User;
      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      import java.io.IOException;
      import java.io.InputStream;
      import java.util.List;
      
      public class MyTest {
      
      //原来的MyBatis链接数据库方法
      @Test
      public void Test() throws IOException {
      String resources = "mybatis-config.xml";
      InputStream resourceAsStream = Resources.getResourceAsStream(resources);
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = sqlSessionFactory.openSession(true);
      
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      List<User> users = mapper.selectUser();
      for (User user : users) {
      System.out.println(user.toString());
      }
      }
      
      //Spring整合过后的链接数据库的方法
      @Test
      public void stringTest(){
      ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
      UserMapper userMapper = applicationContext.getBean("userMapperImpl2", UserMapper.class);
      for (User user : userMapper.selectUser()) {
      System.out.println("user = " + user);
      }
      }
      
      }

    12、声明式事务

    1. 回顾事务
        要么都成功,要么都失败
      • 事务在项目开发中十分重要,涉及到数据的一致性问题
      • 确保完整性和一致性的关键
    2. 事务的ACID原则:
        原子性
      • 一致性
      • 隔离性:多个业务可能操作同一个资源,互相隔离不影响正确性。防止数据损坏
      • 持久性:事务一旦完成,无论系统发生如何变故,结果都不会再被影响,被持久化写到存储器中

    12.1、Spring中的事物管理

    • 声明式事务:AOP横切
    • 编程式事务:代码中事务管理

    12.2、案例

    1. ApplicationContext.xml

      <!--创建声明事务管理对象-->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <constructor-arg ref="dataSource" />
      </bean>
      
      <!--结合AOP实现声明式事务织入-->
      <!--配置事务通知-->
      <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.neil.mapper.*.*(..))"/>
      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
      </aop:config>
    2. UserMapperImpl

      package com.neil.mapper;
      
      import com.neil.pojo.User;
      import org.mybatis.spring.SqlSessionTemplate;
      import org.mybatis.spring.support.SqlSessionDaoSupport;
      
      import java.util.List;
      
      public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
      //我们的所有操作与拿来都使用sqlSession
      
      @Override
      public List<User> selectUser() {
      
      User user = new User(6, "张三三", "123123");
      UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
      mapper.addUser(user);
      mapper.deleteUser(6);
      return mapper.selectUser();
      }
      
      @Override
      public int addUser(User user) {
      return getSqlSession().getMapper(UserMapper.class).addUser(user);
      }
      
      @Override
      public int deleteUser(int id) {
      return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
      }
      }

      参考文献:本篇学习笔记主要总结于bilibili up主 “狂神学Java” 的系列视频— 【狂神说Java】Spring5最新完整教程IDEA版通俗易懂

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐