Spring、spring整合mybatis

在这里插入图片描述
spring官方文档

spring官网

spring组成七大模块:
在这里插入图片描述
1.为了使用spring,需要导入依赖。下面这些,只需要导入Spring-Web-MVC这个包,它就会自动导入其他的核心、上下文等相关依赖包。
在这里插入图片描述

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

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



在这里插入图片描述
下面就可以使用spring了。

一.第一个spring项目
1.spring的简单使用
建立一个实体类:有一个属性str

package com.han.pojo;

public class Hello {

    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

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

spring中所有的类都是要注入容器中的,由spring托管、创建、装配bean,不再由开发人员去new一个对象,这就是IOC(控制反转)。这里先使用配置文件的方式。在resource下面新建一个配置文件beans.xml(即ApplicationContext .xml):

id是每个bean的唯一标识符,也是创建的对象hello,class是对象类型Hello。
property是bean里面的属性,name是属性名str,value是给这个属性赋初始值。
基本类型使用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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="hello" class="com.han.pojo.Hello">
       <property name="str" value="Spring"></property>
   </bean>

</beans>

对于基本类型属性,通过value直接赋值。如果此时有一个实体类Person,而在Hello类里面,有一个私有属性:private Person person。对person属性,需要通过ref:

  <bean id="person" class="com.han.pojo.Person"/>
  
  <bean id="hello" class="com.han.pojo.Hello">
       <property name="str" value="Spring"></property>
       <property name="person" ref="person"></property>
   </bean>

建立测试类
使用配置文件,这里获取上下文的方式就固定为(获取配置文件):
ApplicationContext context=new ClassPathXmlApplicationContext(“beans.xml”);

import com.han.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public static void main(String[] args) {
    
        ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
        Hello hello=(Hello) context.getBean("hello");
        
        System.out.println(hello.toString());
    }
}

注意:上面在创建hello实例时,做了一次类型强制转为Hello类型。如果不想强制转换,可以使用反射:

    Hello hello= context.getBean("hello",Hello.class);

在这里插入图片描述
注:
在spring中,对象是由spring创建的,对象的属性是由spring赋值的,hello对象的属性str注入spring是通过str的set()方法,所以在Hello实体类里面属性必须要有其对应set()方法。

在spring中,对象hello的创建默认是通过实体类Hello的无参构造创建的,在加载配置文件之前,对象就通过无参构造创建好了。如果实体类中没有无参构造,则对象无法被创建。

二.spring对有参构造的属性赋值

假设此时实体类中只有有参构造,它覆盖了默认存在的无参构造。spring提供了几种使用有参构造给属性赋值的方式(IOC创建对象的三种方式)。再新建一个实体类,它只有一个有参构造:

package com.han.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;
    }

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

此时bean注入和上面一样,只是属性赋值发生了改变。
第一种,通过下表index赋值:

 <bean id="user" class="com.han.pojo.User">
        <constructor-arg index="0" value="张三"/>
    </bean>

第二种,通过类型赋值,但是如果实体类有多个String类型的属性,就不能使用这种方法了,不推荐使用这种方式:

    <bean id="user" class="com.han.pojo.User">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

第三种,通过属性名赋值

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

测试

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

在这里插入图片描述
三.spring中bean的作用域

下面再测试这样的一段代码(bean作用域):
同一个bean,取出两个对象,发现它们是相等的。

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

        User user1=context.getBean("user", User.class);
        User user2=context.getBean("user",User.class);
        System.out.println(user1 == user2);
        
    }
}

在这里插入图片描述
这是因为spring默认bean的模式为单例模式(scope=“singleton”),这两个对象是在内存中是同一个实例。

    <bean id="user" class="com.han.pojo.User"  scope="singleton">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

如果改为原型模式(scope=“prototype”)

    <bean id="user" class="com.han.pojo.User"  scope="prototype">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

此时测试user1==user2的结果就是false了,每一个创建的对象都是独立的。

下面介绍spring的简单配置

四.spring中bean设置别名的方式

别名Alias(只能设置一个别名)

    <bean id="user" class="com.han.pojo.User"  scope="singleton">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

对于这个bean,user。可以通过alisa起一个user1

    <bean id="user" class="com.han.pojo.User"  scope="singleton">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

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

