欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SMM框架学习日志——Spring详述

程序员文章站 2022-07-12 13:18:01
...

1、Spring5

1.1 简介

  • Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed, simplicity, and productivity has made it the world’s most popular Java framework.

  • Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

  • 2002,首次推出了Spring框架的雏形:interface21框架

  • spring理念:使现在的技术更加简化,解决企业应用开发的复杂性

  • SSH:Struct2+Spring+Hibernate

  • SSH:SpringMVC+Spring+Mybatis

官网:https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#overview

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

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

使用Spring需要导入的依赖

<!-- 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-webmvc -->
<!--spring事务-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

1.2 优点

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

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

1.3 组成

SMM框架学习日志——Spring详述

1.4 拓展

现代的Java开发,说白了就是基于Spring的开发
SMM框架学习日志——Spring详述

  • SpringBoot
    • 一个快速开发的脚手架
    • 基于SpringBoot可以快速开发某个微服务
    • 约定大于配置
  • SpringCloud
    • SpringCloud是基于SpringBoot实现的

SpringBoot是关键,而要学会SpringBoot的前提就是要掌握Spring和SpringMVC

2、IOC理论

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

依赖注入理解:使用Set方法动态的注入接口,然后将接口具体的实现控制权交给用户,这样程序的耦合性大大降低!程序员也就能更加专注业务层的实现。

个人理解:当Dao层的一个接口包含多个实现类时,在业务层直接调用这些实现类是十分繁琐的,因为随着用户要求增多,改动业务层代码非常麻烦,所以我们可以写一个set方法动态注入dao接口,然后将接口的具体实现交给用户来实现,这样就会简化程序的耦合。

SMM框架学习日志——Spring详述

3、HelloSpring

步骤:

  1. 编写实体类

    package com.xiaojing.pojo;
    
    public class Hello {
        private String str;
    
        public void setStr(String str) {
            this.str = str;
        }
    
        public String getStr() {
            return str;
        }
    
        @Override
        public String toString() {
            return "Hello " + str;
        }
    }
    
  2. 配置beans.xml

    <?xml version="1.0" encoding="UTF8"?>
    <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">
    
        <!--
        id:变量名
        class:new的对象
        property:配置属性
        -->
        <bean id="hello" class="com.xiaojing.pojo.Hello">
            <property name="str" value="Spring"/>
        </bean>
        <!--
        ref:在Spring容器里创建好的对象
        value:具体的值
        -->
    </beans>
    
  3. 测试

    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Hello bean = (Hello) context.getBean("hello");
    System.out.println(bean.toString());
    

在整个过程中,hello对象的创建和其属性的设置都由Spring容器来实现

这个过程就叫控制反转:

控制:在传统程序中,对象的创建由程序自身来创建,使用Spring后,对象由Spring容器来创建

反转:程序不创建对象,而是被动的接收对象,即从Spring容器中取出对象

依赖注入:使用set方法来注入

4、IOC创建对象的方式

  1. 默认使用无参构造

  2. 假设需要使用有参构造

    1. 下标赋值

      <!--第一种方法:下标赋值-->
      <constructor-arg index="0" value="张小敬"/>
      
    2. 类型赋值

      <!--第二种方式:(不推荐使用) 类型赋值-->
      <constructor-arg type="java.lang.String" value="张小敬"/>
      
    3. 属性名赋值

      <!--第三种方式:属性名赋值-->
      <constructor-arg name="name" value="张小敬"/>
      

总结:在加载beans.xml文件的时候,对象就已经创建了,我们可以直接在容器里取

5、Spring配置

5.1 bean

  • id:bean的唯一标识符,相当于变量名
  • class:bean对象所对应的类全名
  • name:别名,可以同时取多个别名

5.2 import

使用import将多个beans.xml文件合并为一个,多用于协同开发

6、依赖注入

6.1 构造器注入

IOC创建对象的方式中已说过

