动力结点听课笔记-Spring框架

Spring框架

绪论:

SSM框架:Spring+SpringMVC+MyBatis。

什么是Spring框架?

它是一个容器,它是整合其他框架的框架,它的核心是IOC和AOP,它由20多个模块构成,它在很多领域都提供优秀的解决方案。

我们课程中学Spring框架,还会学SpringMVC,还会学SpringBoot,以及SpringCloud等等框架。

第一章 Spring概述

Spring框架概述

Spring框架是于2003年兴起的一个轻量级的java开发框架,它是为了解决企业应用程序开发的复杂性而创建的。Spring的核心是控制反转(IOC)和面向切面编程(AOP),Spring框架是可以在javaSE,javaEE中使用的轻量级开源框架。

Spring的主要作用就是为代码”解耦“,降低代码件的耦合度,就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在Spring中说明对象的关系。

Spring更具代码的功能特点,使用IOC降低业务对象之间的耦合度,IOC使得主业务在相互调用的过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了,而是由Spring容器统一管理,自动“注入”,注入即赋值,而AOP使得系统级服务得到了最大的复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由Spring容器统一“注入”。

Spring框架官网 : https://.Spring.io

Spring官网有Spring家族技术的介绍,有相应框架的jar包和文档,还有源码文件,必要的时候可以参考。

Spring框架的优点

Spring框架是一个半成品软件,有20多个模块组成,他是一个容器管理对象 ,容器时装东西的,Spring容器不装文本,数字,装的是对象,Spring是存储对象的容器。

  • 轻量级:一方面指由20多个模块构成,每个jar包都很小,小于1M,整个核心包也就3M左右。另一方面是:对代码无污染。对代码有污染:需要按照框架的规定编写代码,类似Maven,需要搭建目录结构及pom.xml文件的编写格式。
  • 面向接口编程:使用接口就是面向灵活,项目的可扩展性,可维护性都极大的提高了。接口不关心实现类的类型,使用时接口指向实现类,切换实现类即可切换整个功能。
  • AOP(面向切面编程):三层架构:界面层,业务逻辑层,数据支持层。公共的代码,就是切面。公共的内容提取出来。就是将公共的,通用的,重复的代码单独开发,在需要的时候反织回去。底层的原理就是动态代理。
  • 整合其他框架:MyBatis,SpringMVC都被整合,整合之后,会使其他框架更加易用。

体系结构:

AOP编程的支持

公共的业务都可以称作是切面。

通过Spring提供的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付,再Spring中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明的方式,灵活的进行事务的管理,提高开发的效率和质量。

方便继承各种框架

Spring不排斥各种优秀的开源框架,相反Spring可以降低各种狂啊及的使用难度,Spring提供了对各种优秀框架(Struts2,Hibernate,MyBatis)等的直接支持,简化框架的使用。Spring像个插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个框架插头放入插线板,不需要可以轻易删除。

