栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring笔记

目录

一、Spring概述

二、Spring IoC — 基于XML

三、Spring IoC — 基于注解

四、代理设计模式

五、Spring AOP

六、Spring AOP 注解配置

七、Spring整合MyBatis

八、基于Spring的单元测试


一、Spring概述

1.1 web项目开发中的耦合度问题

  • 在Servlet中需要调用service中的方法,则需要在Servlet类中通过new关键字创建Service的实例

    public interface ProductService{
        public List listProducts();
    }
    public class ProductServiceImpl1 implements ProductService{
        public List listProducts(){
            //查询热销商品
        }
    }
    public class ProductServiceImpl2 implements ProductService{
        public List listProducts(){
            //查询好评商品
        }
    }
    public class ProductListServlet extends HttpServlet{
        
        //在servlet中使用new关键字创建ProductServiceImpl1对象,增加了servlet和service的耦合度
        private ProductService productService = new ProductServiceImpl1();
        
        protected void doGet(HttpServletRequest request,HttpServletResponse response){
            doPost(request,response);
        }
        protected void doPost(HttpServletRequest request,HttpServletResponse response){
            productService.listProducts();
        }
    }

  • 在service实现类中需要调用DAO中的方法,也需要在servcie实现类通过new关键字创建DAO实现类对象

  • 如果使用new关键字创建对象:

    • 失去了面向接口编程的灵活性

    • 代码的侵入性增强(增加了耦合度)、降低了代码的灵活性

    • 增强项目的扩展性

1.2 面向接口编程

面向接口编程

解决方案:在Servlet中定义Service接口的对象变量,不使用new关键字创建实现类对象,在servlet的实例化的时候,通过反射动态的给Service对象变量赋值。

如何实现:Spring可以做到!!!

1.3 Spring介绍

Spring是一个轻量级的控制反转和面向切面的容器框架,用来解决企业项目开发的复杂度问题—解耦

  • 轻量级:体积小,对代码没有侵入性

  • 控制反转:IoC(Inverse of Control),把创建对象的工作交由Spring完成,Spring在创建对象的时候同时可以完成对象属性赋值(DI)

  • 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强

  • 容器:实例的容器,管理创建的对象

1.4 Spring架构

  • 官网 Spring | Home

  • Spring架构图

  • 1.4.1 Core Container

Spring容器组件,用于完成实例的创建和管理

  • core

  • beans 实例管理

  • context 容器上下文

1.4.2 AOP、Aspects

Spring AOP组件,实现面向切面编程

  • aop

  • aspects

1.4.3 web

Spring web组件实际指的是SpringMVC框架,实现web项目的MVC控制

  • web (Spring对web项目的支持)

  • webmvc (SpringMVC组件)

1.4.4 Data Access

Spring数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化操作)

  • tx

1.4.5 Test

Spring的单元测试组件,提供了Spring环境下的单元测试支持

  • test

二、Spring IoC — 基于XML

Spring IoC 容器组件,可以完成对象的创建、对象属性赋值、对象管理

2.1 Spring框架部署(IoC)

2.1.1 创建Maven工程

  • Java

  • Web

2.1.2 添加SpringIoC依赖

  • core

  • beans

  • aop

  • expression

  • context


    org.springframework
    spring-context
    5.2.13.RELEASE

2.1.3 创建Spring配置文件

通过配置文件"告诉"Spring容器创建什么对象,给对象属性赋什么值

  • 在resources目录下创建名为applicationContext.xml的文件(文件名是可以自定义的)



        
        
        

2.2 SpringIoC使用

使用 SpringIoC组件创建并管理对象

2.2.1 创建一个实体类

public class Student {
​
    private String stuNum;
    private String stuName;
    private String stuGender;
    private int stuAge;
    private Date enterenceTime; //入学日期
​
}

2.2.2 在Spring配置文件中配置实体类



​
        
        
                
                
                
                
        

2.2.3 初始化Spring对象工厂,获取对象

  • ClassPathXMLApplicationContext

//1.初始化Spring容器,加载Spring配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过Spring容器获取Student对象
Student student2 = (Student) context.getBean("stu");

2.3 IoC和DI

  • IoC (Inverse of Control) 控制反转,通过Spring对象工厂完成对象的创建

  • DI (Dependency Injection)依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值

  • 谈谈对Spring IOC的理解 - 孤傲苍狼 - 博客园  

  

  • 2.3.1 IoC 