或者直接通过name设置一个或多个别名,别名之间可以通过空格、逗号、分号等分隔( name=“user1 user2,user3;user4”)

    <bean id="user" class="com.han.pojo.User"  name="user1 user2,user3;user4">
        <constructor-arg type="java.lang.String" value="张三"/>
    </bean>

测试类中就可以使用user1这个别名获取bean

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

        User user=context.getBean("user1", User.class);
        
        System.out.println(user.toString());
    }
}

五.spring中多配置文件的合并

import(多个配置文件合并,用于协作开发)
在这里插入图片描述

假设现在项目由两个人开发,产生了两个配置文件,beans.xml和beans2.xml:
上下文可以读取多个配置文件

   ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml","beans2.xml");

但一般不会这么做,而是合并到总的配置文件ApplicationContext 中:

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

注意:import对于配置文件的合并,会将不同配置文件中相同的代码合并,只保留一份。

六.spring的依赖注入

接下来,说一下spring依赖注入的三种方式

构造器注入

在之前的开发中,dao-daoImpl-service-serviceImpl

package com.han.pojo;
public interface UserDao {
    void getUser();
}
package com.han.pojo;
public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("默认获取用户数据");
    }
}
package com.han.service;
public interface UserService {
    void getUser();
}
package com.han.service;
import com.han.pojo.UserDao;
import com.han.pojo.UserDaoImpl;
public class UserServiceImpl implements UserService{
    private UserDao userDao=new UserDaoImpl();
    public void getUser() {
        userDao.getUser();
    }
}

    @Test
    public  void testDependency(){
        UserService userService=new UserServiceImpl();
        userService.getUser();
    }

在这里插入图片描述
没啥问题!但现在想要增加一个实现类

package com.han.pojo;
public class MysqlUserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("mysql获取用户数据");
    }
}

则service层的实现类就要修改

package com.han.service;
import com.han.pojo.UserDao;
import com.han.pojo.UserDaoImpl;
public class UserServiceImpl implements UserService{

    private UserDao userDao=new MysqlUserDaoImpl();
    
    public void getUser() {
        userDao.getUser();
    }
}

所以每次增加一个实现类,service层实现类都必须修改

    private UserDao userDao=new XXXUserDaoImpl();

现在,设置一个构造器,将上面的一句代码拆分为

    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

测试类就变成这样了。

  @Test
    public  void testDependency(){
        UserService userService=new UserServiceImpl();
        
        ((UserServiceImpl)userService).setUserDao(new MysqlUserDaoImpl());
        
        userService.getUser();
    }

只需要每次通过set设置一个实现类方式即可,service层无需更改代码。控制权从程序员手中到了用户手中,这也是前面说的控制反转IOC的原型。在spring中实现控制反转的是IOC容器,实现方式是依赖注入。
但这里还是new了一个对象UserService userService=new UserServiceImpl();。现在将类注入spring容器

    <bean id="mysqlImpl" class="com.han.pojo.MysqlUserDaoImpl"/>
    
    <bean id="UserServiceImpl" class="com.han.service.UserServiceImpl">
        <property name="userDao" ref="mysqlImpl"/>
    </bean>

这是测试类就不需要再new UserServiceImpl这个对象,而是上下文获取

  @Test
    public  void testDependency(){
        ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserServiceImpl userService=context.getBean("UserServiceImpl",UserServiceImpl.class);
        userService.getUser();
    }

在这种方式下,想要更改实现类,只需要在配置文件中修改ref的值<property name="userDao" ref="XXXImpl"/>

set注入
bean对象的创建依赖于容器,对象的属性由容器注入。
下面创建实体类Student,测试多种数据类型的属性注入:

package com.han.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 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 +
                '}';
    }
}

Address实体类注入到spring容器

//引用类型内部的属性,要在自己内部完成值的注入
 <bean id="address" class="com.han.pojo.Address">
        <property name="address" value="地球"/>
 </bean>

Student这个Bean的属性值注入:

 <bean id="address" class="com.han.pojo.Address">
        <property name="address" value="地球"/>
    </bean>

    <bean id="student" class="com.han.pojo.Student">

        <property name="name" value="张三"/>        //1、基本类型

         <property name="address" ref="address"/>     //2、引用类型

        <property name="books">            //3、数组
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>

        <property name="hobbys">     //4、集合list
            <list>
                <value>唱歌</value>
                <value>看电影</value>
                <value>打游戏</value>
            </list>
        </property>

        <property name="card">          //4、万能map,key-value键值对
            <map>
                <entry key="身份证" value="123456789"/>
                <entry key="银行卡" value="778480569"/>
            </map>
        </property>

        <property name="games">     //4、集合set
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>

