当前位置:首页 » 《随便一记》 » 正文

狂神说Java --- 记录Spring学习笔记_小智RE0的博客

26 人参与  2021年12月25日 08:53  分类 : 《随便一记》  评论

点击全文阅读


传送门 ==>B站遇见狂神说—Spring5教程

感谢狂神,学习视频真的是通俗易懂♥♥♥

笔记和练习只是跟着视频整理的;有的知识点并没有整理进来


ml

  • 1.Spring概述
  • 2. 控制反转IOC
  • 3.试试Spring
    • 3.1 IOC创建对象的方式
    • 3.2 Spring配置说明
    • 3.3 DI (依赖注入)
      • 3.3.1 set注入
      • 3.3.2 (扩展) p命名set注入 ; c命名构造注入
    • 3.4 Bean的作用域
    • 3.5 自动装配Bean
      • 3.5.1 byName,byType自动装配
      • 3.5.2 使用注解实现装配
        • Spring注解开发
      • 3.5.3 使用JavaConfig实现配置
  • 4. 静态代理模式
  • 5. 动态代理模式
  • 6.面向切面 AOP
    • 方式1:使用原生的Spring API接口
    • 方式 2;使用自定义 实现
    • 方式3: 使用注解实现
  • 7. 整合Mybatis
    • 7.1Spring整合 Mybatis的方式一
    • 7.2 Spring整合 Mybatis的方式二
  • 8.声明式事务


Spring 官网==>https://spring.io
Spring官方文档==>https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html

本次使用的是Spring Web MVC

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

之后还可能用到spring jdbc

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

1.Spring概述

Spring是开源免费的轻量级的控制反转(IOC)和面向切面(AOP)的容器框架;支持事务处理.

由7个模块组成,

在这里插入图片描述

(Spring Boot) 构建一切,(Sprint Cloud)协调一切,(Spring Cloud Data Flow)连接一切

(Spring Boot)就像脚手架,可实现快速开发单个微服务;而学习它的前提是学习Spring和SpringMVC.


2. 控制反转IOC

IOC理论推导

创建一个空的maven项目,删除src目录;作为父级项目;

还是和之前一样,先去检查maven地址;

在这里插入图片描述

pom.xml导包

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.9</version>
    </dependency>
    <!--测试使用-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
</dependencies>

可看到,下载结束后,出现了好几个资源包

在这里插入图片描述

创建一个子级maven工程;

在这里插入图片描述

在这里插入图片描述


就用一个基础的项目构建来说吧;
持久层接口UserDao;

public interface UserDao {
    //查询用户;
    void findUser();
}

持久层实现类UserDaoImpl;

public class UserDaoImpl implements UserDao{
    public void findUser() {
        System.out.println("默认调用的方法");
    }
}

业务层接口UserService;

public interface UserService {
    //查询用户;
    void findUser();
}

业务层接口的实现类UserServiceImpl;

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

    public void findUser() {
      //在业务层调用持久层的方法;
        userDao.findUser();
    }
}

测试调用

@Test
    public void test(){
        UserService userService=new UserServiceImpl();
        userService.findUser();
    }

这时输出

在这里插入图片描述

而如果说,这时候又来需求了,持久层新增了一个实现类UserMySqlImpl

public class UserMySqlImpl implements UserDao{
    public void findUser() {
        System.out.println("MySql的方法");
    }
}

那么如果说想要拿到这个需求的实现,就要在业务层的实现类UserServiceImpl中;去更改持久层的实现类改为UserMySqlImpl;

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

    public void findUser() {
      //在业务层调用持久层的方法;
        userDao.findUser();
    }
}

测试调用,即可

在这里插入图片描述

如果说又有一个新的需求,创建了一个新的持久层实现类UserOracleImpl;

public class UserOracleImpl implements UserDao{
    public void findUser() {
        System.out.println("Oracle方式");
    }
}

那么就得去业务层UserServiceImpl中更改;

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

    public void findUser() {
      //在业务层调用持久层的方法;
        userDao.findUser();
    }
}

然后测试调用

在这里插入图片描述

在之前的业务中,用户的需求可能会影响原来的代码,就要根据用户的需求更改代码.

要是在UserServiceImpl中用set实现动态修改实现类;

public class UserServiceImpl implements UserService{
     private UserDao userDao;
    //控制反转的原型,在业务层提供动态的用户需求方法;
    //用set实现动态注入值;
     public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
      }

    public void findUser() {
      //在业务层调用持久层的方法;
      userDao.findUser();
    }
}

在测试执行的方法内,只需要调用setUserDao 方法,去更改里面的持久层实现类即可

public class MyTest {
    @Test
    public void test() {
        UserService userService = new UserServiceImpl();
        ((UserServiceImpl) userService).setUserDao(new UserMySqlImpl());
        userService.findUser();
    }
}

在这里插入图片描述


每使用set设置之前,程序是需要主动创建对象,控制权在开发者手中,使用了set注入后,程序不再具有主动性,变为被动的接受对象.
开发者不必管理对象的创建,系统耦合性降低,专注于业务的实现.

即控制反转 IOC 的原型.


IOC的本质

控制反转IOC 设计思想,而 DI(依赖注入)是实现IOC的一种方法,
在没有使用IOC的程序,作为面向对象编程的语言,对象的创建和对象间的依赖关系是必须硬编码在程序中,对象的创建由程序自己控制,

