❤️六万字《Spring框架从入门到精通》(建议收藏)❤️

❤️ Spring

代码:https://github.com/Donkequan/Spring-Study

❤️ 希望各位博友三连+关注!!!

1、简介

spring理念:是现有的技术更加容易使用,本身是一个大杂烩。

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

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

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

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

1.1、导入包

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

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


1.2、优点

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

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

1.3、Spring组成

img

img

1.4、拓展

  1. Spring Boot 构建一切
    1. 一个快速开发的脚手架
    2. 基于SpringBoot可以快速的开发单个微服务
    3. 约束大于配置!
  2. Spring Cloud 协调一切
    1. SpringCloud是基于SpringBoot实现的
  3. Spring Cloud Data Flow 连接一切

学习SpringBoot的前提,需要完全掌握Spring以及SpringMVC!

2、IOC理论推导

  1. UserDao
  2. UserDaoImp
  3. UserSevice
  4. UserServiceImp

在之前,用户的需求可能会影响原来的代码。

2.1、分析实现

新建一个空白的maven项目

分析实现

我们先用我们原来的方式写一段代码 .

1、先写一个UserDao接口

public interface UserDao {
    
    
  public   void getUser();
}

2、再去写Dao的实现类

public class UserDaoImp implements UserDao{
    
    
    @Override
    public void getUser() {
    
    
        System.out.println("默认获取用户的数据");
    }
}

3、然后去写UserService的接口

public interface UserService {
    
    
    public void getUser();
}

4、最后写Service的实现类

public class UserServiceImp implements UserService{
    
    

    private UserDao userDao = new UserDaoImp();

    public void getUser(){
    
    
        userDao.getUser();
    }
}

5、测试一下

@Test
public void MyTest(){
    
    
    UserService service = new UserServiceImpl();
    service.getUser();
}

这是我们原来的方式 , 开始大家也都是这么去写的对吧 . 那我们现在修改一下 .

把Userdao的实现类增加一个 .

public class UserDaoMysqlImp implements UserDao{
    
    
    @Override
    public void getUser() {
    
    
        System.out.println("Mysql获取用户数据!");
    }
}

紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现

public class UserServiceImpl implements UserService {
    
    
   private UserDao userDao = new UserDaoMySqlImpl();

   @Override
   public void getUser() {
    
    
       userDao.getUser();
  }
}

在假设, 我们再增加一个Userdao的实现类 .

public class UserDaoOracleImpl implements UserDao {
    
    
   @Override
   public void getUser() {
    
    
       System.out.println("Oracle获取用户数据");
  }
}

那么我们要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .

那我们如何去解决呢 ?

我们可以在需要用到他的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下 .

public class UserServiceImpl implements UserService {
    
    
   private UserDao userDao;
// 利用set实现
   public void setUserDao(UserDao userDao) {
    
    
       this.userDao = userDao;
  }

   @Override
   public void getUser() {
    
    
       userDao.getUser();
  }
}

现在去我们的测试类里 , 进行测试 ;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //用户实际调用的是业务层,dao层他们不需要接触!
        UserServiceImp userService = new UserServiceImp();
        ((UserServiceImp) userService).setUserDao(new UserDaoSqlserviceImp());
        userService.getUser();
    }
}

使用一个set。

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

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

2.2、IOC本质

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

img

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

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

img

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

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

示例:

HelloSpring

1、导入Jar包

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

2、编写一个Hello实体类

public class Hello {
    
    
   private String name;

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

   public void show(){
    
    
       System.out.println("Hello,"+ name );
  }
}

3、编写我们的spring文件 , 这里我们命名为beans.xml

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

<!--    使用Spring来创建对象,在Spring中这些都称为Bean
        类型 变量名  = new 类型
        Hello hello = new Hello();
        bean=对象 new Hello()

        id=变量名
        class=new 的对象
        property 相当于给对象中的属性设置有一个值
-->
    <bean id="hello" class="com.kk.pojo.Hello">
        <property name="str" value="Spring">
        </property>
    </bean>