<!--        <property name="wife" value=""/>-->     //设置空串,注意和null的区别

        <property name="wife">    //5、null值
            <null></null>
        </property>

        <property name="info">     //6、配置信息
            <props>
                <prop key="学号">2016211857</prop>
                <prop key="姓名">张三</prop>
                <prop key="性别">男</prop>
            </props>
        </property>

    </bean>

测试

    @Test
    public void SetTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        Student student = context.getBean("student", Student.class);
        System.out.println(student.toString());
    }

在这里插入图片描述

拓展方式注入(p名空间、c命名空间)

建立Teacher实体类用于测试

package com.han.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;
    }

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

为了代码简洁,新建一个配置文件CPnameSpace.xml,并导入到ApplicationContext.xml中,因为我们在测试类中,上下文读取的是总配置文件。

 <import resource="CPnameSpace.xml"/>

下面在CPnameSpace.xml中,导入CP命名空间的支持(必须先导入支持才能使用)

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

注入Teacher这个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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">


</beans>

之前的写法:

   <bean id="teacher" class="com.han.pojo.Teacher">
        <property name="name" value="张三"/>
        <property name="age " value="20"/>
    </bean>

使用p命名空间:

  <bean id="teacher" class="com.han.pojo.Teacher" p:name="张三" p:age="20" />

测试

 @Test
    public void PTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        Teacher teacher= context.getBean("teacher", Teacher.class);
        System.out.println(teacher);
    }

在这里插入图片描述
使用c命名空间
c命名空间,对应构造器注入,所以必须给Teacher实体类加上有参构造;而有参构造会覆盖无参构造,这样p命名空间对应set注入又会报错,所以需要同时加上有参和无参构造。

  public Teacher() {
  
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }
   <bean id="teacher2" class="com.han.pojo.Teacher" c:name="李四" c:age="22"/>

测试


    @Test
    public void CTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        Teacher teacher= context.getBean("teacher2", Teacher.class);
        System.out.println(teacher);
    }

在这里插入图片描述
七.Bean的自动装配—使用配置文件(重要!)

创建测试实体类

package com.han.pojo;
public class Cat {
   public void shout(){
        System.out.println("miao~");
    }
}
package com.han.pojo;
public class Dog {
   public void shout(){
        System.out.println("wang~");
    }
}
package com.han.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 "Person{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}

创建配置文件

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

    <bean id="cat" class="com.han.pojo.Cat"/>
    
    <bean id="dog" class="com.han.pojo.Dog"/>
    
    <bean id="people" class="com.han.pojo.People">
        <property name="name" value="张三"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
    
</beans>

测试

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

在这里插入图片描述
到此,和之前的步骤都是一样的。现在使用ByName的自动装配方式。只给people这个bean注入name的属性,cat和dog的属性通过自动装配(autowire=“byName”):

 <bean id="people" class="com.han.pojo.People"  autowire="byName">
        <property name="name" value="张三"/>
 </bean>

byName:实体类People有两个属性cat和dog,对应的set方法setDog和setCat。People这个bean在装配的时候,在上文中寻找和自己的set方法后面的值相同的bean(dog、cat),不好理解。也就是寻找和自己的这两个属性同名的id对应的bean。
所以,如果这里的id不是cat或者dog,就无法被装配。

  <bean id="cat1" class="com.han.pojo.Cat"/>
   <bean id="dog1" class="com.han.pojo.Dog"/>