第二章 IOC控制反转(##重点

控制反转IOC是一个概念,是一种思想。指将传统上由程序代码直接操作的对象调用

什么时IOC?

  1. 由Spring容器进行对象的创建和依赖注入,程序员在使用的时候直接取出使用。

  2. 正转:由程序员进行对象的创建和依赖注入称为正转,程序员说了算。

    Student stu = new Student();    ====>程序员创建对象
    stu.setName("张三";            ======>程序员进行赋值
    stu.age(20);
    
  3. 反转:由Spring容器创建对象和依赖注入称为反转,将控制权从程序员手中夺走,由Soring容器,进行这些操作。称为反转。

    <bean id="stu" class="com.bjpowernode.pojo.Student">  ==Spring容器负责对象的创建。
    <property name="name" value="张三">       ====>Spring容器依赖注入值
    <property age="age" value=23>
    </bean>
    
  4. 正转:程序员说了算,反转:Spring说了算。

实例:

先创建实体类

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public Student() {
        System.out.println("学生的无参构造方法执行了!");
    }
}

通过写配置文件进行对象的创建

    <!--创建学生对象
    等同于Student stu = new Student();
    id:就是创建对象的名称
    class:就是创建对象的类型,底层通过反射构建对象
    启动容器的同时,创建对象
    -->
    <bean id="stu" class="com.gohi.pojo.Student">
        <property name="name" value="张三"></property>
    </bean>

测试方法

public class TestMyApp {
    @Test
    public void TestStudent(){
        //程序员创建对象
        Student stu = new Student();
        System.out.println(stu);
    }
    @Test
    public void TestStudent1(){
        //由Spring容器对象的创建
        //如果想从Spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student stu = (Student) ac.getBean("stu");
        System.out.println(stu);
    }
}

基于XML的IOC

  1. 创建对象

    <bean id="stu" class="com.gohi.pojo.Student"></bean>
    
  2. 给创建的对象赋值

    • 使用setter注入

      注入分为简单类型注入引用类型注入

      • 简单类型注入使用value属性

      • 引用类型注入使用ref属性

        学生的实体类当中,包含另一个类型的对象,所有对象都是引用类型。

        必须要注意:使用setter注入必须提供无参的构造方法,必须提供setxxx()方法才行

    • 使用构造方法注入

  3. 项目案例:

    使用setter注入

    使用三层架构进行用户的插入操作

    界面层,业务逻辑层,数据访问层(模拟)

    1. Spring会接管三层架构中哪些对象的创建?

      除了实体类,全部交给Spring控制

      非Spring接管下的三层项目构建,先搭建实体类

      实体类:com.bjpowernode.pojo Users

      public class Users {
          private int uid;
          private String uname;
          private int uage;
          
          public Users() {
          }
          public Users(int uid, String uname, int uage) {
              this.uid = uid;
              this.uname = uname;
              this.uage = uage;
          }
          public int getUid() {
              return uid;
          }
          public void setUid(int uid) {
              this.uid = uid;
          }
          public String getUname() {
              return uname;
          }
          public void setUname(String uname) {
              this.uname = uname;
          }
          public int getUage() {
              return uage;
          }
          public void setUage(int uage) {
              this.uage = uage;
          }
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Users users = (Users) o;
              return uid == users.uid && uage == users.uage && Objects.equals(uname, users.uname);
          }
          @Override
          public int hashCode() {
              return Objects.hash(uid, uname, uage);
          }
          @Override
          public String toString() {
              return "Users{" +
                      "uid=" + uid +
                      ", uname='" + uname + '\'' +
                      ", uage=" + uage +
                      '}';
          }
      }
      

      数据访问层:com.bjpowernode.dao UserMapper.java(接口)

      ​ UserMapperImpl.java(实现类)

      /**
       * 数据访问层接口  (interface)
       */
      public interface UsersMapper {
          //增加用户
          int insert(Users u);
      }
      
      /**
      *数据访问层实现类  (java)
      */
      public class UsersMapperImpl implements UsersMapper{
          @Override
          public int insert(Users u) {
              System.out.println(u.getUname() + "用户增加成功!");
              return 1;
          }
      }
      

      业务逻辑层:com.bjpowernode.service UserService.java(接口)

      ​ impl.UserService.java(实现类)

      /**
       * 业务实现层接口 (interface)
       */
      public interface UsersService {
          //增加用户
          int insert(Users u);
      }
      
      /*业务实现层实现类   (java)*/
      public class UsersServiceImpl implements UsersService {
          // 切记切记:在所有的业务逻辑层中都必须有数据访问层的对象
          private  UsersMapper usersMapper = new UsersMapperImpl();
          @Override
          public int insert(Users u) {
              //添加更加复杂的业务
              return usersMapper.insert(u);
          }
      }
      

      界面层:com.bjpowernode.controller UsersController.java

      public class UsersController {
      //界面层如何访问业务逻辑层
      //切记切记:所有的界面层,都会有业务逻辑层的对象。   接口指向实现类   面向接口编程
          public UsersService usersService = new UsersServiceImpl();
          //界面层的功能实现,对外提供访问的功能
          public int insert(Users users){
              return usersService.insert(users);
          }
      }
      

      使用Spring框架接管的代码如下:

          <!--在applicationContext.xml文件中创建各种对象-->
          <!--创建数据访问层的对象-->
          <bean id="uMapper" class="com.bjpowernode.dao.UsersMapperImpl"></bean>
          <!--创建业务逻辑层对象-->
          <bean id="uService" class="com.bjpowernode.Service.impl.UsersServiceImpl">
              <property name="usersMapper" ref="uMapper"></property>
          </bean>
      
          <!--创建界面层对象-->
          <bean id="uController" class="com.bjpowernode.controller.UsersController">
              <property name="usersService" ref="uService"></property>
          </bean>
      

      进行测试:

          //创建容器并启动,取出对象测试
          @Test
          public void TestInsert(){
              //创建容器并启动
              ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
              //取出对象
              UsersController uController = (UsersController) applicationContext.getBean("uController");
              //测试功能
              int num = uController.insert(new Users(200,"李四",23));
              System.out.println(uController);
              System.out.println(num);
          }
      }
      

    通过以上代码可以实现Spring框架最简单的使用,在三层框架中的应用。

使用注解的方法注入

Student stu = new Student(”张三“,22); //调用有参单独构造方法

调用有参的构造方法分为三种,简单类型和引用类型一起解决。

  1. 使用构造方法的参数名称进行注入值

        <!--创建学校的对象,并注入值(使用构造方法的参数名称进行注入值)-->
        <bean id="school" class="com.bjpowernode.pojo2.School">
            <constructor-arg name="name" value="清华大学"></constructor-arg>
            <constructor-arg name="address" value="北京海淀区"></constructor-arg>
        </bean>
    
  2. 使用构造方法参数的下标注入值

        <!--创建学生对象,并注入值(使用构造方法的参数的下标注入值)-->
    	<!--这个里面的参数的顺序可以变化,因为有index,所以不会影响-->
        <bean id="stu" class="com.bjpowernode.pojo2.Student">
            <constructor-arg index="0" value="前七"></constructor-arg>
            <constructor-arg index="1" value="23"></constructor-arg>
            <constructor-arg index="2" ref="school"></constructor-arg>
        </bean>
    
  3. 使用默认的构造方法的参数的顺序注入值

        <!--创建学生对象,并注入值(使用学默认的构造方法的参数顺序)-->
    	<!--这个地方必须注意顺序-->
        <bean id="stuSequence" class="com.bjpowernode.pojo2.Student">
            <constructor-arg value="张麻子"></constructor-arg>
            <constructor-arg value="22"></constructor-arg>
            <constructor-arg ref="school"></constructor-arg>
        </bean>
    

控制界面层有业务逻辑层的对象,业务逻辑层中有数据访问层的对象

控制界面层访问业务逻辑层,业务逻辑层访问数据访问层,数据访问层完成与数据库的交互

基于注解的方式(IOC)

也成为di(dependency injection)它是IOC的具体实现的技术。

基于注解的IOC,必须要在Spring的核心配置文件中添加包扫描,需要告诉Spring!