</beans>

4、测试

import com.kk.pojo.Hello;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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


    }
}

思考

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

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

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

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

可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

修改案例一

我们在案例一中, 新增一个Spring配置文件beans.xml

注入:
1.ref:引用Spring容器中创建好的对象
2.value:具体的值,基本数据类型

<?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-3.0.xsd">
        <bean id="mysqlImp" class="com.kk.dao.UserDaoMysqlImp"></bean>
        <bean id="oracleImp" class="com.kk.dao.UserDaoOracleImp"></bean>
        <bean id="SqlserviceImp" class="com.kk.dao.UserDaoSqlserviceImp"></bean>

        <bean id="UserServiceImp" class="com.kk.service.UserServiceImp">
            <property name="userDao" ref="SqlserviceImp"></property>
        </bean>
    <!--            注入-->
    <!--            ref:引用Spring容器中创建好的对象
                    value:具体的值,基本数据类型-->

</beans>

测试:

import com.kk.dao.UserDaoImp;
import com.kk.dao.UserDaoSqlserviceImp;
import com.kk.service.UserServiceImp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        //获取ApplicationContext  拿到Spring的容器
      ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        UserServiceImp userServiceImp = (UserServiceImp)context.getBean("UserServiceImp");
        userServiceImp.getUser();
    }
}

2.3、IOC创建对象方式

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

    实体类

    1. package com.kk.pojo;
      
      public class User {
              
              
          private String name;
          public User() {
              
              
              System.out.println("user无参构造方法");
          }
          public String getName() {
              
              
            return name;
          }
          public void setName(String name) {
              
              
              this.name = name;
          }
      
          public void show(){
              
              
              System.out.println("name="+ name );
          }
      }
      

      2.beans.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
      
          <bean id="user" class="com.kk.pojo.User">
      <!--       有一个属性name需要注入,-->
              <property name="name" value="KK"></property>
          </bean>
      
      </beans>
      

      3、测试类

      import com.kk.pojo.User;
      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");
      
              User  user = (User)context.getBean("user");
              user.show();
          }
      }
      
  2. 使用有参构造

    1. 下标赋值

      实体类

package com.kk.pojo;

public class User {
    
    
    private String name;

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

    public void show(){
    
    
        System.out.println("name="+ name );
    }
}

beans.xml

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

    <bean id="user" class="com.kk.pojo.User">
<!--        第一种:下标赋值-->
        <constructor-arg index="0" value="kkkkkk"></constructor-arg>
    </bean>

</beans>

测试

import com.kk.pojo.User;
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");

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

2.通过类型创建

<!--   第二种方式:通过类型创建  不建议使用以下方法-->
    <bean id="user" class="com.kk.pojo.User">
        <constructor-arg type="java.lang.String" value="k11"></constructor-arg>
    </bean>

3.直接通过参数名创建

<!--    第三种方式:直接通过参数名创建-->
    <bean id="user" class="com.kk.pojo.User">
        <constructor-arg name="name" value="k111"></constructor-arg>
    </bean>

3、 Spring配置

3.1、别名

<!--    别名,如果添加了别名,我们也可以通过别名获取-->
    <alias name="user" alias="abc"></alias>
import com.kk.pojo.User;
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");
        User  user = (User)context.getBean("abc");
        user.show();
    }
}

3.2、Bean的配置

  • id:bean的id标识符
  • class:bean对象所对应的类型
  • name:别名,更高级,可以同时取多个别名。
<!--    id:bean的唯一标识符,也是相当于我们学的对象名
        class:bean对象所对应的权限定名:包名+类型
        name :也是别名 而且name可以取多个别名
        -->
    <bean id="userT" class="com.kk.pojo.UserT" name="user2,u2 u3;u4">
        <property name="name" value="KK"></property>
    </bean>

3.3、import

这个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
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <import resource="beans.xml"></import>
    <import resource="beans2.xml"></import>
    <import resource="beans3.xml"></import>

</beans>

