2_Spring

1、简介

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

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

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

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

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

Spring Web MVC » 5.2.5.RELEASE

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>
  • spring是开源的免费的容器。
  • spring是一个轻量级的,非入侵式的。
  • 控制反转(IOC),面向切面编程 (AOP)。
  • 支持事务处理,对框架整合的支持。

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

2、IOC理论

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

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

使用一个set:

public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}
  • 之前是主动创建对象,控制权在程序员手上。
  • 使用set之后,是被动接受对象。

3、Hello Spring

pojo中

package com.yunlbd.pojo;

public class Hello {

    private String name;

    public String getName() {
        return name;
    }

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

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

resource中

<?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 = 变量名-->
    <!--class = new的对象-->
    <!--property 相当于给对象中的属性设值-->

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

test

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

public class Mytest {

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

bean = 对象
id = 变量名
class = new的对象
property 相当于给对象中的属性设值

核心用set注入

第一个文件中

<?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="userdaomysql" class="com.yunlbd.dao.UserDaoMysqlImpl"></bean>

    <bean id="userServiceImpl" class="com.yunlbd.service.UserServiceImp">
        <!--ref引用spring中已经创建很好的对象-->
        <!--value是一个具体的值-->
        <property name="userDao" ref="userdaomysql"/>
    </bean>
</beans>

4、IOC创建对象的方式

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

下标赋值:

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

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

类型赋值(不建议使用):

<bean id="user" class="com.yunlbd.pojo.User">
    <constructor-arg type="java.lang.String" value="李四"/>
</bean>

直接通过参数名:

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

Spring类似于婚介网站!

你想不想要,对象都在里面。注册bean之后用不用都会被实例化。

5、Spring配置

别名:

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

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

Bean的配置:

  • id:bean的id标识符
  • class:bean对象所对应的类型
  • name:别名,更高级,可以同时取多个别名。

import:

一般用于团队开发,它可以将多个配置文件,导入合并为一个

<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

6、依赖注入(DI)

构造器注入:见上面第4点。

set方式注入(重点):

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

【环境搭建】

  1. 复杂类型
  2. 真实测试对象
package com.yunlbd.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String, String> card;
    private Set<String> game;
    private Properties infor;
    private String wife;

    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", address=" + address +
            ", books=" + Arrays.toString(books) +
            ", hobbies=" + hobbies +
            ", card=" + card +
            ", game=" + game +
            ", infor=" + infor +
            ", wife='" + wife + '\'' +
            '}';
    }
}
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 + '\'' +
            '}';
    }
}
<?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="address" class="com.yunlbd.pojo.Address">
        <property name="address" value="安徽合肥"></property>
    </bean>

    <bean id="student" class="com.yunlbd.pojo.Student">
        <property name="name" value="张三"/>
        <property name="address" ref="address"/>

        <!--数组注入-->
        <property name="books">
            <array>
                <value>三国</value>
                <value>西游</value>
                <value>水浒</value>
            </array>
        </property>

        <!--list-->
        <property name="hobbies">
            <list>
                <value>eat</value>
                <value>drink</value>
                <value>play</value>
            </list>
        </property>

        <property name="card">
            <map>
                <entry key="身份证" value="34125455254545451"/>
                <entry key="驾驶证" value="1545215415"/>
            </map>
        </property>

        <property name="game">
            <set>
                <value>wangzhe</value>
                <value>daota</value>
                <value>lol</value>
            </set>
        </property>

        <property name="wife">
            <null></null>
        </property>

        <!--properties-->
        <property name="infor">
            <props>
                <prop key="id">20200405</prop>
                <prop key="name">张三</prop>
            </props>
        </property>
    </bean>
</beans>

第三方:

p标签和c标签

package com.yunlbd.pojo;

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

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

    <!--p命名空间注入/set注入-->
    <bean id="use" class="com.yunlbd.pojo.User" p:name="zhang" p:age="10"></bean>

    <!--c命名空间/构造器-->
    <bean id="use2" class="com.yunlbd.pojo.User" c:name="wang" c:age="19"></bean>
</beans>

bean的作用域:

  1. 单例模式(默认)

    <bean id="use2" class="com.yunlbd.pojo.User" c:name="zhang" c:age="19" scope="singleton"></bean>
    
  2. 原型模式: 每次从容器中get的时候,都产生一个新对象!

    <bean id="use2" class="com.yunlbd.pojo.User" c:name="zhang" c:age="19" scope="prototype"></bean>
    
  3. 其余的request、session、application这些只能在web开放中使用!