药:创建对象,并依赖注入。

汤:之前是xml,现在换成annotation(注解)。

  1. 创建对象的注解

    @Component:可以创建任意对象。(万能创建)//交给Spring去创建对象,就是在容器启动的时候创建,创建对象的名称是类名的驼峰命名法,也可以指定对象的名称。@Component(“stu”)

    @Controller:专门用来创建控制器的对象(Servlet),这种对象可以接受用户的请求,可以返回处理结果给客户端。

    @Service:专门用来创建业务逻辑层的对象,负责向下访问数据访问层,处理完毕后的结果返回给界面层。

    @Respository:专门用来创建数据访问层的对象,负责数据库中的增删改查的所有操作。

  2. 依赖注入的注解

    值类型的注入:@Value:专门用来给简单类型注入值(8种基本类型+String)

    引用类型的注入:

    ​ @Autowired:使用类型注入值,从整个bean工厂中搜索同源类型的对象进行注入。

    要求同源类型才可以注入:同源类型:

    1. 被注入的类型(Student中的school)与注入的类型是完全相同的类型。
    2. 被注入的类型(Student中的school)(父)与注入的类型是(子)父子类。
    3. 被注入的类型(接口)(Student中的school)与注入的类型(实现类)是接口和实现类的类型。

    ​ @Autowired+

    ​ @Qualifier:使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入。

注意:在有父子类的情况下,使用按类型注入,就意味着有多个可以注入的对象,此时按照名称进行二次筛选,选中与被注入对象相同名称的对象进行注入。

要注意:只要是基于注解的开发,必须要添加包扫描

添加包扫描

  1. 单个包扫描

    <context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
    <context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
    <context:component-scan base-package="com.bjpowernode.dao"></context:component-scan>
    
  2. 多个包扫描

    <context:component-scan base-package="com.bjpowernode.dao,com.bjpowernode.controller,com.bjpowernode.service。impl"></context:component-scan>
    <!--多个包之间可以逗号,空格或者分号分开-->
    
  3. 扫描根包**(不推荐)**

    <context:component-scan base-package="com.bjpowernode"></context:component-scan>
    <!--会降低容器启动速度,导致多做无用功-->
    
为应用指定多个Spring配置文件

当项目越来越大,需要多人合作开发,一个配置就存在很大的隐患。

  1. 拆分配置文件的策略

    • 按层拆

      applicationContexl_controller.xml

      applicationContext_service.xml

    • 按功能拆

      applicationContexl_Users.xml

      applicationContexl_Book.xml

Spring配置文件的整合
  1. 单个文件导入

        <!--如果是单个导入-->
    <import resource="applicationContext.xml"></import>
    <import resource="applicationContext.xml"></import>
    <import resource="applicationContext.xml"></import>
    
  2. 批量导入

        <!--批量导入-->
    <import resource="applicationContext_*.xml"></import>
    