6.2 Set方法注入(重点)

  • 依赖:对象的创建依赖Spring容器
  • 注入:对象的属性依赖Spring容器注入

测试一下各种注入方式:

  1. 写一个含多种数据类型的实体类

    public class Address {
        private String addr;
        private int code;
    }
    
    public class Student {
        private String name;
        private Address addr;
        private String[] books;
        private List<String> hobbies;
        private Map<String,Integer> grades;
        private Set<String> relation;
        private Properties info;
    }
    
  2. 注入方式杂烩

    <bean name="student" class="com.xiaojing.pojo.Student">
        <!--1、普通值注入-->
        <property name="name" value="张小敬"/>
        <!--2、引用对象注入-->
        <property name="addr">
            <bean class="com.xiaojing.pojo.Address">
                <constructor-arg name="addr" value="Sichuan University"/>
                <constructor-arg name="code" value="519"/>
            </bean>
        </property>
        <!--3、数组注入-->
        <property name="books">
            <array value-type="java.lang.String">
                <value>大泼猴</value>
                <value>三体</value>
                <value>剑来</value>
            </array>
        </property>
        <!--4、Map注入-->
        <property name="grades">
            <map>
                <entry key="数学" value="125"/>
                <entry key="语文" value="108"/>
                <entry key="英语" value="132"/>
                <entry key="理综" value="250"/>
            </map>
        </property>
        <!--5、List注入-->
        <property name="hobbies">
            <list>
                <value>打游戏</value>
                <value>听音乐</value>
                <value>看剧</value>
                <value>唱歌</value>
            </list>
        </property>
        <!--6、properties注入-->
        <property name="info">
            <props>
                <prop key="birth">1997-08-17</prop>
                <prop key="height">171</prop>
                <prop key="weight">65</prop>
            </props>
        </property>
        <!--7、set注入-->
        <property name="relation">
            <set>
                <value>parents</value>
                <value>friends</value>
                <value>wife</value>
            </set>
        </property>
    </bean>
    
  3. 完善测试类

    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Student student = (Student) context.getBean("student");
            System.out.println(student.toString());
            /*Student{
            name='张小敬'
            addr=Address{addr='Sichuan University', code=519}
            books=[大泼猴, 三体, 剑来]
            hobbies=[打游戏, 听音乐, 看剧, 唱歌]
            grades={数学=125, 语文=108, 英语=132, 理综=250}
            relation=[parents, friends, wife]
            info={height=171, weight=65, birth=1997-08-17}
            }
            * */
        }
    }
    

6.3 p-namespace和c-namespace注入

引入约束:

<!--引入p-namespace-->
xmlns:p="http://www.springframework.org/schema/p"
<!--引入c-namespace-->
xmlns:c="http://www.springframework.org/schema/c"

注入:

<!--引用p-namespace,可以直接注入属性的值-->
<bean id="user" class="com.xiaojing.pojo.User" p:name="张小敬" p:age="22"/>
<!--引用c-namespace,构造器注入-->
<bean id="user2" class="com.xiaojing.pojo.User" c:name="张小敬" c:age="22"/>

6.4 bean的作用域

SMM框架学习日志——Spring详述

  1. 代理模式(Spring默认机制)

SMM框架学习日志——Spring详述

<bean id="user" class="com.xiaojing.pojo.User" p:name="张小敬" p:age="22" scope="singleton"/>
  1. 原型模式(每次从容器中get对象时,都会获得一个新对象)

SMM框架学习日志——Spring详述

  1. 其余的模式多用于web开发

7、Bean的自动装配

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

在Spring中有三种装配的方式

  1. xml显示装配
  2. java显示装配
  3. 隐式的装配(重点)

7.1 环境搭建

package com.xiaojing.pojo;

public class Cat {
    public void shout(){
        System.out.println("Miao!");
    }
}
package com.xiaojing.pojo;

public class Dog {
    public void shout(){
        System.out.println("Wang!");
    }
}
package com.xiaojing.pojo;