7、Bean的自动装配

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

在Spring中有三种装配的方式:

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

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

  • Byname自动装配:byname会自动查找,和自己对象set对应的值对应的id

    保证所有id唯一,并且和set注入的值一致

  • Bytype自动装配:byType会自动查找,和自己对象属性相同的bean

    保证所有的class唯一

public class Cat {
    public void jiao(){
        System.out.println("miao~");
    }
}
public class Dog {
    public void jiao(){
        System.out.println("wang~");
    }
}
package com.yunlbd.pojo;

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

    @Override
    public String toString() {
        return "People{" +
            "cat=" + cat +
            ", dog=" + dog +
            ", name='" + 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;
    }
}
<?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="cat11" class="com.yunlbd.pojo.Cat"/>
    <bean id="dog" class="com.yunlbd.pojo.Dog"/>
    <!--byname会自动查找,和自己对象set对应的值对应的id-->
    <!--<bean id="people" class="com.yunlbd.pojo.People" autowire="byName">-->
    <!--<property name="name" value="zhang"></property>-->
    <!--</bean>-->
    <!--byType会自动查找,和自己对象属性相同的bean-->
    <bean id="people" class="com.yunlbd.pojo.People" autowire="byType">
        <property name="name" value="zhang"></property>
    </bean>
</beans>

使用注解自动装配:

jdk1.5支持的注解,spring2.5支持的注解

The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.

导入context约束:

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

    <context:annotation-config/>
</beans>

@Autowire:

在属性上个使用,也可以在set上使用

我们可以不用编写set方法了

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}
@Nullable 字段标志的注解,说明这个字段可以为null

如果@Autowired自动装配环境比较复杂。自动装配无法通过一个注解完成的时候,我们可以使用@Qualifier(value = "dog")去配合使用,指定一个唯一的id对象

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

@Resource(name="dog")也可以

区别:

  • @Autowired通过byType实现,而且必须要求这个对象存在
  • @Resource默认通过byName实现,如果找不到,通过byType实现

8、使用注解开发

在spring4之后,必须要保证aop的包导入

使用注解需要导入context的约束

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

    <context:annotation-config/>