第三章 AOP面向切面编程(##重点

什么是面向切面编程?

AOP(Aspect Orient Programming),面向切面编程

切面:公共的,通用的,重复的功能称为切面,面向切面编程就是将切面提取出来,单独开发,在需要调用的方法中通过动态代理的方式进行织入。

手写AOP框架

业务:图书购买的业务

切面:事务;

  1. 第一个版本:业务和切面紧紧的耦合在一起,没有拆分。

    /**
     * @program: SpringAll
     * @description: 图书实现类    图书购买业务和事务的切面耦合在一起
     * @author: Andy-W
     * @create: 2022-08-30 15:55
     **/
    public class BookServiceImpl {
        public void buy(){
            try {
                System.out.println("事务开启。。。。。。。");
                System.out.println("图书购买业务功能实现......");
                System.out.println("事务提交。。。。。。");
            }catch (Exception e){
                System.out.println("事务回滚。。。。。。");
            }
        }
    }
    
  2. 第二个版本:使用子类代理的方式拆分特务和切面。

    /**
     * @program: SpringAll
     * @description: 使用子类代理的方式进行图书业务和事务切面的拆分     BookServiceImpl.java
     * @author: Andy-W
     * @create: 2022-08-30 15:59
     **/
    public class BookServiceImpl {
        //在父类中只有干干净净的业务
        public void buy(){
            System.out.println("图书购买功能实现。。。。。。");
        }
    }
    
    /**
     * @program: SpringAll
     * @description: 子类实现类  
     *  子类就是代理类,将父类的图书购买功能添加事务的切面。    SubBookServiceImpl
     * @author: Andy-W
     * @create: 2022-08-30 16:01
     **/
    public class SubBookServiceImpl extends BookServiceImpl{
        /*** 子类的加持下,完成切面的整合*/
        @Override
        public void buy() {
            try {
                //事务的切面
                System.out.println("事务开启。。。。。。");
                //主业务实现
                super.buy();
                //事务的切面
                System.out.println("视图提交。。。。。。");
            } catch (Exception e) {
                System.out.println("事务回滚。。。。。。");}}}
    
  3. 第三个版本:使用静态代理拆分业务和切面,此时业务和业务接口已拆分,此时切面紧耦合在业务中

    Agent类(代理类)

    •  /**
        * @program: SpringAll
        * @description: 代理对象   静态代理已经实现了目标对象的灵活切换,图书购买业务,商品购买业务
        * @author: Andy-W
        * @create: 2022-08-30 16:09
        **/
       public class Agent implements Service{
           //设计成员变量的累心更为接口,为了灵活i求耳环目标对象
       
           public Service target;
           public Agent(Service target){
               this.target = target;
           }
           @Override
           public void buy() {
               try {
                   //切面功能
                   System.out.println("事务开启。。。。。。");
                   //业务功能
                   target.buy();
                   //切面功能
                   System.out.println("事务提交。。。。。。");
               } catch (Exception e) {
                   System.out.println("事务回滚。。。。。。");
               }
           }
       }
      

      Service接口(业务接口)规定了业务功能

    •  /**
        * 规定当前实现的业务:图书购买
        */
       public interface Service {
           //规定业务功能
            void buy();
       }
      

      BookServiceImpl类(图书购买业务实现类)

    •  /**
        * @program: SpringAll
        * @description: 目标对象业务功能的具体实现
        * @author: Andy-W
        * @create: 2022-08-30 16:08
        **/
       public class BookServiceImpl implements Service{
           @Override
           public void buy() {
               System.out.println("图书购买完成了。。。。。。");
           }
       }
      

      ProductServiceImpl(商品购买实现类)

    •  /**
        * @program: SpringAll
        * @description: 商品购买的业务类
        * @author: Andy-W
        * @create: 2022-08-30 16:14
        **/
       public class ProductServiceImpl implements Service{
           @Override
           public void buy() {
               System.out.println("商品购买业务实现。。。。。。");
           }
       }
      
  4. 第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口。

    上接口就是上灵活。

    public class BookServiceImpl implements Service {		//BookServiceImpl.java(class)
        @Override
        public void buy() {
            System.out.println("图书购买完成了。。。。。。");
        }
    }
    
    public class ProductServiceImpl implements Service {	//ProductServiceImpl.java(class)
        @Override
        public void buy() {
            System.out.println("商品购买业务实现。。。。。。");
        }
    }
    
    public interface Service {							//Service.java(interface)
        //规定业务功能
         void buy();
    }
    
    public interface AOP {								//AOP.java(interface)
        default void before(){}
        default void after(){}
        default void exception(){}
    }
    
    public class LogAop implements AOP{					//LogAop.java(class)
        @Override
        public void before() {
            System.out.println("前置日志输出。。。。。。");
        }
    }
    
    public class TransAop implements AOP {				//TransAop.java(class)
        @Override
        public void before() {
            System.out.println("事务开启。。。。。。。");
        }
        @Override
        public void after() {
            System.out.println("事务提交......");
        }
        @Override
        public void exception() {
            System.out.println("事务回滚。。。。。。");
        }
    }
    
    public class Agent implements Service{					//Agent.java(class)
        //传入业务对象,传入目标;传入切面对象:AOP
        Service target;
        AOP aop;
    
        //使用构造方法初始化业务对象和切面对象
        public Agent(Service target,AOP aop){
            this.target = target;
            this.aop = aop;
        }
        @Override
        public void buy() {
            try {
                //切面
                aop.before();
                //业务
                target.buy();
                //切面
                aop.after();
            } catch (Exception e) {
                //切面
                aop.exception();
            }
        }
    }
    
  5. 第五个版本:使用动态代理完成第四个版本的优化。

    //使用了ProxyFactory
    public class ProxyFactory {
        //完成返回动态代理的操作
        public static Object getAgent(Service target, AOP aop) {
            return Proxy.newProxyInstance(
                    //类加载器
                    target.getClass().getClassLoader(),
                    //目标对象实现的所有接口
                    target.getClass().getInterfaces(),
                    //代理功能实现
                    new InvocationHandler() {
                        @Override
                        public Object invoke(
                                //生成的代理对象
                                Object proxy,
                                //正在被调用的目标方法
                                Method method,
                                //目标方法的参数
                                Object[] args) throws Throwable {
                            Object obj = null;
                            try {
                                //切面
                                aop.before();    //事务,日志
                                //业务
                                //target.buy();     这样还是写死了
                                obj = method.invoke(target, args);
                                //切面
                                aop.after();
                            } catch (IllegalAccessException e) {
                                //切面
                                aop.exception();
                            }
                            return obj;
                        }
                    }
            );
        }
    }
    

Spring原生AOP实现(Spring支持的AOP的实现)

  1. Before通知:在目标方法被调用前调用,涉及接口:org.springframework.aop.MethodBeforeAdvice;
  2. After通知:在目标方法被调用之后调用,涉及接口:org.springframework.aop.AfterReturningAdvice;
  3. Throws通知:目标方法抛出异常时调用,涉及接口:org.springframework.aop.ThrowsAdvice;
  4. Around通知:拦截对目标对象方法调用,涉及接口:org.springframework.aop.MethodIntercepteor;

AOP常用术语

  1. 切面:就是哪些重复的,公共的,通用的功能称为切面,例如:日志,事务,权限。
  2. 连接点:就是目标方法,是因为在目标方法中要实现目标方法的功能和切面功能的整合。
  3. ==切入点:(PointCut)==指定切入的位置,多个连接点构成切入点,切入点可以是一个目标方法,可以是一个类中的所有方法,可以是某个包下的所有类中的方法。
  4. 目标对象:操作谁,谁就是目标对象。
  5. ==通知(Advice):==来指定切入的时机,是在目标方法执行前,还是在执行后,还是在出错时,还是环绕目标方法切入切面的功能。

AspectJ框架

AspectJ框架是一个优秀的面向切面的框架,它扩展了java语言,提供管理强大的切面实现。因为是基于java语言开发的,所以可以做到无缝扩。本来AOP就是无缝扩展,这个框架更是无缝扩展。对于AOP这种编程思想,很多框架都进行了实现,Spring就是其中之一,可以完成面向切面编程。然而,AspectJ也实现了AOP的功能,且其实现方式更为简单便捷,使用更为方便,而且还支持注解式的开发。所以Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。

​ 在Spring中使用AOP开发时,一般使用AspectJ的实现方式。官网地址:http://eclipse.com/AspectJ

AspectJ的通知类型(理解即可)
  1. 前置通知:@Before:在目标方法执行前执行的通知
  2. 后置通知:@AfterReturning:在目标方法执行之后执行的通知
  3. 环绕通知:@Around:伴随目标方法执行的时候执行的通知,主要用于事务!最强大的通知
  4. 最终通知:@After:无论目标方法执行是否成功,都会执行此通知
  5. 定义切入点:@Pointcut:简化定义切入点表达式。
AspectJ的切入点表达式(掌握

AspectJ定义了专门的表达式用于指定切入点。表达式的原型是:

excution(modifiers-pattern? ret-type-pattern
	declaring-type-pattern?name-pattern(param-pattern)
	throws-pattern?)
	//

规范的公式

execution(访问权限 方法返回值 方法声明(参数) 异常类型)

简化后的公式

execution(方法返回值 方法声明(参数))

用到的符号

*:代表任意个任意的字符(类似通配符,啥都能匹配);

…:分情况

  1. 如果出现在方法的参数中,则代表任意参数,
  2. 如果出现在路径中,则代表本路径及其所有的子路径。

示例:

execution(public * *(…)) :任意的公共的方法

访问权限:所有公共的访问方法,* 代表方法返回值 *(…)代表方法声明,任意方法的任意参数,异常类型可以省略,省略了。

execution(* set*(…)) :任何一个以set开始的方法。任意返回值类型的任意的set方法

访问权限省略了,* 代表方法返回值 set*任意路径下的以set打头的任意的方法,任意的参数。

execution(* com.xyz.service.impl.*.*(…)):

返回值类型是* com.xyz.service.impl.*.* 任意的返回值类型,在com.xyz.service.impl包下的任意类的任意方法的任意参数的方法上添加切面功能。

execution(* com.xyz.service…*.*(…)) :

任意的返回值类型,在这个包下面及其子包下的任意类的任意的方法的任意的参数。

execution(* *…service.*.*(…)):任意多个子包任意类下的任意方法;service之前可以有任意的子包

execution(* *.service.*.*(…)):service之前只有一个包,只能有一级包。

AspectJ的前置通知 @Before

@Before:在目标方法执行前切入切面的功能,在切面方法中不可以获得目标方法的返回值,只能得到目标方法的签名。

切面方法可以得到目标方法的啥?可以得到目标方法的签名。

public void myBefore(){//得到目标方法的签名,前面的功能,得到目标方法的签名,就是目标方法的方法名那一大串
}
  1. 创建业务接口

  2. 创建业务实现

  3. 创建切面类,实现切面方法

  4. 在applicationContext.xml文件中进行注册(进行切面绑定)

        /**
         * 此类为切面类,包含各种切面方法
         * 所有切面的功能,都是有切面方法来实现的可以将各种切面都在此类中进行开发,也可以多做几个切面类
         * 前置通知的切面方法的规范
         *  1,访问权限是public
         *  2,返回值为void
         *  3,方法名称自定义
         *  4,方法没有参数,如果有也只能是JoinPoint类型(一般都没有)
         *  5,必须使用@Before注解来声明切入的时机和切入点
         *  6,注解的参数value指定切入点表达式。
         */
        //切面功能      public String doSome(String name ,int age)
        @Before(value = "execution(* com.bjpowernode.s01.*.*(..))")
        public void MyBefore(JoinPoint joinPoint){
            System.out.println("切面方法中的前置功能实现......");
            System.out.println("目标方法的签名"+joinPoint.getSignature());
            System.out.println("目标方法的参数"+ Arrays.toString(joinPoint.getArgs()));
        }
    }
    
AspectJ框架切换JDK动态代理和CGlib动态代理
默认是JDK动态代理,取时必须使用接口类型
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
设置为CGLib子类代理,可以使用该接口和实现类接。
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
记住:使用接口来接,永远也不会出错。
AspectJ的后置通知 @AfterReturning

方法的参数分为值传递和引用传递。取决于目标方法返回值的类型。可以得到目标方法的返回值的类型。

  1. 如果目标方法的返回值时8种基本类型或String类型,则不可改变

  2. 如果目标方法的返回值是引用类型,则可以改变。

        /**
         * 后置通知的方法的规范
         *  1,访问权限是public
         *  2,方法没有返回值void
         *  3,方法名称自定义
         *  4,方法有参数(也可以没有参数,如果目标方法没有返回值,则可以写无参的方法,但一般会写有参
         *  这样可以处理无参,可以处理有参,)这个切面方法的蚕食就是目标方法的返回值
         *  5,使用@AfterReturning注解表明是后置通知
         *      参数
         *          value:指定切入点表达式
         *          returning:指定目标方法的返回值的名称,则名称必须于切面方法的参数名称一致
         */
        @AfterReturning(value = "execution(* com.bjpowernode.s02.*.*(..))",returning = "obj")
        public void myAfterReturning(Object obj) {
            System.out.println("后置通知中的功能实现。。。。。。");
            if (obj != null){
                if (obj instanceof String) {
                    obj = obj.toString().toUpperCase();
                    System.out.println("切面方法中目标方法的返回值为:"+obj);
                } else if (obj instanceof Student) {
                    Student stu = (Student) obj;
                    stu.setName("李四");
                    System.out.println("在切面方法中目标方法的返回值是:"+stu);
                }
            }
        }
    }
    