public class Person {
    private Cat cat;
    private Dog dog;
    private String name;
    private int age;

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

    public int getAge() {
        return age;
    }

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

7.2 ByName自动装配

<bean id="cat" class="com.xiaojing.pojo.Cat"/>
<bean id="dog" class="com.xiaojing.pojo.Dog"/>
<!--
    byName:会自动在容器上下文查找,与自己对象set方法后面的值对应的bean id
    比如说:setDog后面的值是dog
    -->
<bean id="person" class="com.xiaojing.pojo.Person" autowire="byName">
    <property name="name" value="不器"/>
    <property name="age" value="18"/>
</bean>

7.3 ByType自动装配

<bean id="cat" class="com.xiaojing.pojo.Cat"/>
<bean id="dog" class="com.xiaojing.pojo.Dog"/>
<!--
byName:会自动在容器上下文查找,与自己对象set方法后面的值对应的bean id
比如说:setDog后面的值是dog
byType:会自动在容器上下文查找,与自己对象类型对应的bean类型
-->
<bean id="person" class="com.xiaojing.pojo.Person" autowire="byType">
    <property name="name" value="不器"/>
    <property name="age" value="18"/>
</bean>

小结:

  • byName要求所有的bean的id唯一,且bean id与自动注入对象的set方法后面的值相等
  • byType要求所有的bean的id唯一,且bean类型与自动注入的属性类型一致(因为唯一性,少用!)

7.4 使用注解实现自动装配

使用注解须知:

  1. 导入约束——context约束

  2. 配置注解支持 context:annotation-config/

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

    @Autowired

    • 在属性上使用,也可在set上使用
    • 使用Autowired注解可以不用编写Set方法,前提是自动装配的属性在容器中存在,且符合名字

    拓展:

    @Nullable	标记的该字段可以为空
    
    public @interface Autowired {
        //默认为true,若显示设置required=false,则标记的字段可以为null,而不会报出空指针异常
        boolean required() default true;
    }
    

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

    @Autowired
    @Qualifier(value = "dog222")
    private Dog dog;
    
    <bean id="dog222" class="com.xiaojing.pojo.Dog"/>
    

    @Resource注解

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

    @Resource注解默认使用byname方式实现,如果找不到名字,就使用byType方式

8、使用注解开发

  1. bean

  2. 属性如何注入

    //等价于    <bean id="user" class="com.xiaojing.pojo.User"/>
    //@Component   组件
    @Component
    public class User {
        //等价于        <property name="name" value="zyj"/>
        @Value("zyj")
        public String name;
    }
    
  3. 衍生的注解

    @Component有几个衍生注解,我们在web开发时有MVC三层架构

    • dao【@Repository】

    • service【@Service】

    • controller【@Controller】

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

  4. 自动装配

    @Autowired:	通过类型、名字自动装配
    	如果无法通过一个@Autowired完成时,可以使用@Qualifier(value = "xxxx")xxxx是指定的bean对象id
    @Nullable:	注解的这个字段可以为null
    @Resource:	通过名字、类型自动装配
    
  5. 作用域

    @Component
    @Scope("singleton")
    public class User {
        //等价于        <property name="name" value="zyj"/>
        @Value("zyj")
        public String name;
    }
    
  6. 小结

    xml于注解

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

    xml与注解最佳实践:

    • xml只用来管理bean
    • 注解只负责属性注入

    我们在使用注解的时候,需要注意一个问题:必须让注解生效,要开启注解的支持

9、使用Java的方式配置Spring

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

SMM框架学习日志——Spring详述

1.实体类(组件)

@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("不器")
    public void setName(String name) {
        this.name = name;
    }
}

2.配置类