下面这一中方式,通过类型自动装配( autowire=“byType”

 <bean id="people" class="com.han.pojo.People"  autowire="byType">
        <property name="name" value="张三"/>
 </bean>

byType:实体类People有两个属性cat和dog,它们是Cat和Dog类型。因此在这种自动装配方式下,会在上下文寻找class为Cat或者Dog的bean。
所以,如果此时同时存在两个class类型相同的bean,就无法自动装配。

 <bean id="dog" class="com.han.pojo.Dog"/>
  <bean id="dog1" class="com.han.pojo.Dog"/>

也就是说,byType必须保证bean的类型全局唯一。在byType的方式下,bean的id可以省略。

八.Bean的自动装配—使用注解(重要!)

要使用注解,需要导入context约束和aop支持

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

还要配置注解的支持

    <context:annotation-config/>

也就是下面这样:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       
       xmlns:context="http://www.springframework.org/schema/context"
       
       xmlns:aop="http://www.springframework.org/schema/aop"
       
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">


        <context:annotation-config/>
        
       <bean id="cat" class="com.han.pojo.Cat"/>
       <bean id="dog" class="com.han.pojo.Dog"/>
       <bean id="people" class="com.han.pojo.People"/>

</beans>

此时,这三个bean的注入就是这样了

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

最后在People中使用@Autowired注解即可

   @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;

这种方式,要求bean的id必须对应实体类的属性名,和byName相同。Autowired注解可以使用在属性或者set方法上使用,使用Autowired可以不写set方法,前提是自动装配的属性在spring容器中存在,且id符合byName

    @Autowired(required = false)
    private Cat cat;

Autowired注解可以加上required属性,为false表明这个对象可以为NULL,默认为true,不能为空。

public People(@Nullable String name) {
        this.name = name;
    }

也可以给属性加上@Nullable注解,此时属性为空也不会报空指针异常。

Autowired注解需要遵守byName,现在所有的bean的id都无法匹配到,此时可以结合注解@Autowired的value指定到一个bean,让value=这个bean的id即可自动装配。

     <bean id="cat1" class="com.han.pojo.Cat"/>
     <bean id="cat2" class="com.han.pojo.Cat"/>
     <bean id="dog1" class="com.han.pojo.Dog"/>
     <bean id="dog2" class="com.han.pojo.Dog"/>
    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;
    
    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;

一般Autowired和Qualifier配合使用。

还可以通过javax.annotation的原生注解实现自动装配@Resource

    @Resource
    private Cat cat;

    @Resource
    private Dog dog;

Resource会先根据id进行上下文查找

     <bean id="cat" class="com.han.pojo.Cat"/>
     <bean id="cat2" class="com.han.pojo.Cat"/>
     
     <bean id="dog" class="com.han.pojo.Dog"/>
     <bean id="dog2" class="com.han.pojo.Dog"/>

只要有一个id对应的上(cat、dog),就能自动装配。如果id没有一个可以匹配

    <bean id="cat1" class="com.han.pojo.Cat"/>
     <bean id="cat2" class="com.han.pojo.Cat"/>
     
     <bean id="dog1" class="com.han.pojo.Dog"/>
     <bean id="dog2" class="com.han.pojo.Dog"/>

Resource就会根据class类型去寻找bean,此时要求类型要全局唯一,遵守byType。除此之外,Resource也支持加上name属性,对应唯一bena。
此时id无法对应:

    <bean id="cat1" class="com.han.pojo.Cat"/>
     <bean id="cat2" class="com.han.pojo.Cat"/>
     
     <bean id="dog1" class="com.han.pojo.Dog"/>
     <bean id="dog2" class="com.han.pojo.Dog"/>

但是指定了name属性:

    @Resource(name="cat1")
    private Cat cat;

    @Resource(name="dog1")
    private Dog dog;

这样也能实现自动装配。
@Autowired默认使用byName自动装配,且要求属性在容器中存在;
@Resource默认先使用byName,找不到再根据byType装配,都找不到才会报错。

九.spring使用注解开发

使用注解开发需要注意(参见8):
必须导入aop这个依赖包;
在这里插入图片描述

必须增加注解的驱动和支持;

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

        <context:annotation-config/>

</beans>

还有重要的一步要做,我们在每个bean中写的注解,得需要spring扫描后才会生效,通过包扫描的方式(至于扫描哪个层级的包需要视情况而定),这些注解是spring特有的注解,不能像@Autowired那样直接使用,需要spring扫描:

  <context:component-scan base-package="com.han.pojo"/>

至此,就可以使用注解开发了。
建立一个实体类User

package com.han.pojo;
public class User {
    public String name="张三";
}

这是一个User的bean,按照之前的做法,我们需要在配置文件中注入这个bean
<bean id="user" class="com.han.pojo.User"/>
但现在我们使用注解@Component加在User这个bean上:

package com.han.pojo;
import org.springframework.stereotype.Component;
@Component
public class User {
    public String name="张三";
}

只需要在User这个bean上面加上@Component注解,就已经将这个bean注入到了spring容器中,不需要再使用bean标签显式的注册。

结论:@Component 等价于 <bean id="user" class="com.han.pojo.User"/>

测试:
注意,因为不再使用原来的bean标签注入容器,也就不存在id了。在通过context获取bean的时候,这里的"user"是实体类User的小写。也就是说,使用注解开发,实体类的小写就是默认的该实体类的bean的"id"。

public class AnnotationTest {
    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        User user=context.getBean("user",User.class);
        System.out.println(user.name);
    }
}