4、DI依赖注入

4.1、构造器注入

4.2、Set方式注入

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

环境搭建

1.复杂类型

public class Address {
    
    
    private String address;
    public String getAddress() {
    
    
        return address;
    }

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

}

2.真实测试对象

package com.kk.pojo;


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 Student() {
    
    

    }

    public Student(String name, Address address, String[] books,
                   List<String> hobbys, Map<String, String> card,
                   Set<String> games, String wife, Properties info) {
    
    
        this.name = name;
        this.address = address;
        this.books = books;
        this.hobbys = hobbys;
        this.card = card;
        this.games = games;
        this.wife = wife;
        this.info = 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 +
                '}';
    }
}

3.bean.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-3.0.xsd">

<!--    第一种:普通值注入  使用value值注入-->
    <bean id="student" class="com.kk.pojo.Student">
        <property name="name" value="火神"></property>
    </bean>

</beans>

4.测试类:

import com.kk.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());
    }
}

5.完善注入信息:

<?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-3.0.xsd">
    <bean id="address" class="com.kk.pojo.Address">
        <property name="address" value="广东"></property>
    </bean>


    <bean id="student" class="com.kk.pojo.Student">
        <!--    第一种:普通值注入  使用value值注入-->
        <property name="name" value="火神"></property>
<!--        第二种:Bean注入 ,使用ref注入-->
        <property name="address" ref="address"></property>
<!--        数组注入,-->
        <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="4412002121223***"></entry>
                <entry key="银行卡" value="654645454546"></entry>
            </map>
        </property>

<!--        set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CSOL</value>
            </set>
        </property>

<!--        wife注入-->
        <property name="wife">
            <null></null>
        </property>

<!--        Properties-->
        <property name="info">
            <props>
                <prop key="学号">2011***</prop>
                <prop key="性别"></prop>
                <prop key="姓名">k</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
     </bean>

</beans>

4.3、其他方式注入

实体类:

package com.kk.pojo;

public class User {
    
    
    private int age;
    private String name;

    //c命名空间需要有参构造器


    public User(int age, String name) {
    
    
        this.age = age;
        this.name = name;
    }

    public User() {
    
    

    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

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

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

​ 1、P命名空间注入 : 需要在头文件中加入约束文件

 导入约束 : xmlns:p="http://www.springframework.org/schema/p"

 <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.kuang.pojo.User" p:name="kk" p:age="17"/>

测试类:

    @Test
    public void test2(){
    
    
       ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
//        User user =(User) context.getBean("user");
        User user = context.getBean("user", User.class);
        System.out.println(user);

    }

2、c 命名空间注入 : 需要在头文件中加入约束文件

 导入约束 : xmlns:c="http://www.springframework.org/schema/c"
 <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.kuang.pojo.User" c:name="kk" c:age="17"/>

测试类:

@Test
public void test3(){
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    System.out.println(user);

}

4.4、bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IOC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象

img

​ 几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

4.4.1、Singleton单例模式(Spring默认机制)

​ 当一个bean的作用域为Singleton,那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

    <bean id="user2" class="com.kk.pojo.User" c:name="kk" c:age="99" scope="singleton"></bean>

测试:

@Test
public void test3(){
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    User user2 = context.getBean("user2", User.class);
    System.out.println(user==user2);//true	
    System.out.println(user);

}

4.4.2、Prototype(原型模式)每次get的时候都会产生新的对象

​ 当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

<!--    每次get的时候都会产生新的对象-->
    <bean id="user2" class="com.kk.pojo.User" c:name="kk" c:age="99" scope="prototype"></bean>

测试:

@Test
public void test3(){
    
    
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    User user = context.getBean("user2", User.class);
    User user2 = context.getBean("user2", User.class);
    System.out.println(user.hashCode());
    System.out.println(user2.hashCode());
    //两个值不同
    System.out.println(user==user2);//false

    System.out.println(user);

}

4.4.3、Request

​ 当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

​ 针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

4.4.4、Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

​ 针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

5、Bean的自动装配

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

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

  1. 在xml中显示的配置
  2. 在java中显示配置
  3. 隐式的自动装配bean(重要)

5.1、测试

环境搭建:一个人有两个宠物!

实体类:

Cat

package com.kk.pojo;

public class Cat {
    
    
    public void shout(){
    
    
        System.out.println("miao~");
    }
}

Dog

package com.kk.pojo;

public class Dog {
    
    
    public void shout(){
    
    
        System.out.println("Wang~");
    }
}

People

package com.kk.pojo;

public class People {
    
    
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
    
    
        return cat;
    }

    public void setCat(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 + '\'' +
                '}';
    }
}

beans.xml:

5.2、ByName自动装配

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