控制反转后将对象的创建转移到第三方, 获取依赖对象的方式反转.


3.试试Spring

ok新建一个实体类User;

public class User {
    private String name;

    public User(){
        System.out.println("User类的无参构造");
    }
    
    public User(String name){
        this.name=name;
        System.out.println("User类的有参构造");
    }
    
    public String getName() {
        return name;
    }

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

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

resources目录下创建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">

    <!--使用spring 创建对象;就是 bean-->
    <!--这时的id就是变量名;class就是new的对象-->
    <!--property: 可以为对象的属性设置值-->

    <bean id="user" class="com.lzq.pojo.User">
        <property name="name" value="xiaozhi"/>
    </bean>
</beans>

测试

public class Test01 {
    public static void main(String[] args) {
        //获取Spring得上下文对象;
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //由于创建对象就是 Spring 中的bean; 获取bean即可获取对象;
        User user = (User) context.getBean("user");

        System.out.println(user.toString());
    }
}

输出:

User类的无参构造
User{name='xiaozhi'}

可以看出,从始至终没有使用new构造方法进行创建User类的对象;
但是实际上在xml配置文件中,已经被Spring创建对象了;且设置了属性name的值;
程序本身不去创建对象,作为了被动的接收对象.

此过程就是控制反转;IOC; 由Spring 创建 管理 装配.

在这里插入图片描述

这里使用了set方法来进行注入的;若删除set方法;就无法得到属性name.
设置value是针对于基本数据;

在这里插入图片描述

设置实体类显示小叶子图标;

在这里插入图片描述


再看看刚才的UserDao,实际上,根据需要就可以直接在配置文件修改即可.

ref: 会引用 在Spring中设置创建的对象


3.1 IOC创建对象的方式

刚在在测执行时;注意到,输出有调用到无参构造方法;

在这里插入图片描述

那么如果将User类的无参构造方法去掉;即为User类写个显示的有参构造,默认隐式的无参构造就没有了;

在这里插入图片描述

再次执行,出现异常提示;提示初始化异常;

在这里插入图片描述

在xml配置文件中也有提示

在这里插入图片描述

IOC创建对象时默认使用无参构造方法,

那么如果说想用有参构造方法呢,有三种方式;

方式1:使用index(下标) 指定有参构造方法的参数是第几个; value 为参数赋值 ;
beans.xml 文件的bean标签下的constructor-arg标签中配置

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

    <!--方式1:使用 index(下标)  指定有参构造方法的参数是第几个; value 为参数赋值 -->

    <bean id="user" class="com.lzq.pojo.User">
        <constructor-arg index="0" value="小智从零开始学Java"/>
    </bean>
</beans>

执行,是调用了有参构造方法的;且为参数name赋值也取到了.

在这里插入图片描述

方式2:使用type 属性 显式的指定构造函数参数的类型;value为参数赋值.

注意: 基本类型可直接写;引用类型要注明类的地址;
例如官方文档中是这么写的.

<bean id="exampleBean" class="examples.ExampleBean">
   <constructor-arg type="int" value="2001"/>
   <constructor-arg type="java.lang.String" value="Zara"/>
</bean>

ok,用方式2调有参构造方法,为user类创建对象.

<?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">
    <!--方式2: 使用 type属性 显式的指定构造函数参数的类型; value 为参数赋值-->
    <bean id="user" class="com.lzq.pojo.User">
        <constructor-arg type="java.lang.String" value="方式二"/>
    </bean>
</beans>

执行,使用成功;

在这里插入图片描述

方式 3: 直接使用 参数名name设置 ; 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">
    <!--方式3: 使用参数名name; -->
    <bean id="user" class="com.lzq.pojo.User" >
        <constructor-arg name="name" value="方式3"/>
    </bean>
</beans>

执行,使用成功;

在这里插入图片描述


还有就是,在pojo包下,再创建一个Student类;

public class Student {
    private int age;

    public Student() {
        System.out.println("Student类的无参构造方法");
    }

    public int getAge() {
        return age;
    }

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

注意;仅仅是在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">

    <!--User类-->
    <!--方式3: 使用参数名name; -->
    <bean id="user" class="com.lzq.pojo.User" >
        <constructor-arg name="name" value="方式3"/>
    </bean>

    <!--Student类-->
    <bean id="student" class="com.lzq.pojo.Student">

    </bean>

</beans>

诶,这里还是执行之前测试User类的代码;

public class Test01 {
    public static void main(String[] args) {
        //获取Spring得上下文对象;
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //由于创建对象就是 Spring 中的bean; 获取bean即可获取对象;
        User user = (User) context.getBean("user");

        System.out.println(user.toString());
    }
}

但是输出结果时,发现Student类创建对象时调用的无参构造方法也有记录;

在这里插入图片描述

实际上, Spring容器就像是一个婚介网站

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


3.2 Spring配置说明

别名
alias

比如说;为User类创建对象时的变量id user 取个别名u

<alias name="user"  alias="u"/>

在这里插入图片描述

那么,在执行测试时,使用别名也能获取到这个User类的对象;

在这里插入图片描述

Bean的配置
细细看看<bean>标签内的几个配置标签

id 就是 bean的唯一标识符 ,也就相当于对象名;

classbean 对应的全限定名==> 包名 +类型;

name别名 ;且 可以写多个别名,用逗号或者分号隔开即可

<bean id="user" class="com.lzq.pojo.User"  name="u1,u2;u3">
</bean>

import标签
一般在团队开发中使用;可以将多个配置文件导入合并为一个

<import resource="beans.xml"/>
<import resource="otherbeans.xml"/>
<import resource="otherbeans2.xml"/>

3.3 DI (依赖注入)

第一个就是构造函数注入,也就是刚才在创建对象时修改调用有参无参构造方法时;就使用过了.

第二个就是set注入,这个很重要

依赖: = = > bean对象的创建依赖于Spring容器
注入: = => bean对象中的所有属性,由容器注入.

3.3.1 set注入

用一个复杂的案例看看这个set注入

新建一个Adderss

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

新建一个Student

public class Student {
    private String name;
    //这里的Address是自定义的类;
    private Address address;