在这里插入图片描述
不适用bean注入容器,也就没有了id。此时bean的属性的值的注入

public String name="张三";

该例是直接通过java语言赋初始值。可以通过@Value注解为属性赋值:

@Component
public class User {
    @Value("张三")
    public String name;
}

此外,@Value也可以放在属性的set方法上面:

@Component
public class User {
    public String name;
    @Value("张三")
    public void setName(String name) {
        this.name = name;
    }
}

结论:@Value(“张三”)等价于<property name="name" value="张三"/>
对于一些简单的、个别的 属性通过@Value会非常方便,但如果属性非常多,当然建议使用配置为文件。

@Component有几个衍生注解,它们的本质还是@Component,只不过在分层开发模式下,代表特有的层级:
@Repository表示当前为dao层(mapper层)

@Repository
public class UserDao {
}

@Service表示当前为service层

@Service
public class UserService {
}

@Controller表示当前为controller层

@Controller
public class UserController {
}

注解开发下,bean作用域使用注解@Scope加在bean上,可以通过value属性为bean指定作用域:

@Component
@Scope(value = "singleton/prototype")
public class User {
    @Value("张三")
    public String name;
}

十.基于javaConfig实现配置

到目前位置,所有的例子都使用了配置文件,但支持完全不适用配置文件的全注解开发的,就是使用javaConfig实现配置,JavaConfig是spring的一个子项目。

新建一个实体类User
使用Component注解将这个bean注入到容器中,并给name属性赋初始值:

@Component
public class User {

    @Value("张三")
    private String name;

    public String getName() {
        return name;
    }

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

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

新建config文件夹,建立JavaConfig配置类(相当于之前的xml配置文件)

package com.han.config;

import com.han.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JavaConfig {
    @Bean
    public User getUser(){
        return new User();
    }
}

@Configuration:表示这个类是配置类,相当于xml配置文件,本质上还是@Component,是一个bean,会被spring接管到容器;
@Bean:相当于xml配置文件中的bean标签,会注册一个bean到容器中;
@getUser:方法名是bean的"id";
@User:返回值类型是bean的"class";
@return new User():返回值就是创建的对象;

测试:

public class JavaConfigTest {
    @Test
    public void test(){
        ApplicationContext context=new AnnotationConfigApplicationContext(JavaConfig.class);
        User user=context.getBean("getUser",User.class);
        System.out.println(user.getName());
    }
}

在这里插入图片描述

不再是之前的XML获取上下文ClassPathXmlApplicationContext:

  ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

而是annotation获取上下文AnnotationConfigApplicationContext
获取的也不再是一个配置文件ApplicationContext.xml,
而是上面写的配置类JavaConfig.class:

   ApplicationContext context=new AnnotationConfigApplicationContext(JavaConfig.class);

获取bean时,配置类中@Bean下的方法名getUser就是bean的"id";

如果需要扫描包,就在配置类中@Configuration这个注解下面加上@ComponentScan("com.han.pojo")

@Configuration
@ComponentScan("com.han.pojo")
public class JavaConfig {
    @Bean
    public User getUser(){
        return new User();
    }

}

我们知道,使用xml配置文件时,如果多个配置文件,可以通过import合并到总的配置文件中,上下文读取总的配置文件即可;现在使用注解开发,同样可以合并多个配置类到总的配置类中.通过@Import实现:
现在增加一个配置类,JavaConfigT

package com.han.config;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JavaConfigT {
  
}

通过@Import@Import(JavaConfigT.class)导入到总配置类里面;

@Configuration
@ComponentScan("com.han.pojo")

@Import(JavaConfigT.class)

public class JavaConfig {
    @Bean
    public User getUser(){
        return new User();
    }
}

十一.AOP—横切事务(重要!)

静态代理—>动态代理(proxy、InvocationHandler)—>Spring AOP

使用spring的AOP,需要先导入织入依赖包:

<!--        springAOP织入包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

建立业务模型和实现类:

package com.han.service;
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}
package com.han.service;

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 select() {
        System.out.println("查询一个用户");
    }
}