    <bean id="cat" class="com.kk.pojo.Cat"></bean>
    <bean id="dog" class="com.kk.pojo.Dog"></bean>

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

5.3、ByType自动装配

byType:会自动在容器上下文中查找,和自己对象属性类型相同的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="cat" class="com.kk.pojo.Cat"></bean>
<!--    <bean id="dog1212" class="com.kk.pojo.Dog"></bean>-->
    <bean  class="com.kk.pojo.Dog"></bean>

    <!--    byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean! 必须保证类目全局唯一-->
    <bean id="people" class="com.kk.pojo.People" autowire="byType">
        <property name="name" value="kk"></property>
        <!--        <property name="dog" value="dog"></property>-->
        <!--        <property name="cat" value="cat"></property>-->
    </bean>

</beans>

测试类:

import com.kk.pojo.People;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    @Test
    public void test1(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        People people = context.getBean("people", People.class);
        people.getDog().shout();
        people.getCat().shout();
    }
}

小结

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

5.4、使用注解实现自动装配

使用注解

jdk1.5开始支持注解,spring2.5开始全面支持注解。

准备工作:利用注解的方式注入属性。

1、在spring配置文件中引入context文件头

 xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
  http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

2、开启属性注解支持!

<!--    开启注解的支持-->
    <context:annotation-config/>

5.4.1、Autowired

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

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

测试:

实体类

package com.kk.pojo;

public class cat {
    
    
    public void shout(){
    
    
        System.out.println("miao~");
    }
}
package com.kk.pojo;

public class dog {
    
    
    public void shout(){
    
    
        System.out.println("Wang~");
    }
}
package com.kk.pojo;

import org.springframework.beans.factory.annotation.Autowired;

public class People {
    
    
    @Autowired
    private cat cat;
    @Autowired
    private dog dog;
    private String name;

    public com.kk.pojo.cat getCat() {
    
    
        return cat;
    }

    public void setCat(com.kk.pojo.cat cat) {
    
    
        this.cat = cat;
    }

    public com.kk.pojo.dog getDog() {
    
    
        return dog;
    }

    public void setDog(com.kk.pojo.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 + '\'' +
                '}';
    }
}

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

    <!--    开启注解的支持-->
        <context:annotation-config/>
    <bean id="cat" class="com.kk.pojo.cat"></bean>
    <bean id="dog" class="com.kk.pojo.dog"></bean>
    <bean id="people" class="com.kk.pojo.People"></bean>

</beans>

测试类:

import com.kk.pojo.People;
import org.testng.annotations.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    @Test
    public void test1(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        People people = context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();

    }
}

注意:

@Nullable 字段标记了这个

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

@Qualifier

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

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用。

测试实验步骤:

1、配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

<bean id="dog1" class="com.kk.pojo.dog"/>
<bean id="dog2" class="com.kk.pojo.dog"/>
<bean id="cat1" class="com.kk.pojo.cat"/>
<bean id="cat2" class="com.kk.pojo.cat"/>

2、没有加Qualifier测试,直接报错

3、在属性上添加Qualifier注解

@Autowired
@Qualifier(value = "cat2")
private cat cat;
@Autowired
@Qualifier(value = "dog2")
private dog dog;

测试,成功输出!

@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

实体类:

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