    private String[] books;

    private List<String> hobby;

    private Map<String,String>  card;

    private Set<String> games;

    private String friend;

    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

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

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    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 getFriend() {
        return friend;
    }

    public void setFriend(String friend) {
        this.friend = friend;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address.toString()  +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", games=" + games +
                ", friend='" + friend + '\'' +
                ", info=" + info +
                '}';
    }
}

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

    <!--为Address类创建对象-->
    <bean id="address" class="com.xiaozhi.pojo.Address">
        <property name="address" value="陕西"/>
    </bean>

    <!--为Student类创建对象-->
    <bean id="student" class="com.xiaozhi.pojo.Student">

        <property name="name" value="小智"/>
        <!--自定义引用类型 -->
        <property name="address" ref="address"/>
        <!--数组类型的 books-->
        <property name="books">
            <array>
                <value>算法图解</value>
                <value>大话数据结构</value>
                <value>剑指Offer</value>
                <value>java核心技术</value>
            </array>
        </property>
        <!--list类型的 hobby-->
        <property name="hobby">
            <list>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>学习</value>
                <value>敲代码</value>
            </list>
        </property>
        <!--map类型的 card-->
        <property name="card">
            <map>
                <entry key="电卡" value="123456789"/>
                <entry key="水卡" value="223456789"/>
                <entry key="燃气卡" value="000000000"/>
            </map>
        </property>
        <!--set类型的 games-->
        <property name="games">
            <set>
                <value>Fortnite</value>
                <value>OverWatch</value>
            </set>
        </property>
        <!--null类型的 friend-->
        <property name="friend" >
            <null/>
        </property>
        <!--Properties类型的 info-->
        <property name="info">
            <props>
                <prop key="性别"></prop>
                <prop key="电话">123654</prop>
            </props>
        </property>
     </bean>
</beans>

执行测试输出,不同类型的属性赋值均取到;

public class Test03 {
    public static void main(String[] args) {
        //读取applicationContext.xml配置文件;获取Spring的上下文对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取Student类的 bean;
        Student student = (Student) applicationContext.getBean("student");

        System.out.println(student.toString());
    }
}

在这里插入图片描述


3.3.2 (扩展) p命名set注入 ; c命名构造注入

p: 命名空间注入,可直接注入属性和值
注意在beans标签的头部需要导入这个xml约束

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

实体类User

public class User {
    private String name;
    private int age;
    
    public User() {
        System.out.println("无参构造方法");
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("有参构造方法");
    }
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

配置文件

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

    <!--创建User类的对象   p: 命名空间注入,可直接注入属性和值-->
    <bean id="user" class="com.xiaozhi.pojo.User" p:name="小智" p:age="21"/>
</beans>

getBean(“对象”,类型)
若在第二个参数处指明使用的类型,就不用强制转换类型了;

public class Test04 {
    public static void main(String[] args) {
        //读取applicationContext.xml配置文件;获取Spring的上下文对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext2.xml");
        //获取User类的 bean;
        User user =  applicationContext.getBean("user",User.class);

        System.out.println(user.toString());
    }
}

输出

无参构造方法
User{name='小智', age=21}

c: 命名空间注入,可直接注入构造方法的参数与值
注意在beans标签的头部需要导入这个xml约束

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

在配置文件中使用

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

    <!--可直接 使用构造方法的 参数-->
    <bean id="user1" class="com.xiaozhi.pojo.User" c:name="小智" c:age="21"/>