第一种方式,使用spring原生的API接口。
现在,UserService有增删改查四种业务,想要通过AOP向UserService中横向切入增加日志功能Log。
新建Log实体类(通过前置增强的方式切入):

package com.han.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
//method:要执行的目标对象的方法;
//args:参数;
//target:目标对象;
public class Log implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {

        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
        
    }
}

新建实体类AfterLog,通过后置增强的方式切入:

package com.han.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println("执行了"+method.getName()+",返回结果为"+returnValue);

    }
}

接下来,在ApplicationContext配置文件中,注入真实类UserService和两个日志增强类,并加入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
        https://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean id="userService" class="com.han.service.UserServiceImpl"/>
    <bean id="log" class="com.han.log.Log"/>
    <bean id="afterLog" class="com.han.log.AfterLog"/>

    <!-- 配置aop-->
    <aop:config>

        <!-- 切入点-->
           <!--  pointcut:切入点-->
           <!--  execution:  执行的位置-->
        <aop:pointcut id="pointcut" expression="execution(* com.han.service.UserServiceImpl.*(..))"/>

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

     </aop:config>

</beans>

第二种方式,自定义切面和切入点。
定义一个切面DiyPointCut :

package com.han.diy;

public class DiyPointCut {

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

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

配置aop:

 <bean id="diy" class="com.han.diy.DiyPointCut"/>

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

第三种方式,使用注解实现AOP
自定义一个切面annotationPointCut

package com.han.diy;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//@Component  //将切面注册到spring容器
@Aspect  //表明这是个切面
public class annotationPointCut {
    @Before("execution(* com.han.service.UserServiceImpl.*(..))") //注解设置前置增强及切入点
    public void before(){
        System.out.println("方法执行前");
    }
    @After("execution(* com.han.service.UserServiceImpl.*(..))")  //注解设置后置增强及切入点
    public void after(){
        System.out.println("方法执行后");
    }
}

在配置文件中加入支持

    <!--  将切面注入到spring容器,或者在切面中使用@Component注解-->
    <bean id="annotationPointCut" class="com.han.diy.annotationPointCut"/>
    
    <!--  使用注解的方式需要增加支持:自动代理-->
    <aop:aspectj-autoproxy/>

测试

public class AopTest {
    @Test
     public void test(){

        ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
          // 动态代理的是接口
        UserService userService=context.getBean("userService", UserService.class);
        userService.add();
    }
}

在这里插入图片描述
可以看到,虽然真实业务类中只执行了add方法,但由于横切织入了日志增强类,于是日志内容也输出了。

下面测试环绕增强Around(ProceedingJoinPoint jp)可以给方法指定一个参数,表明执行的具体方法:

  @Around("execution(* com.han.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("环绕前");
        Signature signature=jp.getSignature();//获得签名:执行的方法
        System.out.println(signature);

        Object proceed=jp.proceed();
        System.out.println("环绕后");

        System.out.println(proceed);
    }

在这里插入图片描述
可以看到,先执行环绕前,然后进入前置增强,结束环绕增强,最后进入后置增强。签名就是执行的方法。
以上就是AOP的三种实现方式,以及几种增强方式。

十二.spring整合mybatis
Mybatis能够无缝整合到srping中。spring整合mybatis需要导入两个依赖包

   <!--spring整合mybtis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>

spring整合mybatis后,数据源就使用spring的数据源,因此需要导入jdbc依赖:

 <!--srping-jdbc:使用spring的数据源代替mybatis的数据源,需要导入这个包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

上面这两个包,是spring整合mybatis必须要用到。整合需要的包如下:

 <dependencies>
 
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        
        <!--连接数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        
         <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        
         <!--srping-jdbc:使用spring的数据源代替mybatis的数据源,需要导入这个包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        
         <!--aop织入包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        
         <!--spring整合mybtis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        
         <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
        