beans.xml

<!--    开启注解的支持-->
    <context:annotation-config/>

        <bean id="cat1" class="com.kk.pojo.cat"></bean>
        <bean id="cat2" class="com.kk.pojo.cat"></bean>
        <bean id="dog" class="com.kk.pojo.dog"></bean>
        <bean id="dog11" class="com.kk.pojo.dog"></bean>
        <bean id="people" class="com.kk.pojo.People"></bean>


测试:结果OK

配置文件2:beans.xml , 删掉cat2

    <bean id="dog" class="com.kk.pojo.dog"></bean>
    <bean id="cat1" class="com.kk.pojo.cat"></bean>

实体类上只保留注解

@Resource
private cat cat;
@Resource
private dog dog;

结果:OK

结论:先进行byName查找,失败;再进行byType查找,成功。

小结

@Autowired与@Resource异同:

​ 1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

​ 2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

​ 3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

6、使用注解开发

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

img

使用注解开发需要导入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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!--    开启注解的支持-->
    <context:annotation-config/>

</beans>

6.1、bean

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

1、配置扫描哪些包下的注解

<!--指定注解扫描包-->
<context:component-scan base-package="com.kk.pojo"/>

2、在指定包下编写类,增加注解

package com.kk.pojo;


import org.springframework.stereotype.Component;
//这里等价于    <bean id="user" class="com.kk.pojo.User"></bean>
//Component 组件
@Component
public class User {
    
    
    public String name="k";
}

3、测试

import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

6.2、属性如何注入

package com.kk.pojo;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这里等价于    <bean id="user" class="com.kk.pojo.User"></bean>
//Component 组件
@Component
public class User {
    
    

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

6.3、衍生的注解

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

  • dao【@Repository】
  • service【@Service】
  • controller【@Controller】

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

6.4、自动装配置

-@Autowired :自动装配通过类型  名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxxx")
-@Nullable 字段标记了这个注解,说明这个字段可以为null
-Resource:自动装配通过名字  类型

6.5、作用域

<?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
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--    指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.kk"></context:component-scan>
    <!--    开启注解的支持-->
    <context:annotation-config/>


<!--    <bean id="user" class="com.kk.pojo.User">-->
<!--        <property name="name" value="kk"></property>-->
<!--    </bean>-->

</beans>
package com.kk.pojo;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//这里等价于    <bean id="user" class="com.kk.pojo.User"></bean>
//Component 组件
@Component
@Scope("prototype")
public class User {
    
    

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

singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。

prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
*/

6.6、小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便,维护相对复杂

xml与注解整合开发 :推荐最佳实践

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

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

7、基于Java的方式配置Spring

完全不使用Spring的xml配置,全权交给java来做!

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

img

测试

实体类:User

package com.kk.pojo;

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;

    public String getName() {
    
    
        return name;
    }
    @Value("kk")//属性注入值
    public void setName(String name) {
    
    
        this.name = name;
    }

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

配置文件:MyConfig

package com.kk.config;

import com.kk.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 代表这是一个配置类,就和beans.xml一样的
//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component
@Configuration
@ComponentScan("com.kk.pojo")
@Import(MyConfig2.class)  //引入
public class MyConfig {
    
    

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

配置文件:MyConfig2

package com.kk.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig2 {
    
    
}

测试类:

import com.kk.config.MyConfig;
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    

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

8、静态/动态代理模式

为什么要学习代理模式,因为AOP的底层机制就是动态代理!

代理模式:

  • 静态代理

  • 动态代理

    img

8.1、静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

代码步骤:

1、接口

//租房的接口
public interface Rent {
    
    
    public void rent();
}

2、真实角

//房东
public class Host {
    
    
    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() {
    
    
        seeHouse();
        host.rent();
        hetong();
        fare();

    }