    <!--也可使用 构造方法的 参数的索引-->
    <bean id="user2" class="com.xiaozhi.pojo.User" c:_0="阿杰" c:_1="21"/>
</beans>

测试执行

public class Test04 {
    public static void main(String[] args) {
        //读取applicationContext.xml配置文件;获取Spring的上下文对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext2.xml");
        //获取User类的 bean;
        User user1 =  applicationContext.getBean("user1",User.class);
        User user2 =  applicationContext.getBean("user2",User.class);

        System.out.println(user1.toString());
        System.out.println(user2.toString());
    }
}

输出:

有参构造方法
有参构造方法
User{name='小智', age=21}
User{name='阿杰', age=21}

3.4 Bean的作用域

singleton(默认值) 单例模式:在 Spring 中只存在一个 bean 实例

即使不写也是默认存在的.

<bean id="user1" class="com.xiaozhi.pojo.User" c:name="小智" c:age="21" scope="singleton"/>

在这里插入图片描述

比如说,我在执行这边分别获取两次bean;两次取到到的bean是同一个

在这里插入图片描述

prototype:原型模式 getBean()的时候都会 new Bean()

<bean id="user1" class="com.xiaozhi.pojo.User" c:name="小智" c:age="21" scope="prototype"/>

在这里插入图片描述

还是刚才的执行例子;两次取到的对象不一致了.

在这里插入图片描述

request:每次 http 请求都会创建一个 bean, 仅用于 WebApplicationContext环境
session:同一个 http session 共享一个 Bean, 不同 Session 使用不同的 Bean, 使用环境同上


3.5 自动装配Bean

自动装配是Spring为满足bean依赖的一种方式;由Spring在上下文中自动寻找,自动装配bean.

有三种装配方式;
在xml显示的手动装配;
在java中显示地装配
隐式的自动装配

首先设计几个类

Cat类

public class Cat {
    public void shoot(){
        System.out.println("阿猫说喵喵");
    }
}

Dog类

public class Dog {
    public void shoot(){
        System.out.println("阿狗说汪汪");
    }
}

Person类

public class Person {
    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 "Person{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", 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-3.0.xsd">

    <!--配置Cat类和Dog类的bean-->
    <bean id="cat" class="com.xiaozhi.pojo.Cat"/>
    <bean id="dog" class="com.xiaozhi.pojo.Dog"/>

    <!--配置Person类的bean-->
    <bean id="person" class="com.xiaozhi.pojo.Person">
        <property name="name" value="小智"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>

</beans>

测试调用

public class Test04 {
    public static void main(String[] args) {
        //读取applicationContext.xml配置文件;获取Spring的上下文对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

        //获取User类的 bean;
        Person person = applicationContext.getBean("person", Person.class);
        person.getCat().shoot();
        person.getDog().shoot();
    }
}

输出

阿猫说喵喵
阿狗说汪汪

3.5.1 byName,byType自动装配

使用byName时,需要保证所有的bean 的id是唯一的,且bean是需要与自动注入的属性的set方法一致;

使用byType时,需要保证所有bean的class唯一; 且bean要和自动注入的属性的类型保持一致.

先看看byName

需要在applicationContext.xml中更改配置Person类的bean;

直接用autowire="byName" 属性
自动在容器的上下文中查找,与自己对象的set方法后的值对应的bean.

<!--配置Cat类和Dog类的bean-->
    <bean id="cat" class="com.xiaozhi.pojo.Cat"/>
    <bean id="dog" class="com.xiaozhi.pojo.Dog"/>
    
 <!--配置Person类的bean-->
<bean id="person" class="com.xiaozhi.pojo.Person" autowire="byName">
    <property name="name" value="小智"/>
</bean>

在这里插入图片描述

在执行后,依然可输出

在这里插入图片描述

但是,配置Cat类和Dog类的bean时,修改id;

<!--配置Cat类和Dog类的bean-->
<bean id="cat456" class="com.xiaozhi.pojo.Cat"/>
<bean id="dog666" class="com.xiaozhi.pojo.Dog"/>

<!--配置Person类的bean-->
<bean id="person" class="com.xiaozhi.pojo.Person" autowire="byName">
    <property name="name" value="小智"/>
</bean>

诶,执行时就出现异常了

在这里插入图片描述

这个说明什么呢;
只要配置的类的bean的id ; 和这个想要自动装配bean 的类中的set方法名字后面的名称一致,它就能自动装配bean了.


再看看 byType

和byName设置时差不多
需要在applicationContext.xml中更改配置Person类的bean;
设置autowire="byType"
自动在容器的上下文中查找;对应类的属性中的类型相同的bean

<!--配置Cat类和Dog类的bean-->
    <bean id="cat456" class="com.xiaozhi.pojo.Cat"/>
    <bean id="dog666" class="com.xiaozhi.pojo.Dog"/>
    
    <!--配置Person类的bean-->
    <!--byType 对应类的属性中的类型相同的bean-->
    <bean id="person" class="com.xiaozhi.pojo.Person" autowire="byType">
        <property name="name" value="小智"/>
    </bean>

虽然这里的id是和set方法后面的名称不一致的;但是仍然可以执行成功;

在这里插入图片描述

但是有个问题啊;你这边一旦创建多个bean;就不行了;
byType需要保证这里的类型要是唯一的

在这里插入图片描述

但是,用了byType后,你甚至可以把装配的bean的id给省略了

在这里插入图片描述


3.5.2 使用注解实现装配

需要导入相关xml配置约束;配置注解的支持

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

    <context:annotation-config/>

</beans>

配置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"
       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">

    <bean id="cat" class="com.xiaozhi.pojo.Cat"/>
    <bean id="dog" class="com.xiaozhi.pojo.Dog"/>
    <bean id="person" class="com.xiaozhi.pojo.Person"/>
    