</beans>
  1. 属性如何注入

    @Component
    public class User {
    
        @Value("zhang")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
  2. 衍生的注解

    @Component有几个衍生注解,会按照web开发中,mvc架构中分层。

    • dao (@Repository)
    • service(@Service)
    • controller(@Controller)

    这四个注解功能一样的,都是代表将某个类注册到容器中

  3. 作用域

    @Scope("singleton")

    @Component
    @Scope("prototype")
    public class User {
    
        @Value("zhang")
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

小结:

  • xml与注解:
    • xml更加万能,维护简单
    • 注解,不是自己的类,使用不了,维护复杂
  • 最佳实践:
    • xml用来管理bean
    • 注解只用来完成属性的注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           https://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <!--指定要扫描的包-->
    <context:component-scan base-package="com.pojo"/>
</beans>

9、使用java方式配置Spring

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

@Configuration //这个也会被spring容器托管,注册到容器中,因为他本来就是一个@Component
@ComponentScan("com.yunlbd.pojo")
@Import(Config2.class)
public class MyConfig {

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

    @Value("zhang")
    private String name;

    public String getName() {
        return name;
    }

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

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

测试类:

pub1ic class MyTest {
    pub1ic static void main(String[] args) {
        //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载!
        App1icationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = (User) context.getBean("user");
        System.out.print1n(user.getName());
    }
}

这种纯java配置方式,在springboot中,随处可见。

10、代理模式

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

代理模式:

  • 静态代理
  • 动态代理

学习aop之前 , 我们要先了解一下代理模式!

10.1、静态代理

静态代理角色分析

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

代码实现

Rent.java即抽象角色

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

Host.java即真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
    public void rent() {
        System.out.println("房屋出租");
    }
}

Proxy.java 即代理角色

//代理角色:中介
public class Proxy implements Rent {
    private Host host;
    public Proxy() { }
    public Proxy(Host host) {
        this.host = host;
    }

    //租房
    public void rent(){
        seeHouse();
        host.rent();
        fare();
    }
    //看房
    public void seeHouse(){
        System.out.println("带房客看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

Client.java 即客户

//客户类,一般客户都会去找代理!
public class Client {
    public static void main(String[] args) {
        //房东要租房
        Host host = new Host();
        //中介帮助房东
        Proxy proxy = new Proxy(host);
        //你去找中介!
        proxy.rent();
    }
}

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

优点:

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

缺点 :

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

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

10.2、静态代理再理解

我们再来举一个例子,巩固大家的学习!

练习步骤:

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

//抽象角色:增删改查业务
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5、测试访问类:

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

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

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

聊聊AOP:纵向开发,横向开发

10.3、动态代理

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

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

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

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

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

核心 : InvocationHandler 和 Proxy,打开JDK帮助文档看看

  • InvocationHandler:调用处理程序

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

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

代码实现

抽象角色和真实角色和之前的一样!

Rent.java 即抽象角色

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

Host.java 即真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
    public void rent() {
        System.out.println("房屋出租");
    }
}

ProxyInvocationHandler.java 即代理角色

public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

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

    //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                      rent.getClass().getInterfaces(),this);
    }

    // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
    // 处理代理实例上的方法调用并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //核心:本质利用反射实现!
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    //看房
    public void seeHouse(){
        System.out.println("带房客看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

Client.java

//租客
public class Client {

    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理实例的调用处理程序
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(host); //将真实角色放置进去!
        Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
        proxy.rent();
    }
}

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

10.4、动态代理深入理解

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

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

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

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

    // proxy : 代理类
    // method : 代理类的调用处理程序的方法对象.
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }

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

测试!

public class Test {
    public static void main(String[] args) {
        //真实对象
        UserServiceImpl userService = new UserServiceImpl();
        //代理对象的调用处理程序
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService); //设置要代理的对象
        UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
        proxy.delete();
    }
}

10.5、动态代理的好处

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

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

11、AOP

11.1、什么是AOP

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

11.2、Aop在Spring中的作用

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

以下名词需要了解下:

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

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

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

11.3、使用Spring实现AOP

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

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

第一种方式:通过 Spring API 实现

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

    public interface UserService {
    
        public void add();
    
        public void delete();
    
        public void update();
    
        public void search();
    }
    
    public class UserServiceImpl implements UserService{
    
        @Override
        public void add() {
            System.out.println("增加用户");
        }
    
        @Override
        public void delete() {
            System.out.println("删除用户");
        }
    
        @Override
        public void update() {
            System.out.println("更新用户");
        }
    
        @Override
        public void search() {
            System.out.println("查询用户");
        }
    }
    
  2. 然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

    public class Log implements MethodBeforeAdvice {
    
        //method : 要执行的目标对象的方法
        //objects : 被调用的方法的参数
        //Object : 目标对象
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
        }
    }
    
    public class AfterLog implements AfterReturningAdvice {
        //returnValue 返回值
        //method被调用的方法
        //args 被调用的方法的对象的参数
        //target 被调用的目标对象
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法,返回值:" + returnValue);
        }
    }
    
  3. 最后去spring的文件中注册,并实现aop切入实现,注意导入约束 。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--注册bean-->
        <bean id="userService" class="com.yunlbd.service.UserServiceImpl"/>
        <bean id="log" class="com.yunlbd.log.Log"/>
        <bean id="afterLog" class="com.yunlbd.log.AfterLog"/>
    
        <!--aop的配置-->
        <aop:config>
            <!--切入点 expression:表达式匹配要执行的方法-->
            <aop:pointcut id="pointcut" expression="execution(* com.yunlbd.service.UserServiceImpl.*(..))"/>
            <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
    </beans>
    
  4. 测试

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

Aop的重要性 : 很重要, 一定要理解其中的思路,主要是思想的理解这一块。

Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来。实现公共业务的重复利用。领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理。

第二种方式:自定义类来实现AOP

目标业务类不变依旧是userServiceImpl

  1. 写我们自己的一个切入类

    public class DiyPointcut {
    
        public void before(){
            System.out.println("---------方法执行前---------");
        }
        
        public void after(){
            System.out.println("---------方法执行后---------");
        }
    }
    
  2. 去spring中配置

    <!--第二种方式自定义实现-->
    <!--注册bean-->
    <bean id="diy" class="com.yunlbd.config.DiyPointcut"/>
    
    <!--aop的配置-->
    <aop:config>
        <!--第二种方式:使用AOP的标签实现-->
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPonitcut" expression="execution(* com.yunlbd.service.UserServiceImpl.*(..))"/>
            <aop:before pointcut-ref="diyPonitcut" method="before"/>
            <aop:after pointcut-ref="diyPonitcut" method="after"/>
        </aop:aspect>
    </aop:config>
    
  3. 测试

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