    </dependencies>

此外,为了保证配置文件正常加载,需要导入spring静态资源过滤的配置:

 <!--静态资源过滤-->
    <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>

下面,不直接给出所有整合代码,而是看看spring是如何接管mybatis的。
建立实体类User

package com.han.pojo;
import lombok.Data;
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}

建立一个接口类Usermapper:

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

建立接口实现的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 namespace="com.han.mapper.UserMapper">

    <!--获取所有用户-->
    <select id="selectUser" resultType="User">
      select * from User;
    </select>

</mapper>

建立数据库信息配置文件db.properties:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root

建立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"/>

     <!--扫描实体类:接口xml配置文件中就可以不用写全路径-->
    <typeAliases>
        <package name="com.han.pojo"/>
    </typeAliases>
    
    <environments default="development">
        <environment id="development">
            <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 class="com.han.mapper.UserMapper"/>
    </mappers>

</configuration>

测试

public class MyTest {
    @Test
    public void test() throws IOException {
    
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession=sqlSessionFactory.openSession(true);

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

在这里插入图片描述
注意上面的例子是mybatis的使用,还没有牵扯到整合。
现在开始整合。
建立spring-dao.xml,这是spring整合mybatis的配置文件,也就是spring的配置文件。这个文件中,只配置三个东西:
1.数据源DateSource
2.SqlSessionFactory
3.创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 1.数据源DateSource:使用spring的数据源替换mybatis的配置-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

<!--2.SqlSessionFactory:获取与数据库的连接-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="datasource"/>
    <!--绑定mybatis核心配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <!--所有接口xml配置文件的注册都可以放在这里-->
    <property name="mapperLocations" value="classpath:com/han/mapper/*.xml"/>
</bean>

<!-- 3.创建sqlSessionTemplate对象:它就是mybatis里面的sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--sqlSessionFactory这个属性的注入只能通过构造器注入,因为没有set方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

</beans>

在这个配置文件中,spring来管理数据源,管理xml接口配置文件,创建和数据库的连接。于是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"/>-->

     <!--扫描实体类:接口xml配置文件中就可以不用写全路径-->
    <typeAliases>
        <package name="com.han.pojo"/>
    </typeAliases>

    <!--环境:spring接管数据源后,这部分就可以不要了-->
<!--    <environments default="development">-->
<!--        <environment id="development">-->
<!--            <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>-->

    <!--注册所有的接口配置文件:已经映射到了spring-dao.xml,可以省略-->
<!--    <mappers>-->
<!--        <mapper class="com.han.mapper.UserMapper"/>-->
<!--    </mappers>-->

</configuration>

但是,spring整合mybatis后,mybatis对于接口的实现,spring是无法做到,因此就每个XXXMapper.xml就需要建立对应的XXXMapperImpl实现类。这里需要给Usermapper接口创建对应的实现UsermapperImpl:

package com.han.mapper;

import com.han.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {

    //SqlSessionTemplate:就是sqlSessionFactory
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    // 获取所有用户
    public List<User> selectUser() {
        UserMapper mapper= sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

现在,需要把这个实现类注入到容器:
注意:这个实现类可以写在spring-dao.xml里面。但是,可以发现spring-dao.xml这个配置文件里面都是关于数据源的配置,如果放在这里面,每次新建实现类,都要注入在这里面,这是不合适的。spring的多配置文件import合并的作用就来了。新建ApplicationContext.xml这个总的配置文件,将spring-dao.xml导入进去,同时将每个实现类注入进去。
这样spring-dao.xml的内容就固定了,只放数据源的配置。而且后面再整合SpringMvc的时候,也可以将SpringMvc的配置文件也导入到总的配置文件。

<!--注入接口实现类-->
    <bean id="userMapper" class="com.han.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">


   <!--导入spring整合mybatis的配置文件-->
   <import resource="spring-dao.xml"/>



    <!--注入接口实现类-->
    <bean id="userMapper" class="com.han.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>


</beans>

到这里就完成了整合,下面测试,整合后就要使用spring的测试了:

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

//        Mybatis测试
//        String resource = "mybatis-config.xml";
//        InputStream inputStream = Resources.getResourceAsStream(resource);
//        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        SqlSession sqlSession=sqlSessionFactory.openSession(true);
//
//        UserMapper mapper=sqlSession.getMapper(UserMapper.class);
//        List<User> userList=mapper.selectUser();
//        for (User user : userList) {
//            System.out.println(user);
//        }


        //整合后,就要使用spring的测试
        ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
        UserMapper userMapper=context.getBean("userMapper",UserMapper.class);
        List<User> userList=userMapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

在这里插入图片描述
十三.Spring整合Myabtis的第二种方式

在第一种方式的基础上,稍作改动。
1、其他文件不变,将UserMapperImpl做如下修改:

原来的UserMapperImpl:

package com.han.mapper;

import com.han.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl implements UserMapper {

    //SqlSessionTemplate:就是sqlSessionFactory
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }
    // 获取所有用户
    public List<User> selectUser() {
        UserMapper mapper= sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

现在的UserMapperImpl:

package com.han.mapper;

import com.han.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

//整合Mybatis的第二种方式
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    // 获取所有用户
    public List<User> selectUser() {
        UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

通过继承SqlSessionDaoSupport类,省去下面的set方法和sqlSession的组合。

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

2、实现类注入到spring的改变
原来:

    <!--注入接口实现类-->
    <bean id="userMapper" class="com.han.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

现在:

    <!--注入接口实现类-->
    <bean id="userMapper" class="com.han.mapper.UserMapperImpl">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

完成。

十四.spring的声明式事务(AOP的方式)

事务ACID原则:要么都成功,要么都失败。

  • 原子性
  • 一致性
  • 隔离性
  • 持久性
    在原来的spring整合号mybatis的基础上,在userMapper的接口中增加两个方法:
public interface UserMapper {
    public List<User> selectUser();

    // 添加一个用户
    public int addUser(User user);
    //根据删除一个用户
    public int deleteUser(int id);
    
}

在UserMapper.xml中写入sql:

<mapper namespace="com.han.mapper.UserMapper">

    <!--获取所有用户-->
    <select id="selectUser" resultType="User">
      select * from User;
    </select>

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

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

</mapper>

在UserMapperImpl实现类中实现这两个方法,并在selectUser中做测试:

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    // 获取所有用户
    public List<User> selectUser() {
    
        User userT=new User(7,"王二麻子","1234321");
        
        UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
        
        mapper.addUser(userT);
        mapper.deleteUser(6);
        
        return mapper.selectUser();
    }

    @Override
    public int addUser(User user) {
        UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
        return mapper.addUser(user);
    }

    @Override
    public int deleteUser(int id) {
        UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
        return mapper.deleteUser(id);
    }
}

注意:这里故意将delete写成deletes,使程序发生错误。

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

测试:
在这里插入图片描述
程序报deletes的错误,但此时数据库还是成功插入了User userT=new User(7,"王二麻子","1234321");这条数据。
在这里插入图片描述
这是不符合事务ACID原则的。
下面采用声明式事务进行处理,使程序报错的情况下,既然无法删除,也无法插入。
即要么都成功,要么都失败。
在spring-dao.xml配置文件中加入aop事务的配置:

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

    <!--2.结合aop实现事务的织入-->
    <!--配置事务通知:需要先导入tx支持-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--propagation:配置事务的传播特性,默认是REQUIRED(如果当前没有事务,就新建一个事务);
                name="*":所有方法都支持事务;
                name="add":只对add这个方法支持事务-->
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--3.配置事务切入-->
    <aop:config>
        <!--execution(* com.han.mapper.*.*(..)):com.han.mapper这个包下的所有类的所有方法的所有参数,都切入事务-->
        <aop:pointcut id="txPointCut" expression="execution(* com.han.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

tx支持:

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


        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

再次测试,程序依然是deletes报错,但此时由于配置了声明式事务,User userT=new User(7,"王二麻子","1234321");这条数据并没有插入到数据库中。
在这里插入图片描述
将deletes改为delete:
删除id=6的用户,插入id=7的新用户

 public List<User> selectUser() {
        User userT=new User(7,"王二麻子","1234321");
        UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
        mapper.addUser(userT);
        mapper.deleteUser(6);
        return mapper.selectUser();
    }

在这里插入图片描述
成功,说明声明式事务已经在起作用了。

完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值