    <!--注意开启注解支持-->
    <context:annotation-config/>

</beans>

直接在Person类的属性cat和dog处使用注解即可;@Autowired

@Autowired先执行byType

不过还是建议在setter方法上使用注解.

在这里插入图片描述

同样地装配成功了

在这里插入图片描述

扩展

(1) @Nullable 注解,在字段处标记后,该字段可以为null

(2) 仔细看看注解==@Autowired==;注意到后面是可以写参数required (默认为true)的;
要是显示地定义了 @Autowired(required = false) ,那么说明此字段的对象可以为null

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

(3) 如果xml文件中同一个对象被多个bean使用;使用@Autowired无法按类型找到;’

这时就需要使用注解 @Qualifier(value=" ") 配合注解@Autowired;指定唯一的bean对象注入

在这里插入图片描述

(4) 使用注解 @Resource ,java的原生注解; 它是根据class来找的.

@Resource先执行byName

如果xml文件中同一个对象被多个bean使用;@Resource还可以加参数 @Resource(name=" ")

在这里插入图片描述


Spring注解开发

spring后,先保证导入 aop的包
且注解有约束和支持

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

    <!--指定扫描的包-->
    <context:component-scan base-package="com.xiaozhi.pojo"/>
    <!--注意开启注解支持-->
    <context:annotation-config/>
</beans>

bean的注入

在类上使用注解 @Component 可以代替xml文件中的配置bean

比如说这里使用注解就相当于

<bean id="user" class="com.xiaozhi.pojo.User"/>

在这里插入图片描述

属性的注入
在属性的字段上使用注解 @Value(“赋值”) 即可

比如说这里使用了注解就相当于

<bean id="user" class="com.xiaozhi.pojo.User">
     <property name="name" value="小智"/>
     <property name="age" value="21"/>
</bean>

在这里插入图片描述

实际上; 注解 @Component 还有衍生的几个注解;
一般在dao层使用注解 @Repository ; 表示仓库;
service层使用注解 @Service ;
controller 层 使用 注解 @Controller

对于作用域还可使用注解 @Scope ;里面可传参value指定所用域范围.


3.5.3 使用JavaConfig实现配置

这部分不使用spring的配置文件;

之前仅作为spring的子项目,但在spring4之后是作为核心功能.

在一个类上使用注解 @Configuration ;该注解的底层已使用了注解@Component; 他也被spring接管了;表示该类为一个配置类

使用注解@ComponentScan("com.xiaozhi.pojo")配置包扫描;配置包扫描后,其实默认的id就是类名的小写了;

注解 @Bean 就相当于配置文件的bean标签;方法名相当于id;方法的返回值相当于class 属性;

注解 @Import(“其他配置文件类”) 可用来合并配置文件.

注意获取对象时,要先用AnnotationConfigApplicationContext获取上下文对象;

实体类User

//注解表名该类被spring接管;注入bean;
@Component
public class User {

    private String name;

    private int age;

    public User() {
    }

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

    public String getName() {
        return name;
    }

    //该注解为属性赋值;
    @Value("小智")
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @Value("21")
    public void setAge(int age) {
        this.age = age;
    }

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

配置类UserConfig

//该注解的底层已使用了注解@Component; 他也被spring接管了;
//表示该类为一个配置类
@Configuration
//配置包扫描;
@ComponentScan("com.xiaozhi.pojo")
public class UserConfig {


    //配置bean;
    //方法名相当于之前配置文件的bean标签的id 属性
    //方法的返回值相当于class 属性;
    @Bean
    public User getUser(){
        return new User();
    }
}

执行;

public class Test06 {
    public static void main(String[] args) {
        //用注解的方式获取的上下文对象
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);

        //获取对象;
        User user = applicationContext.getBean("getUser", User.class);

        System.out.println(user);

    }
}

可输出

User{name='小智', age=21}

4. 静态代理模式

代码模式原型

在这里插入图片描述

关于静态代理

抽象角色 = => 接口或 抽象类

真实角色 = => 被代理的角色

代理角色 = => 代理真实角色, 代理真实角色后,会做出附属操作

客户 = => 访问代理对象

(1)可以使真实角色的操作简单;不用去关注公共业务;而公共业务交给代理角色;实现了业务的分工;公共业务发生扩展时,方便集中管理.
(2)缺点: 一个真实角色就要产生一个代理角色;代码量翻倍;开发效率会变低.

第一个例子;面向中介去租房;
租房接口

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

房东实体类

//房东
public class Host implements Rent{

    public void rent() {
        System.out.println("房东出租房子");
    }
}

代理中介

//代理中介
public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

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

    public void rent() {
        seeHouse();
        host.rent();
        contract();
        fee();
    }

    //看房子;
    public void seeHouse(){
        System.out.println("由中介带着看房");
    }
    //收费;
    public void fee(){
        System.out.println("收中介费用");
    }
    //签合同
    public void contract(){
        System.out.println("和中介签合同");
    }
}

租房的客户

//租房的人 =,我
public class Me {
    public static void main(String[] args) {
        Host host=new Host();

        Proxy proxy = new Proxy(host);
        //直接面向代理中介;
        proxy.rent();
    }
}

直接执行客户租房

由中介带着看房
房东出租房子
和中介签合同
收中介费用

在这里插入图片描述

例子2:
比如说原有一个抽象业务接口
UserService

//抽象的业务;
public interface UserService {
    void addUser();
    void deleteUser();
    void updateUser();
    void findUser();
}

有他的实现类UserServiceImpl

//真实对象;
public class UserServiceImpl implements UserService{
    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }

    public void updateUser() {
        System.out.println("更新用户");
    }

    public void findUser() {
        System.out.println("查询用户");
    }
}

业务已经跑起来了,但是需要一个额外的扩展功能;这时就需要一个代理类;
UserServiceProxy

//代理人;  可扩展功能;并不影响原来的业务功能;
public class UserServiceProxy implements UserService{