AspectJ的环绕通知 @Around
  1. 它是通过拦截目标方法的方式,在目标方法前后增强功能的通知。它是功能最强大的通知,一般事务使用此通知。它可以轻易的改变目标方法的返回值。

  2. 目标方法就是环绕通知方法的参数。切面方法的返回值就是目标方法的返回值。它是功能最强大的通知,所以它是最强大的返回值。

    @Aspect
    @Component
    public class MyAspect {
        /**
         * 环绕通知方法的规范
         *  1,访问权限是public
         *  2,切面方法有返回值,此返回值就是目标方法的而返回值
         *  3,方法名称自定义
         *  4,方法有参数,此参数就是目标方法
         *  5,回避异常,最底层的操作,必须要回避
         *  6,使用@Around注解声明是环绕通知
         *      参数
         *          value:指定切入点表达式
         */
        @Around(value = "execution(* com.bjpowernode.s03.*.*(..))")
        public Object MyAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //前切功能实现
            System.out.println("环绕通知中的前置通知功能实现。。。。。。");
            //目标方法调用
            Object obj = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
            //后切功能实现
            System.out.println("环绕通知中的后置通知功能实现......");
            return obj.toString().toUpperCase();        //改变了目标方法的返回值
        }
    }
    
AspectJ的最终通知 @After
  1. 无论目标方法是否正常,最终通知都会被执行。目标方法可能会发生问题,但是最终通知一定会执行。类似于finally。

    @Aspect
    @Component
    public class MyAspect {
        /**
         * 最终通知方法的规范
         *  1,访问权限是public
         *  2,方法没有返回值
         *  3,方法名称自定义
         *  4,方法没有参数,如果有也只能是JoinPoint
         *  5,使用@After注解表明是最终通知
         *      参数
         *       value:指定切入点表达式
         */
        @After(value = "execution(* com.bjpowernode.s04.*.*(..))")
        public void myAfter(){
            System.out.println("最终通知的功能");
        }
    }
    