    //看房
    public void seeHouse(){
    
    
        System.out.println("带房客看房");
    }
    //租赁
    public void hetong(){
    
    
        System.out.println("签租赁合同");
    }
    //收中介费
    public void fare(){
    
    
        System.out.println("收中介费");
    }
}

4、客户端访问代理角色

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //房东要租房子
        Host host = new Host();
        //代理,中介帮方法租房子
        //代理操作一般会带一些附属操作
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

静态代理再理解

1、创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

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

2、我们需要一个真实对象来完成这些增删改查操作

//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
    
    

   public void add() {
    
    
       System.out.println("增加了一个用户");
  }

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

   public void update() {
    
    
       System.out.println("更新了一个用户");
  }

   public void query() {
    
    
       System.out.println("查询了一个用户");
  }
}

3、需求来了,现在我们需要增加一个日志功能,怎么实现!

  • 思路1 :在实现类上增加代码 【麻烦!】
  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!

4、设置一个代理类来处理日志!代理角色

//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
    
    
   private UserServiceImpl userService;

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

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

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

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

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

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

}

5、测试访问类:

public class Client {
    
    
   public static void main(String[] args) {
    
    
       //真实业务
       UserServiceImpl userService = new UserServiceImpl();
       //代理类
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理类实现日志功能!
       proxy.setUserService(userService);

       proxy.add();
  }
}

OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

AOP:纵向开发,横向开发

img

8.2、动态代理

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理–cglib
    • 现在用的比较多的是 javasist 来生成动态代理
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy

【InvocationHandler:调用处理程序】

img

Object invoke(Object proxy, 方法 method, Object[] args)//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

【Proxy : 代理】

img

img

img

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

代码实现

Rent.java 即抽象角色

//抽象角色:租房
public interface Rent {
    
    
   public void rent();
}

Host.java 即真实角色

package com.kk.demo03;

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

ProxyInvocationHandler. java 即代理角色

package com.kk.demo03;

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


//使用该类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    
    

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
    
    
        this.rent = rent;
    }

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

    //处理代理事例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //动态代理的本质就是使用反射机制实现!
        seeHouse();
        fare();
        Object result = method.invoke(rent, args);
        return result;
    }
    public void seeHouse(){
    
    
        System.out.println("中介带看房子");
    }
    public void fare(){
    
    
        System.out.println("收中介费");
    }
}

Client.java

package com.kk.demo03;

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //真实角色
        Host host = new Host();

        //代理角色:现在无
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们需要调用的接口对象!
        pih.setRent(host);
        Rent proxy =(Rent) pih.getProxy();//这里的proxy就是动态代理
        proxy.rent();
    }

}

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!

深化理解

我们来使用动态代理实现代理我们后面写的UserService!

我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

package com.kk.demo04;

import com.kk.demo03.Rent;

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 {
    
    
        //动态代理的本质就是使用反射机制实现!
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

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

测试!

package com.kk.demo04;

import com.kk.demo02.UserService;
import com.kk.demo02.UserServiceImp;

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

}
动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!

9、AOP

9.1、什么是AOP

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

img

9.2、Aop在Spring中的作用

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

以下名词需要了解下:

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

img

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

img

9.3、使用Spring实现Aop

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

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

第一种方式

9.3.1、通过 Spring API 实现【主要Spring API接口实现】

首先编写我们的业务接口和实现类



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


public class UserServiceImp 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("查询了一个用户");
    }
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class log implements MethodBeforeAdvice {
    
    
   //method 要执行的目标对象的方法
    //args:参数
    //target目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");

    }
}
package com.kk.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    
    

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

最后去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: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.kk.service.UserServiceImp"></bean>
    <bean id="log" class="com.kk.log.log"></bean>
    <bean id="afterLog" class="com.kk.log.AfterLog"></bean>

    <!--方式一:使用原生的Spring API接口-->
<!--    配置aop,需要aop的约束-->
    <aop:config >
<!--        需要切入点 expression:表达式     execution() 要执行的位置-->
        <aop:pointcut id="poincut"  expression="execution(* com.kk.service.UserServiceImp.*(..))"></aop:pointcut>
       