    private  UserServiceImpl userService;

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

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

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

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

    public void findUser() {
        log("find查询");
        userService.findUser();
    }

    //模拟使用日志;
    public void log(String str){
        System.out.println("[模拟日志]=>使用的是"+str+"方法");
    }
}

那么用户在实现时,只要走代理类的功能;既保证了原有的业务功能,也有新的扩展功能;

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

        UserServiceImpl userService=new UserServiceImpl();

        //使用代理角色;
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(userService);
        userServiceProxy.addUser();
        userServiceProxy.deleteUser();
        userServiceProxy.updateUser();
        userServiceProxy.findUser();
    }
}

输出

[模拟日志]=>使用的是add添加方法
添加用户
[模拟日志]=>使用的是delete删除方法
删除用户
[模拟日志]=>使用的是update更新方法
更新用户
[模拟日志]=>使用的是find查询方法
查询用户

5. 动态代理模式

代理类是动态生成的,
基于接口是 -->JDK动态代理 ;
基于类–> cglib
java的字节码实现时–>javasist

动态 代理接口,对应一类业务.

java.lang.reflect
Interface InvocationHandler

是由代理实例的调用处理程序实现的接口 。

其中有一个invoke方法;

Object invoke(Object proxy, 方法 method, Object[ ] args)
处理代理实例上的方法调用并返回结果。

在这里插入图片描述

java.lang.reflect
Class Proxy

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

在这里插入图片描述

为某个接口创建代理FooInvocationHandler handler = new MyInvocationHandler(...);
     Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
     Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
                     newInstance(handler); 或更简单地: 
  Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class<?>[] { Foo.class },
                                          handler); 

在这里插入图片描述

案例;
还是刚才的userservice,userimpl案例;
但是代理类动态生成

//动态生成代理类;
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口;
    private Object object;

    public void setObject(Object object){
        this.object=object;
    }

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

    //处理代理实例,返回结果;
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        //动态代理本质,用反射实现;
        Object res=method.invoke(object,args);
        return res;
    }

    //模拟日志的方法;
    public void log(String str){
        System.out.println("[模拟日志]=>使用的是"+str+"方法");
    }
}

在用户调用时;

public class Me {
    public static void main(String[] args) {
        //真实的角色;  UserServiceImpl
        UserServiceImpl userService=new UserServiceImpl();

        //并不存在的代理角色;
        ProxyInvocationHandler pI=new ProxyInvocationHandler();
        //设置需要代理的对象;
        pI.setObject(userService);
        //动态得到代理类;
        UserService proxy = (UserService) pI.findProxy();

        proxy.addUser();
        proxy.findUser();
        proxy.deleteUser();
        proxy.updateUser();
    }
}

输出

[模拟日志]=>使用的是addUser方法
添加用户
[模拟日志]=>使用的是findUser方法
查询用户
[模拟日志]=>使用的是deleteUser方法
删除用户
[模拟日志]=>使用的是updateUser方法
更新用户

6.面向切面 AOP

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

在这里插入图片描述

在这里插入图片描述

使用spring实现AOP,先用maven导包

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

方式1:使用原生的Spring API接口

创建UserService接口

//抽象的业务;
public interface UserService {
    void addUser();
    void deleteUser();
    void updateUser();
    void findUser();
}

实现类UserServiceImpl

//真实对象;
public class UserServiceImpl implements UserService{
    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }

    public void updateUser() {
        System.out.println("更新用户");
    }

    public void findUser() {
        System.out.println("查询用户");
    }
}

模拟前置日志BeforeLog