//@Configuration 是一个@Component,也会被Spring托管
//@Configuration代表这是一个配置类,相当于之前的xml
@Configuration
//扫描包加载注解
@ComponentScan("com.xiaojing.pojo")
//导入其他@Configuration
@Import(UserConfig2.class)
public class UserConfig {
    //相当于之前注册的bean
    //方法名相当于id
    //返回值相当于class
    @Bean
    public User getUser(){
        return new User();
    }
}
@Configuration
public class UserConfig2 {
}

3.测试

@Test
public void test(){
    //通过AnnotationConfigApplicationContext加载配置类的class对象,来获得容器上下文
    ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
    User user = (User) context.getBean("getUser");
    System.out.println(user.getName());
}

这种纯Java的配置方式,在SpringBoot中随处可见!

10、代理模式(重点)

  • 为什么学习代理模式?
    • 这是SpringAOP的底层!
  • 代理模式的分类
    • 静态代理
    • 动态代理

以租房为例:
SMM框架学习日志——Spring详述

10.1 静态代理

角色分析:

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

代码步骤:

  1. 抽象角色(接口业务)

    public interface Rent {
        void rent();
    }
    
  2. 真实角色

    public class Host implements Rent {
        public void rent() {
            System.out.println("房东要租房");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent {
        private Host host;
    
        public Proxy() {
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public void rent() {
            search();
            contract();
            host.rent();
            fee();
        }
    
        public void fee(){
            System.out.println("收取中介费");
        }
        public void contract(){
            System.out.println("签订合同");
        }
        public void search(){
            System.out.println("看房");
        }
    }
    
  4. 客户访问

    public class Client {
        public static void main(String[] args) {
            Host host = new Host();
            //代理角色代理房东,一般会附加额外操作
            Proxy proxy = new Proxy(host);
            proxy.rent();
        }
    }
    

代理模式的好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色,开发效率降低。即,静态代理只能代理一个类

10.2 代理模式加深理解

在不改变原有纵向开发的逻辑基础上,扩展功能时选择横向切入!

比如:要在原有的业务层上,增加一个打印日志功能,而改动业务层代码繁琐且一般不允许,所以使用代理业务来拓展

记住:在公司中,轻易改动业务层代码是大忌!

SMM框架学习日志——Spring详述

  1. dao

    public interface UserDao {
        void add();
        void delete();
        void update();
        void query();
    }
    
  2. service

    public class UserServiceImpl implements UserDao {
        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. serviceproxy(横向开发)

    package com.xiaojing.demo02;
    
    public class UserServiceProxy implements UserDao {
        private UserServiceImpl userService;
    
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        public void add() {
            userService.add();
            log("add");
        }
    
        public void delete() {
            userService.delete();
            log("delete");
        }
    
        public void update() {
            userService.update();
            log("update");
        }
    
        public void query() {
            userService.query();
            log("query");
        }
    
        private void log(String msg){
            System.out.println("[Debug]进行了"+msg+"方法");
        }
    }
    
  4. controller

    public class UserController {
        public static void main(String[] args) {
            UserServiceImpl userService = new UserServiceImpl();
            UserServiceProxy proxy = new UserServiceProxy();
            proxy.setUserService(userService);
    
            proxy.add();
            proxy.delete();
            proxy.update();
            proxy.query();
        }
    }
    

10.2 动态代理

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

需要了解两个类:Proxy:代理,InvocationHandler:代理调用处理程序

  1. 可以将ProxyInvocationHandler封装成工具类(因为每个动态代理代理接口的代码结构都差不多)

    package com.xiaojing.demo04;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvocationHandler implements InvocationHandler {
        //代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //生成代理类实例
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
        }
    
        //动态代理处理程序,并返回结果
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            logs(method.getName());
            return method.invoke(target,args);
        }
        //扩展的方法
        public void logs(String msg){
            System.out.println("[Debug]调用了"+msg+"方法");
        }
    }
    
  2. 测试

    package com.xiaojing.demo04;
    
    import com.xiaojing.demo02.UserDao;
    import com.xiaojing.demo02.UserServiceImpl;
    
    public class UserController {
        public static void main(String[] args) {
            //真实对象
            UserServiceImpl userService = new UserServiceImpl();
            //代理处理程序,此时还没有代理对象
            ProxyInvocationHandler handler = new ProxyInvocationHandler();
            //将需要代理的真实对象放入代理处理程序
            handler.setTarget(userService);
            //动态生成代理对象
            UserDao proxy = (UserDao) handler.getProxy();
    
            proxy.add();
        }
    }
    

动态代理的好处:

  • 静态代理的所有好处
  • 一个动态代理类可以代理一个接口,一般是一个业务
  • 一个动态代理类可以代理多个类,只要实现了同一个接口

11、AOP

11.1 什么是AOP

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

SMM框架学习日志——Spring详述

11.2 AOP在Spring中的作用

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

  • 横切关注点:与业务逻辑无关,但需要拓展的功能
  • 切面(Aspect):被模块化的特殊对象,即它是一个类
  • 通知(Advice):切面要完成的工作,即它是类的一个方法
  • 目标:被通知对象
  • 代理:通知后创建的对象
  • 切入点:切面通知执行的“地点”的定义(说白了就是目标对象的方法)
  • 连接点:与切入点匹配的执行点

SMM框架学习日志——Spring详述

11.3 使用Spring实现AOP

方式一:使用Spring的API接口

  1. 导入依赖

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    
  2. 目标对象、切入点和通知

    • 目标对象及通知

      public interface UserService {
          void add();
          void delete();
          void update();
          void query();
      }
      
      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("查询一个用户");
          }
      }
      
    • 通知

      //执行前日志
      public class Log implements MethodBeforeAdvice {
          //Method    要执行的对象方法
          //args      参数
          //target    目标接口
          public void before(Method method, Object[] args, Object target) throws Throwable {
              System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法");
          }
      }
      
      //执行后日志
      public class AfterLog implements AfterReturningAdvice {
      
          public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
              System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法,得到返回值:"+returnValue);
          }
      }
      
  3. 配置xml

    <?xml version="1.0" encoding="UTF8"?>
    <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-->
        <bean id="userService" class="com.xiaojing.service.UserServiceImpl"/>
        <bean id="log" class="com.xiaojing.log.Log"/>
        <bean id="afterlog" class="com.xiaojing.log.AfterLog"/>
    
        <!--方式一:使用Spring的API-->
        <!--配置约束-->
        <aop:config>
            <!--切入点 expression表达式-->
                <!--
                execution(修饰符 返回值 类名+方法名+参数)
                修饰符:默认是public,可以不写
                *:代表任意返回值类
                com.xiaojing.service.UserServiceImpl.*:代表该类下的所有方法
                (..):代表所有的参数类型
                -->
            <aop:pointcut id="pointcut" expression="execution(* com.xiaojing.service.UserServiceImpl.*(..))"/>
            <!--执行环绕,即将log切入到userservice-->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
        </aop:config>
    </beans>
    
  4. 测试

    @Test
    public void Test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userservice = (UserService) context.getBean("userService");
    
        userservice.add();
        
        /*  
        打印结果:
        com.xiaojing.service.UserServiceImpl执行了add方法
        增加一个用户
        com.xiaojing.service.UserServiceImpl执行了add方法,得到返回值:null
        */
    }
    