<!--       执行环绕增加!  -->
        <aop:advisor advice-ref="log" pointcut-ref="poincut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="poincut"/>



    </aop:config>


</beans>

测试

import com.kk.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    
    
    public static void main(String[] args) {
    
    
      ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      //动态代理  代理的是接口
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

第二种方式

9.3.2、自定义类来实现Aop【主要是切面定义】

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

public class DiyPointcut {
    
    

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

去spring中配置

<?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.kk.service.UserServiceImp"></bean>
    <bean id="log" class="com.kk.log.log"></bean>
    <bean id="afterLog" class="com.kk.log.AfterLog"></bean>

<!--    方式二,自定义类-->
    <bean id="diy" class="com.kk.diy.DiyPointCut"></bean>

    <aop:config>
<!--        自定义切面,使用ref调用要引用的类-->
        <aop:aspect ref="diy">
<!--            切入点-->
            <aop:pointcut id="point" expression="execution(* com.kk.service.UserServiceImp.*(..))"/>
<!--            通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>


</beans>

测试:

public class MyTest {
    
    
   @Test
   public void test(){
    
    
       ApplicationContext context = newClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}

第三种方式

9.3.3、使用注解实现

第一步:编写一个注解实现的增强类

package com.kk.diy;

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

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

@Aspect  //使用注解标记这个类是一个切面
public class AnnotationPointCut {
    
    
    @Before("execution(* com.kk.service.UserServiceImp.*(..))")  //注解的内容  应写入插入点
    public void before(){
    
    
        System.out.println("===========方法执行前==========");
    }

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

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

        System.out.println("环绕前");
        Object proceed = jp.proceed();        //执行方法
        System.out.println("环绕后");

        Signature signature = jp.getSignature();//获得签名
        System.out.println("signature:"+signature);
        System.out.println(proceed);

    }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--    方式三:使用注解实现AOP-->
    <bean id="AnnotationPointCut" class="com.kk.diy.AnnotationPointCut"></bean>

<!--    开启注解支持  jdk(默认) proxy-target-class="false"        cglib proxy-target-class="true"   -->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

aop:aspectj-autoproxy:说明

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

10、整合MyBatis

步骤

1、导入相关jar包

junit

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

mybatis

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

mysql-connector-java

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

spring相关

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>

aspectJ AOP 织入器

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

mybatis-spring整合包 【重点】

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>2.0.2</version>
</dependency>

配置Maven静态资源过滤问题!

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>true</filtering>
       </resource>
   </resources>
</build>

2、编写配置文件

3、代码实现

10.1、回忆MyBatis

1、编写pojo实体类

package com.kk.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    
    
    private int id;  //id
    private String name;   //姓名
    private String pwd;   //密码

}

2、实现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>
        <package name="com.kk.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;
                useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8&amp;
                allowPublicKeyRetrieval=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
      <mapper class="com.kk.mapper.UserMapper"/>
    </mappers>
</configuration>

3、UserDao接口编写

package com.kk.mapper;

import com.kk.pojo.User;

import java.util.List;

public interface UserMapper {
    
    
    public List<User> selectUser();
}

4、接口对应的Mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kk.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>


</mapper>

5、测试类

import com.kk.mapper.UserMapper;
import com.kk.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    
    
    @Test
    public void selectUser() throws IOException {
    
    

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.selectUser();
        for (User user: userList){
    
    
            System.out.println(user);
        }

        sqlSession.close();
    }
}

10.2、MyBatis-Spring学习

引入Spring之前需要了解mybatis-spring包中的一些重要类;

http://www.mybatis.org/spring/zh/index.html

img

什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

知识基础

在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要

MyBatis-Spring 需要以下版本:

MyBatis-Spring MyBatis Spring 框架 Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

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

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

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
</bean>

注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。

SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(),数据源()和 MyBatis 的事务管理器()都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

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

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

package com.kk.mapper;

import com.kk.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImp implements UserMapper{
    
    

    //在原来,所有的操作,都使用SqlSession来执行; 现在都使用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;
    public void setSqlSession(SqlSessionTemplate sqlSession){
    
    
        this.sqlSession=sqlSession;
    }

    @Override
    public List<User> selectUser() {
    
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

按下面这样,注入 SqlSessionTemplate:

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

整合实现一

1、引入Spring配置文件beans.xml

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

2、配置数据源替换mybaits的数据源

<!--    DataSource使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;
                useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8&amp;
                allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

3、配置SqlSessionFactory,关联MyBatis

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

4、注册sqlSessionTemplate,关联sqlSessionFactory;

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

5、增加Mapper接口的实现类;私有化sqlSessionTemplate

package com.kk.mapper;

import com.kk.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImp implements UserMapper{
    
    

    //在原来,所有的操作,都使用SqlSession来执行; 现在都使用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;
    public void setSqlSession(SqlSessionTemplate sqlSession){
    
    
        this.sqlSession=sqlSession;
    }

    @Override
    public List<User> selectUser() {
    
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

6、注册bean实现

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

7、测试

public class MyTest {
    
    
    @Test
    public void selectUser() throws IOException {
    
    

       ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
    
    
            System.out.println(user);
        }
    }
}

结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.kk.pojo"/>
    </typeAliases>

</configuration>

整合实现二

mybatis-spring1.2.3版以上的才有这个 .

官方文档截图 :

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看

图片

测试:

1、将我们上面写的UserMapperImp修改一下

package com.kk.mapper;

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

import java.util.List;

public class UserMapperImp2 extends SqlSessionDaoSupport implements UserMapper{
    
    

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

2、修改bean的配置

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

3、测试

public class MyTest {
    
    
    @Test
    public void selectUser() throws IOException {
    
    

       ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
    
    
            System.out.println(user);
        }

    }
}

11、声明式事务

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。
  • 事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

原子性(atomicity)

  • 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

一致性(consistency)

  • 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

隔离性(isolation)

  • 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

持久性(durability)

  • 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

测试

将上面的代码拷贝到一个新项目中

在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;

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

//根据id删除用户
int deleteUser(int id);

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

<insert id="addUser" parameterType="com.kk.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>

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

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    
    

   //增加一些操作
   public List<User> selectUser() {
    
    
       User user = new User(8,"bb","123456");
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       mapper.addUser(user);
       mapper.deleteUser(4);
       return mapper.selectUser();
  }

   //新增
   public int addUser(User user) {
    
    
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.addUser(user);
  }
   //删除
   public int deleteUser(int id) {
    
    
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.deleteUser(id);
  }

}

测试

public class MyTest {
    
    
        public static void main(String[] args) {
    
    
       ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> userList = userMapper.selectUser();
    }
}

Resources:

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <typeAliases>
        <package name="com.kk.pojo"/>
    </typeAliases>


</configuration>

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: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-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;
                useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8&amp;
                allowPublicKeyRetrieval=true"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

<!--    sqlSeesionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
<!--        绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/kk/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.kk.mapper.UserMapperImp">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>



</beans>

application.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">

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

    <bean id="userMapper" class="com.kk.mapper.UserMapperImp">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

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

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事务管理器后我们需要去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事务传播特性:

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

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

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

配置AOP

导入aop的头文件!

<!--配置aop织入事务-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

删掉刚才插入的数据,再次测试!

@Test
public void test2(){
    
    
   ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
   UserMapper mapper = (UserMapper) context.getBean("UserMapper");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

r" class=“com.kk.mapper.UserMapperImp”>

```

application.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">

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

    <bean id="userMapper" class="com.kk.mapper.UserMapperImp">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

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

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事务管理器后我们需要去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事务传播特性:

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

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

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

配置AOP

导入aop的头文件!

<!--配置aop织入事务-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

删掉刚才插入的数据,再次测试!

@Test
public void test2(){
    
    
   ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
   UserMapper mapper = (UserMapper) context.getBean("UserMapper");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

猜你喜欢

转载自blog.csdn.net/weixin_50569789/article/details/120125876