第三种方式:使用注解实现

  1. 编写一个注解实现的增强类

    package com.yunlbd.config;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class AnnotationPointcut {
        @Before("execution(* com.yunlbd.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println("---------方法执行前---------");
        }
    
        @After("execution(* com.yunlbd.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println("---------方法执行后---------");
        }
    
        @Around("execution(* com.yunlbd.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint jp) throws Throwable {
            System.out.println("环绕前");
            System.out.println("签名:"+jp.getSignature());
            //执行目标方法proceed
            Object proceed = jp.proceed();
            System.out.println("环绕后");
            System.out.println(proceed);
        }
    }
    
  2. 在Spring配置文件中,注册bean,并增加支持注解的配置

    <!--第三种方式:注解实现-->
    <bean id="annotationPointcut" class="com.yunlbd.config.AnnotationPointcut"/>
    <aop:aspectj-autoproxy/>
    

    aop:aspectj-autoproxy:说明

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

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

  3. 测试

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

12、整合mybatis

文档: https://mybatis.org/spring/zh/

整合步骤:

1、导入相关jar包

  • junit
  • mybatis
  • mysql数据库
  • spring相关的
  • aop织入
  • mybatis-spring【new】

2、编写配置文件

3、测试

12.1、回忆Mybatis

mybatis的配置流程:

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

实例:

  1. 导入相关jar,pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-study</artifactId>
            <groupId>com.melodyhub</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>spring-10-mybatis</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!--<version>5.1.47</version>-->
                <version>8.0.19</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.0.RELEASE</version>
            </dependency>
            <!--Spring操作数据库的话,需要一个spring-jdbc-->
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.4</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.3</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.10</version>
            </dependency>
        </dependencies>
    
        <!--在build中配置resources,来防止资源导出失败的问题-->
        <!-- Maven解决静态资源过滤问题 -->
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <!--<filtering>false</filtering>-->
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <!--<filtering>true</filtering>-->
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    </project>
    
  2. 编写实体类,User.java

    package com.yunlbd.pojo;
    
    import lombok.Data;
    
    @Data
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    
  3. 编写核心配置文件,resources/mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <!--configuration 核心配置文件-->
    <configuration>
    
        <!--开启日志-->
        <!--<settings>
            <setting name="logImpl" value="STDOUT_LOGGING" />
        </settings>-->
    
        <!--可以给实体类起别名 -->
        <typeAliases>
            <package name="com.melodyhub.pojo"/>
        </typeAliases>
    
        <!--environments 可配置多个环境-->
        <environments default="development">
            <!--多个环境,不同id-->
            <environment id="development">
                <!--事务管理-->
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;autoReconnect=true"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册!!!-->
        <mappers>
            <mapper class="com.melodyhub.mapper.UserMapper"/>
            <!--<mapper resource="com/melodyhub/mapper/UserMapper.xml"/>-->
        </mappers>
    </configuration>
    
  4. 编写接口,UserMapper.xml

    package com.yunlbd.mapper;
    
    import com.yunlbd.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        public List<User> selectUser();
    }
    
  5. 编写接口配置文件,UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.yunlbd.mapper.UserMapper">
        <!-- 绑定接口 -->
        <select id="selectUser" resultType="user">
            select * from mybatis.user;
        </select>
    </mapper>
    
  6. 测试,test/java/MyTest.java

    import com.yunlbd.mapper.UserMapper;
    import com.yunlbd.pojo.User;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;
    import org.apache.ibatis.io.Resources;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    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);
            }
        }
    }
    

12.2、mybatis-spring:方式一

  1. 编写数据源配置
  2. sqlSessionFactory
  3. sqlSessionTemplate(相当于sqlSession)
  4. 需要给接口加实现类【new】
  5. 将自己写的实现类,注入到Spring中
  6. 测试!

数据源:

  • DataSource:使用Spring的数帮源替换Mybatis的配置
  • 这使用Spring提供的JDBC:org.springframework.jdbc.datasource
  • 其他数据源:c3p0、dbcp、druid

核心对象:

  • sqISessionFactory

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

    文档:http://mybatis.org/spring/zh/factorybean.html

  • sqISessionTemplate

    SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

    当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions。

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

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

    文档:http://mybatis.org/spring/zh/sqlsession.html