方式二:自定义来实现AOP

  1. 自定义日志

    package com.xiaojing.diy;
    
    public class DiyLog {
        public void before(){
            System.out.println("====方法执行前======");
        }
        public void after(){
            System.out.println("=====方法执行后======");
        }
    }
    
  2. 自定义切面

    <!--方式二:自定义-->
    <bean id="diylog" class="com.xiaojing.diy.DiyLog"/>
    <aop:config>
        <!--自定义切面-->
        <aop:aspect id="aspect" ref="diylog">
            <aop:pointcut id="pointcut" expression="execution(* com.xiaojing.service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
    

方式三:使用注解实现AOP

  • 切面类

    //方式三:使用注解实现AOP
    @Aspect
    public class AnnoDiyLog {
        @Before(value = "execution(* com.xiaojing.service.UserServiceImpl.*(..))")
        public void before(){
            System.out.println("====方法执行前======");
        }
        @After(value = "execution(* com.xiaojing.service.UserServiceImpl.*(..))")
        public void after(){
            System.out.println("=====方法执行后======");
        }
    }
    
  • 配置

    <!--方式三:使用注解-->
    <bean id="annodiylog" class="com.xiaojing.diy.AnnoDiyLog"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>
    

12、整合Mybatis

  1. 配置jar包

    <dependencies>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <!--spring操作数据库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.5</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>
    
  2. 测试Mybatis