当我们需要通过Spring对象工厂创建某个类的对象时候,需要将这个交给Spring管理——通过bean标签配置

2.3.2 DI

通过Spring容器给创建的对象属性赋值


    

2.4 DI依赖注入

2.4.1 依赖注入三种方式

Spring容器加载配置文件之后,通过反射创建类的对象,并给属性赋值;

Spring容器通过反射实现属性注入有三种方式:

  • set方法注入

  • 构造器注入

  • 接口注入(不常用)

2.4.2 set方法注入

在bean标签中通过配置property标签给属性属性赋值,实

际上就是通过反射调用set方法完成属性的注入

简单类型及字符串

  • 直接通过property标签的value属性赋值


    
    
    
    
    

日期类型

  • 方式1:在property标签中通过ref引用Spring容器中的一个对象


    
    
  • 方式2:在property标签中添加子标签bean来指定对象


    
    
        
    
  • 方式3

    < bean id = "simpleDateFormat" class = "java.text.SimpleDateFormat" >
              < constructor-arg value = "yyyy-MM-dd" >
    
         < bean id = "dateTest" class = "com.yiibai.pojo.DateTest" >
            < property name = "date" >
                 
                 
                     < constructor-arg value = "2017-11-24" >
                 
           
    
    
    
    < bean id = "simpleDateFormat" class = "java.text.SimpleDateFormat" >
         < constructor-arg value = "yyyy-MM-dd" >
    
          
          < constructor-arg value = "2017-11-24" >
    
    ​
    < bean id = "dateTest" class = "com.yiibai.pojo.DateTest" >
         < property name = "date" ref="date" />             
    ​
    
    ​
     
    
    public String toString() {
            SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd");
    ​
            return "Student{" +
                    "stuNum='" + stuNum + ''' +
                    ", stuName='" + stuName + ''' +
                    ", stuGender='" + stuGender + ''' +
                    ", stuAge=" + stuAge +
                    ", enterenceTime=" + sim.format(enterenceTime) +
                    '}';
        }

自定义类对象属性

  • 方式1:


    
    

    
    
  • 方式2


    
    
        
            
            
        
    

集合类型

  • List

    • List List中的元素是字符串或者简单类型的封装类

    • 以下都在标签中

    • 第一种方式

      第二种方式
    
    
        
            旅游
            电影
            Java
        
    
    • List List中的元素是对象类型

      
          
              
              
              
              
          
      
      
       
                      
                          
                              
                          
                          
                              
                          
                      
      
      
          
              
                
              
          
      

    • Set

      
          
              
          
      

    • Map

      
          
              
                  
                      k1
                  
                  123
              
              
                  
                      k2
                  
                  456
              
          
      

    • Properties

      
          
              aaa
              bbb
          
      

    • 2.4.3 构造器注入

      简单类型、字符串、对象

      public class Student {
      ​
          private String stuNum;
          private String stuName;
          private String stuGender;
          private int stuAge;
          private double weight;
          private Date enterenceTime; //入学日期
          private Clazz clazz;
      ​
          public Student(String stuNum, String stuName, String stuGender, int stuAge, double weight, Date enterenceTime, Clazz clazz) {
              this.stuNum = stuNum;
              this.stuName = stuName;
              this.stuGender = stuGender;
              this.stuAge = stuAge;
              this.weight = weight;
              this.enterenceTime = enterenceTime;
              this.clazz = clazz;
          }
      }
      
                 
          
          
                 
          
                 
                            
              
          
      

      集合类型属性

      public class Student{
          private List hobbies;
          private Set sets;
          private Map maps;
          private Properties properties;
      ​
          public Student(List hobbies, Set sets, Map maps, Properties properties) {
              this.hobbies = hobbies;
              this.sets = sets;
              this.maps = maps;
              this.properties = properties;
          }
      }
      
          
              
                  11
                  22
                  33
              
          
          
              
                  aa
                  bb
                  cc
              
          
          
              
                  
                      key1
                      value1
                  
                  
                      key2
                      value2
                  
              
          
          
              
                  v1
                  v2
              
          
      

      2.5 Bean的作用域

      在bean标签可以通过scope属性指定对象的的作用域

      • scope="singleton" 表示当前bean是单例模式(默认饿汉模式,Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置 lazy-init="true"变为懒汉模式)

      • scope="prototype" 表示当前bean为非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象

      • 单例

      • 多例

      2.6 Bean的声明周期方法

      在bean标签中通过init-method属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行,通过destroy-method属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行-->

      • Bean类

        public class Book {
        ​
            private int bookId;
            private String bookName;
        ​
             //初始化方法:在创建当前类对象时调用的方法,进行一些资源准备工作
            public void init(){
                System.out.println("-------init");
            }
        ​
            //销毁方法:在Spring容器销毁对象时调用此方法,进行一些资源回收性的操作
            public void destory(){
                System.out.println("-------destory");
            }
        }

      • Spring配置文件

      2.7 自动装配

      自动装配:Spring在实例化当前bean的时候, 从Spring容器中找到匹配的实例, 赋值给当前bean的属性

      自动装配策略有两种:

      • byName 根据当前Bean的属性名在Spring容器中寻找匹配的对象 ,如果根据name找到了bean但是类型不匹配则抛出异常

        byName只会自动寻找其他Bean的 id值是否为当前Bean属性所需要赋值的属性名 , 如果当前属性值是clazz , 而Clazz Bean的id值是clazz,则匹配 . 如果是clazz2或者其它值 , 则找不到.

      • byType 根据当前Bean的属性类型在Spring容器中寻找匹配的对象,如果根据类型找到了多个bean也会抛出异常

      • byName

      
      
      
      • byType

      2.7.1 使用案例

      public class ShoppingServlet {
      ​
          private Service service;
          
          //凡是依赖注入,都需要给予set方法
          public void setService(Service service){
              this.service = service;
          }
      ​
          public void doGet(){
              doPost();
          }
      ​
          public void doPost(){
              service.buy();
          }
      }
      ​
      ​
      public class ServiceImpl implements Service {
      ​
         public void buy(){
             System.out.println("您购买了商品");
          }
      }
      ​
       
      

      2.8 SpringIoC 工作原理

      三、Spring IoC — 基于注解

      SpringIoc的使用,需要我们通过XML将类声明给Spring容器进行管理,从而通过Spring工厂完成对象的创建及属性值的注入;

      Spring除了提供基于XML的配置方式,同时提供了基于注解的配置:直接在实体类中添加注解声明给Spring容器管理,以简化开发步骤。

      3.1 Spring框架部署

      3.1.1 创建Maven项目

      3.2.2 添加SpringIoC依赖

      
          org.springframework
          spring-context
          5.2.13.RELEASE
      

      3.2.3 创建Spring配置文件

      • 因为Spring容器初始化时,只会加载applicationContext.xml文件,那么我们在实体类中添加的注解就不会被Spring扫描,所以我们需要在applicationContext.xml声明Spring的扫描范围,以达到Spring初始化时扫描带有注解的实体类并完成初始化工作

      
      
      ​
          
          
      ​
          
          

      3.2 IoC常用注解

      3.2.1 @Component

      • 类注解,声明此类被Spring容器进行管理,相当于bean 标签的作用

      • @Component(value="stu") value属性用于指定当前bean的id,相当于bean标签的id属性;value属性也可以省略,如果省略当前类的id默认为类名首字母改小写

      • 除了@Component之外 @Service、@Controller、@Repository这三个注解也可以将类声明给Spring管理,他们主要是语义上的区别

        • @Controller 注解主要声明将控制器类配置给Spring管理,例如Servlet

        • @Service 注解主要声明业务处理类配置Spring管理,Service接口的实现类

        • @Repository 直接主要声明持久化类配置给Spring管理,DAO接口

        • @Component 除了控制器、servcie和DAO之外的类一律使用此注解声明

        如何像xml配置那样给属性赋值?

        给类设置默认值,就是在类中给属性设置默认值

      3.2.2 @Scope

      • 类注解,用于声明当前类单例模式还是 非单例模式,相当于bean标签的scope属性

      • @Scope("prototype") 表示声明当前类为非单例模式(默认单例模式)

      3.2.3 @Lazy

      • 类注解,用于声明一个单例模式的Bean是否为懒汉模式

      • @Lazy(true) 表示声明为懒汉模式,默认为饿汉模式

      3.2.4 @PostConstruct

      在方法前注解

      • 方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性

      3.2.5 @PreDestroy

      • 方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destory-method属性

      3.2.6 @Autowired

      在属性前注解

      • 属性注解、方法注解(set方法),声明当前属性自动装配,默认byType 方法注解和属性注解在这里是相同作用

      • @Autowired(required = false) 通过requried属性设置当前自动装配是否为必须,设置false就按属性类型找,找到则返回对象,找不到就是默认值null(默认必须——如果没有找到类型与属性类型匹配的bean则抛出异常)

        • byType

        • ref引用 ( 通过byType找到了多个 , 所以 用@Quelifier("") 指定使用哪一个 )

        @Qualifier(" clazz ") 是表明当前匹配多个相同类型对象 , 按照name在这些相同类型对象里找,找到name相同返回对象,如果一个其它类注解为@Component(“ clazz ”),即使找到也会报类型错误

         
        @Autowired
        public void setClazz(@Qualifier("clazz") Clazz clazz) {
            this.clazz = clazz;
        }

      3.2.7 @Resource

      • 属性注解,也用于声明属性自动装配

      • 默认装配方式为byName,如果根据byName没有找到对应的bean,则继续根据byType寻找对应的bean,根据byType如果依然没有找到Bean或者找到不止一个类型匹配的bean,则抛出异常。

      四、代理设计模式

      4.1 生活中的代理

      代理设计模式的优点:将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

      4.2 静态代理

      静态代理,代理类只能够为特定的类生产代理对象,不能代理任意类

      下面不足的地方就是MyStaticProxy类应该实现GenaralDao接口

      一个典型的静态代理模式通常有三个角色,这里称之为代理三要素 ,共同接口,真实对象,代理对象

      使用代理的好处

      1.被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离

      2.将通用的代码放在代理类中实现,提供了代码的复用性

      3.通过在代理类添加业务逻辑,实现对原有业务逻辑的扩展(增强)

      使用代理的缺点

      4.3 动态代理

      动态代理,几乎可以为所有的类产生代理对象

      动态代理的实现方式有2种:

      • JDK动态代理

      • CGLib动态代理

      4.3.1 JDK动态代理

      • JDK动态代理类实现:

      public class JDKDynamicProxy implements InvocationHandler {
          //被代理对象
          private Object obj;
          public JDKDynamicProxy(Object obj) {
              this.obj = obj;
          }
          //产生代理对象,返回代理对象
          public Object getProxy(){
              //1.获取被代理对象的类加载器
              ClassLoader classLoader = obj.getClass().getClassLoader();
              //2.获取被代理对象的类实现的接口
              Class[] interfaces = obj.getClass().getInterfaces();
              //3.产生代理对象(通过被代理对象的类加载器及实现的接口)
              //第一个参数:被代理对象的类加载器
              //第二个参数:被代理对象实现的接口
              //第三个参数:使用产生代理对象调用方法时,用于拦截方法执行的处理器
              //第三个参数也可以不用实现接口(不推荐) 直接创建InvocationHandler内部类, 然后传递这个内部类对象名到第三个参数
              Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
              return proxy;
          }
              //调用被代理对象的任何方法,都会被jvm导向这个invoke方法,详情见Nyima动态代理笔记的知乎连接
              //                                              args是调用方法时传入的该方法需要的参数
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              begin();
              Object returnValue = method.invoke(obj,args);  //执行method方法(insert)
              commit();
              return returnValue;
          }
      ​
          public void begin(){
              System.out.println("----------开启事务");
          }
      ​
          public void commit(){
              System.out.println("----------提交事务");
          }
      }
      • 测试

      //创建被代理对象
      BookDAOImpl bookDAO = new BookDAOImpl();
      StudentDAOImpl studentDAO = new StudentDAOImpl();
      ​
      //创建动态代理类对象,并将被代理对象传递到代理类中赋值给obj
      JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(studentDAO);
      ​
      //proxy就是产生的代理对象:产生的代理对象可以强转成被代理对象实现的接口类型 ---------------->重要 底下被强转的是接口不是实现类
      GenaralDAO proxy = (GenaralDAO)jdkDynamicProxy.getProxy();
      ​
      //使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类种的invoke方法
      //调用的方法作为一个Method参数,传递给了invoke方法
      proxy.insert(student);

      4.3.2 CGLib动态代理

      由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象。如果一个类没有实现任何接口,该如何产生代理对象呢?

      CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象

      CGLib动态代理不能为final类创建代理对象

      • 添加CGLib的依赖

      
          cglib
          cglib
          3.3.0
      
      • CGLib动态代理实现:

      public class CGLibDynamicProxy implements MethodInterceptor {
      ​
          private Object obj;
          public CGLibDynamicProxy(Object obj) {
              this.obj = obj;
          }
      ​
          public Object getProxy(){
              Enhancer enhancer = new Enhancer();
              enhancer.setSuperclass(obj.getClass());
              enhancer.setCallback(this);
              Object proxy = enhancer.create();
              return proxy;
          }
      ​
      ​
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              begin();
              Object returnValue = method.invoke(obj,objects); //通过反射调用被代理类的方法
              commit();
              return returnValue;
          }
      ​
          public void begin(){
              System.out.println("----------开启事务");
          }
      ​
          public void commit(){
              System.out.println("----------提交事务");
          }
      }
      • 测试

      //创建被代理对象
      BookDAOImpl bookDAO = new BookDAOImpl();
      StudentDAOImpl studentDAO = new StudentDAOImpl();
      ​
      //通过cglib动态代理类创建代理对象
      CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
      //代理对象实际上是被代理对象子类,因此代理对象可直接强转为被代理类类型
      BookDAOImpl proxy = (BookDAOImpl) cgLibDynamicProxy.getProxy();
      ​
      //使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数传递到intercept方法
      proxy.update();

      五、Spring AOP

      5.1 AOP 概念

      Aspect Oriented Programming 面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理),对原有的业务逻辑进行拦截,并且可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。

      基于动态代理实现在不改变原有业务的情况下对业务逻辑进行增强

      5.2 Spring AOP框架部署

      5.2.1 创建Maven项目

      5.2.2 添加依赖

      • context

      • aspects

      
          org.springframework
          spring-context
          5.2.13.RELEASE
      
          org.springframework
          spring-aspects
          5.2.13.RELEASE
      

      5.2.3 创建spring配置文件

      • 需要引入aop的命名空间

      
      

      5.3 AOP配置—基于XML

      在DAO的方法添加开启事务和提交事务的逻辑

      5.3.1 创建一个类,定义要添加的业务逻辑

      public class TxManager {
      ​
          public void begin(){
              System.out.println("-----------开启事务");
          }
      ​
          public void commit(){
              System.out.println("-----------提交事务");
          }
      ​
      }

      5.3.2 配置aop

      
      
      ​
          
          
      ​
          
          
          
              
              
      ​
              
              
                  
                  
                  
              
          
      ​
      
      今天在使用spring框架来写一个事务织入的时候出现了如下报错:
      注意出现如下两个错误都可能是因为没有正确配置proxy-target-class的值导致的:
      错误一:No qualifying bean of type ‘com.pjh.service.Imp.serviceImp’ available
      错误二: Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to XXX-------动态代理(proxy-target-class属性的意义)
      ​
      这句话的意思大致为:没有类型为’com.pjh.service.Imp.serviceImp”合格的bean可用
      ​
      为什么?
      查阅资料得:这是由于没有配置proxy-target-class导致的
      proxy-target-class有两个值:true/false
      决定是基于接口的还是基于类的代理被创建。
      如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。
      如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。
      ​
      我使用这段代码来获取:
      声明:serviceImp是实现service接口的父类
      serviceImp bean1 =(serviceImp) classPathXmlApplicationContext.getBean(serviceImp.class);
      而serviceImp是一个类而不是一个接口
      而我在进行织入的时候没有配置proxy-target-class属性,默认就为proxy-target-class=false,这是基于接口的代理所以报错了
      也就是报错的内容 :没有类型为’com.pjh.service.Imp.serviceImp”合格的bean可用
      ​
       
            
            
        
      ​
      解决方案
      解决方法一:
      当我将代码的proxy-target-class的值改为true的时候就不会报错了,即改为基于类的代理
      ​
      
            
            
        
      ​
      解决方法二
      将传入的参数改为接口,则无需添加proxy-target-class,因为默认就是false,就是基于接口代理
      声明:serivce是serviceImp的父类是一个接口
      ​
       service bean1 =(service) classPathXmlApplicationContext.getBean(service.class);
      ​
      ------------------------------------------------------------------------------------------------------------
          理解: Spring-AOP也是使用Spring-ioc获得代理对象的 , 所以在使用context.getBean()时需要指定aop用JDK动态代理还是CGLib动态代理,默认是JDK动态代理,proxy-target-class值为true时是CGLib动态代理.
              
      ------------------------------------------------------------------------------------------------------------
              如果在配置文件中不为创建的类创建切面,等一些AOP操作,则该类就是普通创建的
              但是如果声明了切面且进行了一些AOP通知策略,则该类就会被动态代理
              总结: 只有对被定义为切入点的类,获取该对象时为代理对象
                    而没有被定义为切入点的类,获取该对象时为真实对象
      ​

      AOP开发步骤:

      1.创建切面类,在切面类定义切点方法

      2.将切面类配置给Spring容器

      3.声明切入点

      4.配置AOP的通知策略

      如果要使用Spring-AOP面向切面编程,调用切入点的对象必须通过使用Spring-ioc获取,而这样的获取方式为调用切入点对象的代理对象,因为这样才能进行拦截处理

                                          第一个* 表示有无返回值都可以 
      
      ​
      
                                                                      第二个*表示这个类中所有方法
      
      ​
      
                                                                      ..表示有无参数都可以的方法
      
      ​
      
      
      ​
      
      
      ​
      
      
      ​
      
      
      ​
      
      

      5.4.2 AOP使用注意事项

      //如果要使用Spring aop面向切面编程,调用切入点方法的对象必须通过Spring容器获取
      //如果一个类中的方法被声明为切入点并且织入了切点之后,通过Spring容器获取该类对象,实则获取到的是一个代理对象
      //如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真实创建的对象
      //BookServiceImpl bookService = new BookServiceImpl();
      BookServiceImpl bookService = (BookServiceImpl) context.getBean("bookServiceImpl");
      bookService.addBook();

      5.5 AOP通知策略

      AOP通知策略:就是声明将切面类中的切点方法如何织入到切入点

      • before

      • after

      • after-throwing

      • after-returning

      • around

      5.5.1 定义切面类

      public class MyAspect {
      ​
          public void method1(){
              System.out.println("~~~~~~~method1");
          }
          public void method2(){
              System.out.println("~~~~~~~method2");
          }
          public void method3(){
              System.out.println("~~~~~~~method3");
          }
          public void method4(){
              System.out.println("~~~~~~~method4");
          }
      ​
          //环绕通知的切点方法,必须准守如下的定义规则:
          //1.必须带有一个ProceedingJoinPoint类型的参数
          //2.必须有Object类型的返回值
          //3.在前后增强的业务逻辑之间执行Object v = point.proceed();
          //4.方法最后返回v
          public Object method5(ProceedingJoinPoint point) throws Throwable {
              System.out.println("~~~~~~~method5---before");
              //此代码的执行,就表示切入点方法的执行
              Object v = point.proceed();
              System.out.println("~~~~~~~method5---after");
              return v;
          }
      ​
      }

      5.5.2 配置切面类

      
      
          
          
      ​
          
              
              
              
              
              
              
              
              
              
              
              
          
      ​
      

      六、Spring AOP 注解配置

      6.1 Spring AOP 注解配置框架部署

      6.1.1 创建Maven工程

      6.1.2 添加Spring依赖

      • context

      • aspects

      6.1.3 Spring配置文件

      
      
      ​
          
          
      ​
          
      
          
          
      ​
      

      6.2 AOP注解配置案例

      @Component
      @Aspect
      public class TransactionManager {
      ​
          @Pointcut("execution(* com.qfedu.dao.*.*(..))")
          public void pc1(){}
      ​
      ​
          @Before("pc1()") 不注明上面的切入点,则在这里就写成  @Before("execution(* com.qfedu.dao.*.*(..))"),后面配置一样写成这样
          public void begin(){
              System.out.println("~~~~开启事务");
          }
      ​
          @After("pc1()")
          public void commit(){
              System.out.println("~~~~提交事务");
          }
      ​
          @Around("pc1()")
          public Object printExecuteTime(ProceedingJoinPoint point) throws Throwable {
              long time1 = System.currentTimeMillis();
              Object v = point.proceed(); //代表切入点方法执行 , 此句话以上相当于before , 以下相当于after
              long time2 = System.currentTimeMillis();
              System.out.println("----time:"+(time2-time1));
              return v;
          }
      ​
      }

      注意:注解使用虽然方便,但是只能在源码上添加注解,因此我们的自定义类提倡使用注解配置;但如果如果使用到第三方提供的类则需要通过xml配置形式完成配置。

      七、Spring整合MyBatis

      Spring两大核心思想:IoC 和 AOP

      IoC : 控制反转,Spring容器可以完成对象的创建、属性注入、对象管理等工作

      AOP : 面向切面,在不修改原有业务逻辑的情况下,实现原有业务的增强

      7.1 Spring可以对MyBatis提供哪些支持?

      • IoC支持 SpringIoC 可以为MyBatis完成DataSource、SqlSessionFactory、SqlSession以及DAO对象的创建

      • AOP支持使用Spring提供的事务管理切面类完成对MyBatis数据库操作中的事务管理

      7.2 Spring整合MyBatis准备工作

      7.2.1 创建Maven工程

      7.2.2 部署MyBatis框架

      • 添加依赖

        • Mysql驱动

        • mybatis

      
          mysql
          mysql-connector-java
          5.1.47
      
          org.mybatis
          mybatis
          3.4.6
      
      • 创建MyBatis配置文件(创建配置文件之后无需进行任何配置)

      
      
      

      7.2.3 部署Spring框架

      • 添加依赖

      
          org.springframework
          spring-context
          5.2.13.RELEASE
      
          org.springframework
          spring-aspects
          5.2.13.RELEASE
      
      
      
          org.springframework
          spring-jdbc
          5.2.13.RELEASE
      
      • 创建Spring配置文件:applicationContext.xml

      
      
          
      ​
      

      7.2.4 添加Spring整合MyBatis的依赖

      • mybatis-spring 就是mybatis提供的兼容Spring的补丁

      
          org.mybatis
          mybatis-spring
          1.3.2
      

      7.2.5 spring中的classpath代表哪个目录?

      在spring项目的XML中加载db.properties文件配置如下:

      开始不知道classpath是哪个路径,后来发现整个项目运行的时候,不仅仅是java类会被编译成class存放在classes目录下,而且properties文件、xml文件也会放到这个目录下:

      所以classpath:spring/springmvc.xml是指spring目录里面的springmvc.xml文件

      故可以把classpath理解成classes目录。

      7.3 Spring整合MyBatis整合IoC配置

      7.3.1 整合Druid连接池

      • 添加druid的依赖

      
          com.alibaba
          druid
          1.1.10
      
      • 创建druid.properties属性文件

      druid.driver=com.mysql.jdbc.Driver
      druid.url=jdbc:mysql://localhost:3306/db_2010_mybatis?characterEncoding=utf-8
      druid.username=root
      druid.password=admin123
      ​
      ## 连接池参数
      druid.pool.init=1
      druid.pool.minIdle=3
      druid.pool.maxActive=20
      druid.pool.timeout=30000
      • 在applicationContext.xml中配置DruidDataSource

      
          
          
          
          
      ​
          
          
          
          
      

      7.3.2 整合MyBatis—创建SqlSessionFactory

      依赖Spring容器创建MyBatis的SqlSessionFactory对象

      
          
          
          
          
          设置这个 那么在Mapper文件里面就可以直接写对应的类名首字母小写 而不用写全路径名了
          
          
          
      

      7.3.3 整合MyBatis-创建Mapper

      
      
          
          
      
      //  测试
      public static void main(String[] args) {
      ​
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              SqlSessionFactory factory = (SqlSessionFactory) context.getBean("sqlSessionFactory");
              SqlSession sqlSession = factory.openSession();
              System.out.println(sqlSession);
              UserDao userDao = (UserDao)context.getBean("userDao");
              List list = userDao.selectAll();
              System.out.println(list);
       }

      7.4 Spring整合MyBatis整合AOP配置

      使用Spring提供的事务管理切面类 完成DAO中增删改操作的事务管理

      7.4.1 事务的隔离级别

      isolation 设置事务隔离级别:READ_UNCOMMITTED ,READ_COMMITTED , REPEATABLE_READ , SERIALIZABLE

      7.4.2 事务的传播机制

      propagation 设置事务的传播机制

      • REQUIRED 如果上层方法没有事务,则创建一个新的事务;如果已经存在事务,则加入到事务中。

      • SUPPORTS 如果上层方法没有事务,则以非事务方式执行;如果已经存在事务,则加入到事务中。

      • REQUIRES_NEW 如果上层方法没有事务,则创建一个新的事务;如果已经存在事务,则将当前事务挂起(表明把上层事务挂起,自己创建新事务)。

      • NOT_SUPPORTED 如果上层方法没有事务,则以非事务方式执行;如果已经存在事务,则将当前事务挂起。表明把上层事务挂起,自己不支持事务,不要把自己放在事务当中

      • NEVER 如果上层方法没有事务,则以非事务方式执行;如果已经存在事务,则抛出异常。

      • MANDATORY 如果上层方法已经存在事务,则加入到事务中执行;如果不存在事务则抛出异常。

      • NESTED 如果上层方法没有事务,则创建一个新的事务;如果已经存在事务,则嵌套到当前事务中。(表明自己要有事务,如果上层有事务,则自己的事务执行完之后再执行上层事务,这是嵌套)

      7.4.3 Spring AOP事务管理配置—XML配置

      事务配置通常配置到service包下的类,不应该配置到dao包下

      
          
      
          
                                                                      //事务传播机制
              
              
              
              
          
      
      ​
      
      
      <原则上也是在service层下配置事务>
      
          
          
      
      ​
      <疑问?:事务传播机制触发之后,当前事务和上层事务隔离级别怎么区分,如果上层事务的隔离级别小于当前事务隔离级别应该怎么办?
      //想法!:上层事务一定考虑和知道内部操作数据库的方法,他的隔离级别一定满足内部的dao操作隔离级别的最大范围,所以这个想法有点多余~~~
      >
      <疑问?:那么为什么这dao方法还需要指定事务传播机制,上层就考虑到了内部dao操作,所以调用dao操作一定是在事务里边啊!
      //想法!:因为你有时候直接调用dao操作,而这些dao操作没有上层事务的保护,所以要默认一个事务传播机制,所以事务传播机制主要是为了保护你单独调用他们而没有事务保护点的行为~~~>
          
      

      7.4.4 Spring AOP事务管理配置—注解配置

      • 在applicationContext.xml中配置事务管理,声明使用注解方式进行事务配置

      
      
          
      
      • 在需要Spring进行事务管理的方法上添加@Transactional注解

      @Transactional(isolation = Isolation.REPEATABLE_READ ,propagation = Propagation.SUPPORTS )
      public List listUsers() {
          return userDAO.queryUsers();
      }

      八、基于Spring的单元测试

      如果想要使用Spring容器实现属性注入、实现AOP面向切面编程,对象必须通过Spring容器获取;为了便于Spring环境下的测试,Spring提供了test组件,专门针对Spring环境进行单元测试。

      8.1 添加依赖

      
           junit
           junit
           4.12
           test
      
      
          org.springframework
          spring-test
          5.2.13.RELEASE
      

      8.2 编写单元测试类

      8.2.1 创建一个单元测试类

      8.2.2 添加注解

      //1.通过@RunWith 声明当前测试类位于Spring容器环境(被Spring容器管理)
      @RunWith(SpringJUnit4ClassRunner.class)
      //2.通过@ContextConfiguration 声明当前测试环境的Spring容器运行时加载的配置文件
      @ContextConfiguration("classpath:applicationContext.xml")
      public class UserServiceImplTest {
      ​
          //因为当前测试类是基于Spring容器运行的,当前测试类的对象是通过Spring容器创建的
          //因此可以通过Spring容器实现属性的注入
          @Resource
          private UserService userServiceImpl2;
          @Resource
          private UserService userServiceImpl;
      ​
          @Test
          public void test(){
              List users = userServiceImpl.listUsers();
              System.out.println(users);
          }
      ​
      }

      转载请注明:文章转载自 www.mshxw.com
      我们一直用心在做
      关于我们 文章归档 网站地图 联系我们

      版权所有 (c)2021-2022 MSHXW.COM

      ICP备案号:晋ICP备2021003244-6号