实例:

  1. 先导入jar包

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-study</artifactId>
            <groupId>com.yunlbd</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>spring-10-mybatis</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!--<version>5.1.47</version>-->
                <version>8.0.19</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.0.RELEASE</version>
            </dependency>
            <!--Spring操作数据库的话,需要一个spring-jdbc-->
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.4</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.3</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.10</version>
            </dependency>
        </dependencies>
    
        <!--在build中配置resources,来防止资源导出失败的问题-->
        <!-- Maven解决静态资源过滤问题 -->
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <!--<filtering>false</filtering>-->
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <!--<filtering>true</filtering>-->
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    </project>
    
  2. 编写pojo实体类 User

    package com.yunlbd.pojo;
    
    import lombok.Data;
    
    @Data
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    
  3. 编写接口UserMapper以及实现类

    package com.yunlbd.mapper;
    
    import com.yunlbd.pojo.User;
    
    import java.util.List;
    
    public interface UserMapper {
        public List<User> selectUser();
    }
    
    package com.yunlbd.mapper;
    
    import com.yunlbd.pojo.User;
    import org.mybatis.spring.SqlSessionTemplate;
    
    import java.util.List;
    
    public class UserMapperImpl implements UserMapper {
        //我们的所有操作,在原来都使用sqlSession来执行,现在都使用SqlSessionTemplate;
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public List<User> selectUser() {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            return userMapper.selectUser();
        }
    }
    
  4. 编写UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.yunlbd.mapper.UserMapper">
        <!-- 绑定接口 -->
        <select id="selectUser" resultType="user">
            select * from mybatis.user;
        </select>
    </mapper>
    
  5. 配置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 核心配置文件-->
    <configuration>
    
        <!--可以给实体类起别名 -->
        <typeAliases>
            <package name="com.yunlbd.pojo"/>
        </typeAliases>
    
    </configuration>
    
  6. 配置spring-dao.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--DataSource:使用Spring的数帮源替换Mybatis的配置 其他数据源:c3p0、dbcp、druid
            这使用Spring提供的JDBC: org.springframework.jdbc.datasource -->
        <!--dataSource -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;autoReconnect=true"/>
            <property name="username" value="root" />
            <property name="password" value="111111" />
        </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/yunlbd/mapper/*.xml"/>
        </bean>
    
        <!-- sqlSessionTemplate 就是之前使用的:sqlsession -->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!-- 只能使用构造器注入sqlSessionFactory 原因:它没有set方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    </beans>
    
  7. 配置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
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--导入其他配置文件-->
        <import resource="spring-dao.xml"/>
    
        <!--mybatis-spring 方式1-->
        <bean id="userMapper" class="com.yunlbd.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    
    </beans>
    
  8. 测试

    public class MyTest {
        @Test
        public void test() {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
            for (User user : userMapper.selectUser()) {
                System.out.println(user);
            }
        }
    }
    

12.3、mybatis-spring:方式二

  • SqlSessionDaoSupport

    SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法,就像下面这样:

    public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
      public User getUser(String userId) {
        return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
      }
    }
    

    在这个类里面,通常更倾向于使用 MapperFactoryBean,因为它不需要额外的代码。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一个非抽象的实现类,那么这个类就很有用了。

    SqlSessionDaoSupport 需要通过属性设置一个 sqlSessionFactory 或 SqlSessionTemplate。如果两个属性都被设置了,那么 SqlSessionFactory 将被忽略。

    假设类 UserMapperImpl 是 SqlSessionDaoSupport 的子类,可以编写如下的 Spring 配置来执行设置:

    <bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
      <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
    

    文档:http://mybatis.org/spring/zh/sqlsession.html

  • 实例

    1. 编写UserServiceImpl2.java

      package com.yunlbd.mapper;
      
      import com.yunlbd.pojo.User;
      import org.apache.ibatis.session.SqlSession;
      import org.mybatis.spring.support.SqlSessionDaoSupport;
      
      import java.util.List;
      
      public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
          @Override
          public List<User> selectUser() {
              return getSqlSession().getMapper(UserMapper.class).selectUser();
          }
      }
      
    2. spring-dao.xml中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">
      
          <!--DataSource:使用Spring的数帮源替换Mybatis的配置 其他数据源:c3p0、dbcp、druid
              这使用Spring提供的JDBC: org.springframework.jdbc.datasource -->
          <!--dataSource -->
          <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driver" value="com.mysql.jdbc.Driver" />
              <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT%2B8&amp;autoReconnect=true"/>
              <property name="username" value="root" />
              <property name="password" value="root" />
          </bean>
      
          <!--sqlSessionFactory-->
          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <!--绑定 Mybatis 配置文件-->
              <property name="configLocation" value="classpath:mybatis-config.xml"/>
              <property name="mapperLocations" value="classpath:com/yunlbd/mapper/*.xml"/>
          </bean>
      </beans>
      
    3. 编写applicationContext.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             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">
      
          <!--导入其他配置文件-->
          <import resource="spring-dao.xml"/>
      
          <!--mybatis-spring 方式-2-->
          <bean id="userMapper2" class="com.yunlbd.mapper.UserMapperImpl2">
              <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
          </bean>
      </beans>
      
    4. 测试

      public class MyTest {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              UserMapper userMapper = (UserMapper) context.getBean("userMapper2");
              for (User user : userMapper.getUser()) {
                  System.out.println(user);
              }
          }
      }
      

13、声明式事务

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

事务的ACID原则:

  1. 原子性
  2. 隔离性
  3. 一致性
  4. 持久性

Spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要再代码中,进行事务管理

声明式事务

  1. 先导入jar包

    <dependencies>
    
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
    
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.4</version>
        </dependency>
    
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.12</version>
        </dependency>
    
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    
    </dependencies>
    
    <!--在build中配置resources,来防止资源导出失败的问题-->
    <!-- Maven解决静态资源过滤问题 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    
  2. pojo实体类 User

    package pojo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private String pwd;
    }
    
  3. 接口UserMapper

    public interface UserMapper {
        public List<User> getUser();
    
        public int insertUser(User user); 
    
        public int delUser(@Param("id") int id); 
    }
    
  4. 实现类UserMapperImpl

    public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
        public List<User> getUser() {
        	User user = new User(5,"张三","12333");
        	insertUser(user);
        	deleteUser(5);
            SqlSession sqlSession = getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.getUser();
            //或者return getSqlSession().getMapper(UserMapper.class).getUser();
        }
        //插入
    	public int insertUser(User user) {
    		return getSqlSession().getMapper(UserMapper.class).insertUser(user);
    	}
    	//删除
    	public int deleteUser(int id) {
    		return getSqlSession().getMapper(UserMapper.class).delUser(id);
    	}
    }
    
  5. UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <!-- 绑定接口 -->
    <mapper namespace="com.yunlbd.mapper.UserMapper">
        <select id="getUser" resultType="com.yunlbd.pojo.User">
            select * from mybatis.mybatis
        </select>
    
        <insert id="insertUser"  parameterType="com.yunlbd.pojo.User" >
            insert into mybatis.mybatis (id,name,pwd) values (#{id},#{name},#{pwd})
        </insert>
    
        <delete id="deleteUser" parameterType="int">
            deletes from mybatis.mybatis where id = #{id}
            <!-- deletes是故意写错的 -->
        </delete>
    </mapper>
    
  6. 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 -->
    <configuration>
    
        <!--开启日志-->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING" />
        </settings>
    
        <!--可以给实体类起别名-->
        <typeAliases> 
            <package name="com.yunld.pojo" />
        </typeAliases>
    
    </configuration>
    
  7. spring-dao.xml

    ?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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/tx
                               https://www.springframework.org/schema/tx/spring-tx.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--data source -->
        <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
            <property name="username" value="root" />
            <property name="password" value="root" />
        </bean>
    
        <!--sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="datasource" />
            <!--绑定 mybatis 配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        </bean>
    
        <!--声明式事务-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="datasource" />
        </bean>
    
        <!--结合aop实现事务织入-->
        <!--配置事务的通知类-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!--给哪些方法配置事务-->
            <!--新东西:配置事务的传播特性 propagation-->
            <tx:attributes>
                <tx:method name="add" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="update" propagation="REQUIRED"/>
                <tx:method name="query" read-only="true"/>
                <!-- *号包含上面4个方法:
                <tx:method name="*" propagation="REQUIRED"/> -->
            </tx:attributes>
        </tx:advice>
    
        <!--配置事务切入-->
        <aop:config>
            <aop:pointcut id="txpointcut" expression="execution(* mapper.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
        </aop:config>
    </beans>
    
  8. 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
                               https://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <import resource="spring-dao.xml" />
    
        <bean id="userMapper" class="com.yunlbd.mapper.UserMapperImpl">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
        </bean>
    </beans>
    
  9. 测试

    public class MyTest7 {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = (UserMapper) context.getBean("userMapper");
            for (User user : userMapper.getUser()) {
                System.out.println(user);
            }
        }
    }
    
posted @ 2021-12-10 10:24  蓝色空间号  阅读(31)  评论(0)    收藏  举报