12.1 mybatis-spring

  1. 配置Datasource(将mybatis的数据源换成spring的)

  2. 配置SqlSessionFactory

  3. 配置SqlSessionTemplate,SqlSessionTemplate是SqlSession的一个实现,可以完全替代SqlSession,且它是线程安全的

    <?xml version="1.0" encoding="UTF8"?>
    <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的DataSource-->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
            <property name="username" value="root"/>
            <property name="password" value="1234"/>
        </bean>
        
        <!--第二步:配置sqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--绑定mybatis-config.xml-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/xiaojing/dao/*.xml"/>
        </bean>
    
        <!--第三步:配置sqlSessionTemplate:即sqlSession-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--sqlSessionTemplate没有set方法,只能构造器注入-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
    </beans>
    

    上面配置spring-mybatis.xml的步骤是固定的,以后可以直接用

    一般来说,在applicationContext.xml(总的配置)中,我们只需要注册bean,用import引入其他的配置

    整合后的mybatis-config.xml一般就用来设置别名和设置日志

  4. 需要给接口加实现类,实现类必须有一个属性的类型是SqlSessionTemplate,用来注入sqlSession

    public interface UserMapper {
        User getUserById(int id);
    }
    
    public class UserMapperImpl implements UserMapper {
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        public User getUserById(int id) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(id);
            return user;
        }
    
    }
    
  5. 将自己写的实现类,注入到Spring中(即在applicationContext.xml中注册bean)

    <?xml version="1.0" encoding="UTF8"?>
    <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-mybatis.xml"/>
    
        <!--bean的注册都在applicationContext.xml-->
        <bean id="userMapper" class="com.xiaojing.dao.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    </beans>
    
  6. 测试

    @Test
    public void Test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper usermapper = (UserMapper) context.getBean("userMapper");
        User user = usermapper.getUserById(1);
        System.out.println(user);
    }
    

13、声明式事务

13.1 回顾事务

  • 要么都成功,要么都失败!
  • 事务在项目开发中,十分重要,涉及到数据的一致性和完整性,不能马虎!

事务ACID原则:

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损坏
  • 持久性
    • 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化到存储器中

13.2 Spring中的事务管理

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

一般在Spring中使用声明式事务管理

/*模拟事务*/
int addUser(User user);

int deleteUser(int id);
public List<User> getUser() {
    User user = new User(8, "kaka", "1234");
    addUser(user);
    deleteUser(7);
    return sqlSession.getMapper(UserMapper.class).getUser();
}

public int addUser(User user) {
    return sqlSession.getMapper(UserMapper.class).addUser(user);
}

public int deleteUser(int id) {
    return sqlSession.getMapper(UserMapper.class).deleteUser(id);
}

故意在UserMapper.xml中没有配置delete的sql,使程序出错,如果没有配置事务,那么addUser(user)仍然能够将用户插入到数据库中,而这并不是我们希望的。所以需要配置事务,让这两个方法同时成功,同时失败。

在spring-mybatis.xml添加:

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

<!--结合AOP实现事务的织入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给方法配置事务-->
    <tx:attributes>
        <!--事务新特性:传播 new propagation-->
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!--AOP事务横切-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.xiaojing.mapper.UserMapper.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>