//模拟前置日志;
public class BeforeLog implements MethodBeforeAdvice {
    /**
     *
     * @param method  要执行的目标对象方法;
     * @param objects 参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行");
    }
}

模拟后置日志

//模拟后置日志;
public class AfterLog implements AfterReturningAdvice {
    /**
     *
     * @param returnValue 返回值
     * @param method  要执行的目标对象方法;
     * @param objects 参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(method.getName()+"执行后,返回"+returnValue);
    }
}

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="userServiceImpl" class="com.xiaozhi.service.UserServiceImpl"/>
   <bean id="beforeLog" class="com.xiaozhi.log.BeforeLog"/>
   <bean id="afterLog" class="com.xiaozhi.log.AfterLog"/>

    <!--方式1   原生api接口-->
    <!--配置aop-->
    <aop:config>
        <!--切入点    要执行的位置 execution(返回值 类名 方法名)
                     (..)两个点代表可以有任意个参数    -->
        <aop:pointcut id="point" expression="execution(* com.xiaozhi.service.UserServiceImpl.*(..))"/>

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

执行输出

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

        //获取bean;

        //动态代理的是接口;
        UserService userService = context.getBean("userServiceImpl", UserService.class);
        //调用方法;
        userService.addUser();
        userService.deleteUser();
        userService.findUser();
        userService.updateUser();
    }
}

输出

com.xiaozhi.service.UserServiceImpl的addUser方法被执行
添加用户
addUser执行后,返回null
com.xiaozhi.service.UserServiceImpl的deleteUser方法被执行
删除用户
deleteUser执行后,返回null
com.xiaozhi.service.UserServiceImpl的findUser方法被执行
查询用户
findUser执行后,返回null
com.xiaozhi.service.UserServiceImpl的updateUser方法被执行
更新用户
updateUser执行后,返回null

方式 2;使用自定义 实现

不用去实现接口,直接自定义类MyLog

public class MyLog {
    public void beforeLog(){
        System.out.println("========模拟执行前置========");
    }
    public void afterLog(){
        System.out.println("========模拟执行后置========");
    }
}

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="userServiceImpl" class="com.xiaozhi.service.UserServiceImpl"/>
    <bean id="myLog" class="com.xiaozhi.mydiy.MyLog"/>
    <!--方式2;使用自定义的类-->
    <!--配置aop-->
    <aop:config>
        <!--使用自定义的切面-->
        <aop:aspect ref="myLog">
            <!--配置切入点-->
            <aop:pointcut id="point" expression="execution(* com.xiaozhi.service.UserServiceImpl.*(..))"/>
            <!--配置方法-->
            <aop:before method="beforeLog" pointcut-ref="point"/>
            <aop:after method="afterLog" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    
</beans>

测试执行;同样可执行

========模拟执行前置========
添加用户
========模拟执行后置========
========模拟执行前置========
删除用户
========模拟执行后置========
========模拟执行前置========
查询用户
========模拟执行后置========
========模拟执行前置========
更新用户
========模拟执行后置========

方式3: 使用注解实现

自定义的类,用注解标注为切面

//使用注解实现;
//@Aspect 标注该类是个切面;
@Aspect
public class MyAnnotation {

    //注意使用的是 import org.aspectj.lang.annotation.Before;包下的
    @Before("execution(* com.xiaozhi.service.UserServiceImpl.*(..))")
    public void beforeLog(){
        System.out.println("========注解方式模拟执行前置========");
    }

    //import org.aspectj.lang.annotation.After;
    @After("execution(* com.xiaozhi.service.UserServiceImpl.*(..))")
    public void afterLog(){
        System.out.println("========注解方式模拟执行后置========");
    }

    //在环绕增强时,可给定参数,表示获取处理切入的点
    @Around("execution(* com.xiaozhi.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("在环绕之前");
        Signature signature = pjp.getSignature();
        System.out.println("获取信息=>"+signature);
        Object proceed = pjp.proceed();
        System.out.println("执行方法=>"+proceed);
        System.out.println("环绕后");
    }
}

在配置文件中,只需要加注解支持即可;

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
       
    <!--方式三  使用注解-->
    <!--注册bean-->
    <bean id="userServiceImpl" class="com.xiaozhi.service.UserServiceImpl"/>
    <bean id="myAnnotation" class="com.xiaozhi.annotation.MyAnnotation"/>

    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

</beans>

执行也可实现

在环绕之前
获取信息=>void com.xiaozhi.service.UserService.addUser()
========注解方式模拟执行前置========
添加用户
========注解方式模拟执行后置========
执行方法=>null
环绕后
在环绕之前
获取信息=>void com.xiaozhi.service.UserService.deleteUser()
========注解方式模拟执行前置========
删除用户
========注解方式模拟执行后置========
执行方法=>null
环绕后
在环绕之前
获取信息=>void com.xiaozhi.service.UserService.findUser()
========注解方式模拟执行前置========
查询用户
========注解方式模拟执行后置========
执行方法=>null
环绕后
在环绕之前
获取信息=>void com.xiaozhi.service.UserService.updateUser()
========注解方式模拟执行前置========
更新用户
========注解方式模拟执行后置========
执行方法=>null
环绕后

7. 整合Mybatis

官方文档mybatis-spring整合文档

https://mybatis.org/spring/zh/index.html

需要用到的包

<dependencies>
        <!--测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--数据库驱动连接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <!-- spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--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整合-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>

先回顾一下Mybatis;

创建

注意需要在pom.xml配置文件中配置资源指向地址

<!--为防止资源导出失败;在build中需要配置resources-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

创建工具类MybatisUtils;

public class MybatisUtils {
    private  static SqlSessionFactory sqlSessionFactory=null;
    //在调用工具类时就执行;
    static {
        try {
            //获取SqlSessionFactory对象;

            //获得配置文件;
            String resource = "mybatis-config.xml";
            //使用流读取资源;
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //加载资源流;
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //从 SqlSessionFactory 中获取 SqlSession;
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }
}

实体类User

//实体类User;
//使用lombok插件的注解;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

持久层接口UserMapper

public interface UserMapper {
    //查询方法;
    public  List<User> findUser();
}

持久层配置文件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">
<!--这里mapper的空间对应创建的持久层接口;-->
<mapper namespace="com.xiaozhi.dao.UserMapper">


    <select id="findUser" resultType="user">
        select * from user;
    </select>

</mapper>

核心配置文件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>
    <!--引入外部配置文件-->
    <properties resource="db.properties"/>
    <!--设置标准日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--使用包扫描的方式 ;实体类的别名默认为首字母小写的类名-->
    <typeAliases>
        <package name="com.xiaozhi.pojo"/>
    </typeAliases>

    <!--environments:配置的环境,可以配置多个-->
    <environments default="development">
        <environment id="development">
            <!--transactionManager:事务管理;这里默认使用JDBC-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--加载连接数据库的重要字段属性-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--绑定接口-->
    <mappers>
        <mapper resource="com/xiaozhi/dao/UserMapper.xml"/>
    </mappers>
</configuration>

与数据库交互的属性文件db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day2021_9_6_studyMybatis_db?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=Asia/Shanghai
username=root
password=123456

测试执行搭建环境

public class Test09 {
    @Test
    public void testFindUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //getmapper;
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = userMapper.findUser();
        //遍历结果;
        for (User user : allUser) {
            System.out.println(user);
        }
        //关闭sqlSession;
        sqlSession.close();
    }
}

测试成功;

在这里插入图片描述


7.1Spring整合 Mybatis的方式一

新建UserMapper接口的实现类UserMapperImpl

public class UserMapperImpl implements UserMapper{

    //操作是用sqlSession执行的;
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> findUser() {
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        return userMapper.findUser();
    }
}

直接新建spring-dao.xml配置文件
编写数据源配置;sqlSessionFactory配置;SqlSessionTemplate配置;将实现类UserMapperImpl的bean也注入;

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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">

    <!--使用Spring的数据源替换mybatis-->

    <bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/day2021_9_6_studyMybatis_db?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- sqlSessionFactory配置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--绑定Mybatis 配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/xiaozhi/dao/UserMapper.xml"/>
        <property name="typeAliases" value="com.xiaozhi.pojo.User"/>
    </bean>

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

    <!--将实现类的bean注册进来-->
    <bean id="userMapper" class="com.xiaozhi.dao.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
     </bean>
</beans>

这么一来,工具类MybatisUtils可直接省略;
且Mybatis的核心配置文件mybatis-config.xml也不用去写太多的配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--设置标准日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

测试执行

//整合Mybatis后;
    @Test
    public void testFindUser2(){
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
       //获取bean
       UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> user = userMapper.findUser();
        for (User user1 : user) {
            System.out.println(user1);
        }
    }

输出

在这里插入图片描述


7.2 Spring整合 Mybatis的方式二

在写UserMapper接口的实现类UserMapperImpl时,可继承一个类SqlSessionDaoSupport;用来提供 SqlSession ;调用 getSqlSession()方法即可得到SqlSessionTemplate
省略了手动注入SqlSession

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    public List<User> findUser() {
        SqlSession sqlSession = getSqlSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        return userMapper.findUser();
    }
}

且在配置bean时,用ref引用指向 赋值即可

<bean id="userMapper" class="com.xiaozhi.dao.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

执行测试,查询成功

在这里插入图片描述


8.声明式事务

在这里插入图片描述

UserMapper接口再添加几个方法

public interface UserMapper {
    //查询方法;
    public  List<User> findUser();

    //添加用户
    public  void addUser(User user);

    //删除用户
    public void deleteUser(int id);
}

UserMapper.xml中编写sql语句

<?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">
<!--这里mapper的空间对应创建的持久层接口;-->
<mapper namespace="com.xiaozhi.dao.UserMapper">


    <!--查询用户-->
    <select id="findUser" resultType="user">
        select * from user;
    </select>

    <!--添加用户-->
    <insert id="addUser" parameterType="user">
        insert into user values (#{id},#{name},#{password});
    </insert>

    <!--删除用户-->
    <delete id="deleteUser" parameterType="_int">
      delete from user where id=#{id}
    </delete>
</mapper>

UserMapper接口的实现类UserMapperImpl

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{

    public List<User> findUser() {
        UserMapper userMapper=getSqlSession().getMapper(UserMapper.class);

        User user=new User(16,"石榴号","dsaasd");
        //添加用户;
        userMapper.addUser(user);
        //删除用户;
        userMapper.deleteUser(16);
        return userMapper.findUser();
    }

    public void addUser(User user) {
        getSqlSession().getMapper(UserMapper.class);
    }

    public void deleteUser(int id) {
        getSqlSession().getMapper(UserMapper.class);
    }
}

Mybatis的核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--设置标准日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

sping-dao.xml配置文件;
编写数据源配置;sqlSessionFactory配置;SqlSessionTemplate配置;配置事务;

<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--使用Spring的数据源替换mybatis-->

    <bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/day2021_9_6_studyMybatis_db?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- sqlSessionFactory配置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--绑定Mybatis 配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/xiaozhi/dao/UserMapper.xml"/>
        <property name="typeAliases" value="com.xiaozhi.pojo.User"/>
    </bean>

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

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

    <!--事务置入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--为方法配置事务-->
        <!--配置事务的特性;默认为 propagation="REQUIRED"-->
        <tx:attributes>
            <tx:method name="findUser" propagation="REQUIRED"/>
            <tx:method name="addUser" propagation="REQUIRED"/>
            <tx:method name="deleteUser" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="point" expression="execution(* com.xiaozhi.dao.*.*(..))"/>
        <aop:advisor pointcut-ref="point" advice-ref="txAdvice"/>
    </aop:config>
    
</beans>

还可以用一个spring容器的xml文件来合并其他的配置文件
applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <!--将pring-dao.xml 的配置合并过来-->
    <import resource="spring-dao.xml" />
    
    <!--将持久层的实现类的bean注册进来-->
    <bean id="userMapper" class="com.xiaozhi.dao.UserMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>

测试执行

public class Test10 {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> user = userMapper.findUser();
        for (User user1 : user) {
            System.out.println(user1);
        }
    }
}

一旦某个执行出现错误;不会将事务提交.



点击全文阅读


本文链接:http://zhangshiyu.com/post/32105.html

注解  方法  执行  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1