给切入点表达式起别名 @Pointcut
  1. 如果多个切面切入到同一个切入点,可以使用别名简化开发
  2. 使用@Pointcut注解创建一个空方法,此方法的名称就是别名
    @Pointcut(value = "execution(* com.bjpowernode.s04.*.*(..))")
    public void myCut(){}
//使用的时候
    @After(value = "myCut()")
    public void myAfter(){
        System.out.println("最终通知的功能");
    }

第四章 Spring集成MyBatis(难点

  1. 将MyBatis与Spring框架进行整合,主要解决的问题就是将SqlSessionFactory对象交由Spring来管理。所以,该整合只需要将SqlSessionFactory的对象生成器SqlSessionFactoryBean注册在Spring容器中,再将其注入给Dao的实现类即可完成整合。实现Spring与MyBatis的整合。常用的方式:扫描Mapper动态处理。Spring像插线板一样,myBatis框架是插头,可以容易的组合到一起。插线板Spring插上MyBatis,两个框架就是一个整体。

  2. SM整合的步骤

    1. 建表

    2. 新建项目,选择quickStart模板

    3. 修改目录

    4. 修改pom文件,添加依赖。(Spring,JDBC,MyBatis,德鲁伊数据库连接池)

    5. 添加MyBatis相应的模板(SqlMapperConfig.xml和XXXMapper.xml文件)

    6. 添加sqlMapConfig.xml(MyBatis核心配置文件),并拷贝jdbc.properties文件到resources目录下

    7. 添加applicationContext_mapper.xml

    8. 添加applicationContext_serviec.xml

    9. 添加Users实体类和Account实体类

    10. 添加mapper包,添加UserMapper接口和UserMapper.xml文件并开发

    11. 添加Service包,添加UserService接口和UserServiceImpl实现类

    12. 添加测试类进行功能测试

4.1Spring的事务管理

基于注解的事务的添加

  1. 如何实现事务的功能

    //这个注解可以添加在类上,可以添加在方法上。添加到类上,可以对类中所有的方法进行事务的管理,添加到方法上,只是对这个方法进行事务的管理。
    @Transactional(propagation = Propagation.REQUIRED, //事务的传播特性,能够规定,如果多个事务叠加到一起,由哪个事务说了算,会决定其他事务互斥的特性。
            noRollbackForClassName = {"ArithmeticException"})//指定发生什么异常不回滚,使用的是异常的名称。
            //noRollbackFor = ArithmeticException 指定发生什么异常不会回滚,使用的是异常的类型
            //rollbackForClassName =指定发生什么异常必须回滚,指定异常名称(按异常名称指定)
            //rollbackFor =指定发生什么异常必须回滚,指定异常类型(按异常的类型进行指定)
            //timeout =  -1 连接超时设置,默认值是-1,表示永不超时 
            //readOnly = true  默认是false,如果是查询操作,必须设置为true
            //isolation =   Isolation.DEFAULT   使用数据库自己的隔离级别,
    

Spring的两种事务处理方式

需要在配置文件中添加配置,在实现类添加注解。

  1. 注解式的事务

  2. 声明式的事务(必须掌握)

在配置文件中添加一次,整个项目遵循事务的设定。只要添加一次即可。所有类中所有方法都符合事务的约束。必须要掌握的这种方式。

事务的5大隔离级别:

每个数据库都有自己的隔离级别。

  1. 未提交读(Read Uncommited):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

  2. 提交读(Read Commited):之恶能读取到已经提交的数据。Oracle等多数数据库默认都是该级别,不重复读。

  3. 可重复读(Repeated Read):可重复读,在用一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻像读,但是innoDB解决了幻读。

  4. 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会堵塞。

  5. 使用数据库默认的隔离级别:isolation = Isolation.DEFAULT;

    MySql默认的事务处理级别是:”REPEATABLE-READ“,也就是可重复读。

    Oracle:oracle数据库支持READ COMMITED 和 SERIALIZABLE 两种事务隔离级别。

    默认事务隔离级别是:READ COMMITED,也就是读已提交。

为什么添加事务管理器
  1. 在JDBC的访问中,事务的提交创建的是Connection对象,con.commit()提交事务,con.rollback()提交回滚。

  2. MyBatis:sqlsession,sqlSession.commit()提交;SQLSession.rollback();

  3. Hibernate:session,session.commit();session.rollback();

    使用哪种框架就需要生成响应的对象。

事务管理其用来生成响应技术的连接+执行语句的对象。可能会生成Connection,session,sqlSession对象。

如果使用MyBatis框架,必须使用DataSourceTransationManageer对象。

    <!--事务处理-->
    <!--1,添加事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--2,配置数据源,因为事务必须关联数据库处理,所以要配置数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--3,添加事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

如果使用MyBatis框架必须使用上述的对象,按照上述流程进行操作。声明这个之后就能完成事务的提交和事务的回滚。

如果是Spring+Hibernate框架得使用HibernateTransactionManger对象。(了解)

事务的传播特性

大多数情况下,不需要我们处理。

  1. 多个事务之间的合并互斥等都可以通过设置事务的传播特性来解决。

常用的传播特性

PROPAGATION_REQUIRED:必被包含事务(曾删改必用)

PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务

PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务

PROPAGATION_NEVER:不能运行中事务,如果包在事务中,抛异常

PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境

不常用的事务

PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常

PROPAGTION_NESTED:嵌套事务

项目中的所有事务必须添加到业务逻辑层上

声明式事务
  1. Spring非常有名的事务处理方式,声明式事务,要求项目中的方法命名有规范。
    1. 完成增加的操作,要包含 add save insert set
    2. 更新操作要包含:update change modify
    3. 删除操作要包含:delete drop remove clear
    4. 查询操作要包含:select find search get
  2. 进行切面配置的时候可以使用通配符进行配置。
  3. 配置事务切面的时候,可以使用通配符 * 来匹配所有

总结

核心及时式IOC和AOP,统一进行对象的创建和依赖管理,提取公共的业务集中给开发,使用时分别织入。

特点

  1. 轻量级
  2. 业务代码零污染
  3. 面向接口编程
  4. 面向切面编程(可以对公共的,通用的代码只写一次处理,避免重复的写大量的代码,写一遍,处处用,减轻开发的压力,代码复用,易于扩展)
  5. 优化框架并整合框架;

IOC

  • 控制反转(Inversion Of Control)是一种思想,由容器集中尽心搞对象的创建和依赖的管理,所有三层对象的创建和管理由Spring管理

  • 实现方式

    • xml的方式

      • 通过bean标签创建对象,id是创建的对象的名称,class是创建对象的类型

        • 通过xxxsetter创建(必须要有无参构造方法和xxxsetter方法)
          • 通过value属性进行简单类型的值的注入
          • 通过ref进行引用类型值的注入
        • 构造方法创建(类中必须要提供响应的构造方法)
          • 使用构造方法参数名称注入
          • 使用构造方法参数下标注入
          • 使用构造方法参数默认的位置进行注入(可以有无参的构造方法)
        • 引用类型自动注入
          • 引用类型似乎可以自动注入的,但凡是引用类型可以使用byNname及逆行注入
          • byName:按照成员变量的名称,在Apring同期中找到相同id的对象,自动进行注入,如果没有相同名称的对象尽心关注如,则注入失败,注入null值,但不会报错
          • byType:按照成员变量类型进行注入,在Spring中容器中找到同源的对象,自动进行注入同源。
            • java类中的成员变量的类型与xml文件中bean对象的类型一样
            • java类中的成员变量的累心关于xml文件中bean对象的是父子类型
            • java类中的成员变量的类型与xml文件中bena对象的类型是接口与实现类型,
            • 注意:一种累心高智能有一个对象,多于一个对象时,注入报错
      • 基于注解的方式

        • 切记在spring的配置文件中添加扫描组件(添加包扫描)

          <context:component-scan base-package="com.bjpowernode.pojo.s03"></context:component-scan>
          
        • 创建对象注解

          • @Compontent:可以创建所有对象,但一般用于除了控制器,业务逻辑,数据访问对象的创建
          • @Controller:专门用于控制器对象的创建,此对象可以接受用户的请求,并且返回处理结果
          • @Service:专门用于业务逻辑层对象的创建, 此对象可以世家事务的管理,向上给控制器提供该数据
          • @Respostory:专门用来进行数据访问层对象的创建,进行数据库中的增删改查操作
        • 赋值的注解

          • @Value:给简单类型(8种基本类型+String)赋值
          • @Autowired:给引用类型按类型注入,相当于byType
          • @Autowired @Qualifier(“myschool”)两个注解一起使用,表示给引用类型按名称注入,相当于byName
      • 基于注解,基于xml文件的配置文件的拆分(applicationContext_mapper.xml,applicationContext_service.xml等文件,合并的时候使用标签即可)

AOP

  1. 面向切面编程AOP(Aspect Orient Programming),将公共的,通用的功能,业务集中开发,在使用时再织入的一种技术,底层使用的是JDK动态代理或者是CGLib动态代理的方式。

  2. 事务,日志,权限,缓存,检查,等这些公共的通用的操作就是切面。谁用谁绑定。

  3. 手写AOP

    1. 拆分 5个案例从最基本理解AOP;将业务和业务分开,切面和切面分开。

    2. 总结

      1. 提取业务和切面接口,使切面更加灵活
      2. 分别实现业务和切面的实现口,替换接口的实现类则替换整个功能实现
      3. 使用JDK动态代理,完美的烤箱了Spring的AOP的处理方式,实现切面与业务的分离。
    3. SpringAOP的通知

      1. Before通知
      2. After通知
      3. Throws通知
      4. Around通知
    4. AspectJ框架(要掌握)

      1. 优秀的面向切面的框架,扩展了java语言,提供了钱打的切面的实现。

      2. 常用术语

        1. 切面:公共的通用的方法
        2. 连接点:指可以被切面植入的具体的业务的方法,就是我们说的目标方法,
        3. 切入点:指一个或多个连接点的集合,包可以是切入点,类可以是切入点,类中某个方法也可能是切入点。
        4. 目标对象:目标对象就是指要被增强的对象。扩展功能的对象,包含主业务逻辑的类的对象。
        5. 通知:通知表示执行的实际,通知也叫增强。通知来决定切面切入到目标对象的实际,是目标对象的
        6. 总结:切入点决定切入的位置,通知决定切入的时机。
      3. 切入点表达式

        1. 原型

          execution(访问权限 方法返回值 方法声明(参数) 异常类型)

          简化后的公式

          execution(方法返回值 方法声明(参数))

      4. AspectJ通知

        • @Before:用来指定切入的时机,使用此注解表示在业务方法(目标方法)执行前,切入切面功能,是一个前置通知
        • @AfterReturning:此通知为后置通知,在目标方法执行后切入切面的功能,也就是增强功能。后置得到的目标方法的返回值是通知方法传入的。返回值类型如果是8种基本的类型,不可改变,如果是引用类型,则可以改变。
        • @Arround:(最强大的通知):增强方法有:ProceedingJoinPoint参数,在目标方法子还行前后都可以增强功能(事务),它可以直接改变目标方法的返回值。因为在切面方法种直接得到目标方法的返回。想改啥改啥,想怎么改怎么改。
        • @After通知:不管目标方法如何执行,都会执行的一个通知,类似trycatch中的final语句@PointCut给切入点表达式起别名。
    5. 事务:事务

      1. 在数据库中的多个操作之间,保证要么都成功,要么都失败。所有的操作都是一个操作一样。四大特性:原子性,一致性,持久性,隔离性。

      2. 事务的隔离级别:MySql数据库的隔离级别是可重复读。oracle数据库的隔离级别是:读已提交。

      3. 事务的传播特性

        1. REQUIRED:必须包含事务
        2. REQUIRED_NEW:自己新开事务,不管之前是否有事务发生。
        3. SUPPORTS:支持事务,如果对方加入的方法有事务,则支持事务。
        4. NEVER:不能运行在事务中,如果包在事务中,抛异常。
        5. NOT_SUPPORTED:不支持事务,运行在非事务环境中,程序结束。
      4. xml配置文件的方式声明事务

            <!--此配置文件与applicationContext_Service.xml的功能一样,只是事务配置不同-->
            <!--导入applicationContext_mapper.xml文件-->
            <import resource="applicationContext_mapper.xml"></import>
            <!--添加包扫描-->
            <context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
            <!--添加事务管理器-->
            <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
            </bean>
            <!--添加注解事务(非必须,仅作认识,必要的时候使用,配合下方的order属性。)-->
            <tx:annotation-driven order="90"></tx:annotation-driven>
            <!--配置事务切面-->
            <tx:advice id="myadvice" transaction-manager="transactionManager">
                <tx:attributes>
                    <tx:method name="*select*" read-only="true"/>
                    <tx:method name="*find*" read-only="true"/>
                    <tx:method name="*search*" read-only="true"/>
                    <tx:method name="*get*" read-only="true"/>
                    <tx:method name="*insert*" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
                    <tx:method name="*add*" propagation="REQUIRED"/>
                    <tx:method name="*save*" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
                    <tx:method name="*update*" propagation="REQUIRED"/>
                    <tx:method name="*change*" propagation="REQUIRED"/>
                    <tx:method name="*modify*" propagation="REQUIRED"/>
                    <tx:method name="*delete*" propagation="REQUIRED"/>
                    <tx:method name="*remove*" propagation="REQUIRED"/>
                    <tx:method name="*drop*" propagation="REQUIRED"/>
                    <tx:method name="*clear*" propagation="REQUIRED"/>
                    <tx:method name="*" propagation="SUPPORTS"/>
                </tx:attributes>
            </tx:advice>
            <!--绑定切面和切入点-->
            <aop:config>
                <aop:pointcut id="mycut" expression="execution(* com.bjpowernode.service.impl.*.*(..))"/>
                <aop:advisor advice-ref="myadvice" pointcut-ref="mycut" order="50"></aop:advisor>
            </aop:config>
        

        需要添加事务管理器,Mabits的事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager

    添加事务前先添加事务管理器,而事务管理器就是帮助我们生成底层数据库提交或者回滚的对象,更根据不同的框架生成不同的对象的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NoSuchManException

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值