Spring入门

Spring

  • Spring Maven 依赖

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <!--JDBC-Spring整合依赖-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  • 特征:

    • Spring是一个轻量级、非入侵式(不会改变项目原有的代码)的开源免费框架(容器)
    • 控制反转(IOC),面向切面编程(AOP)
    • 支持事务的处理,对框架整合的支持
  • 拓展:

    1. Spring Boot
      • 一个快速开发的脚手架
      • 基于SpringBoot可以快速开发单个微服务
      • 约定大于配置
      • 需要完全掌握Spring及SpringMVC
    2. Spring Cloud
      • SpringCloud基于SpringBoot实现

1.IOC:控制反转

即:对象由Spring来创建、管理、装配

例:

Dao接口具有多个实现类,原始实现若要在业务层进行指定实现类的调用,需在业务层实现类创建实现类对象,通过该对象调用对应的实现方法,函数调用业务层实现类方法实现Dao接口,不同的Dao接口实现类需要对业务层实现类Dao接口实现类对象进行修改

此时程序主动创建对象,控制权掌握在程序员手中,需要调用指定接口的实现类时,更改业务层实现类代码主动创建指定接口实现类对象

//业务层实现类指定Dao实现类
private UserDao userDao = new UserDaoImpl();//指定UserDao的实现类
public void getUser(){
    userDao.getUser;
}

而利用set进行动态实现值的注入,在业务层实现类中设置一个set方法,使得函数调用业务层实现类时可以直接传递指定UserDao实现类对象,程序不再具有主动性,而是变成了被动的接受接口实现类对象,

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

image-20200929150654471

这种思想被称为控制反转:IOC
程序员不再管理对象的创建,系统的耦合性大大降低,可以更加专注于业务的实现上,而不用更改系统的架构

IOC本质

IOC(Inversion of Control):控制反转是一种设计思想,DL(依赖注入)是实现IOC的一种方法;在没有IOC的程序中,使用面向对象编程时,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自身控制,控制反转后将对象的创建转移给第三方,从而降低了程序中的耦合

Spring框架中控制反转是一种通过描述,以xml配置文件、注解甚至零配置实现,通过第三方去生产或获取特定对象的方式;Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection)

2.XML配置

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

    <bean id="" class="">
        <property name="" value=""/>
    </bean>

</beans>

Spring使用xml配置文件,在xml文件中Spring创建对象,并将这些对象都称为bean

如下,创建一个bean,设置bean的id为Hello,创建对象bean的实体类的全限定包名为com.flagling.pojo.Hello,在bean中由property标签指定bean对象中属性的值;其中name指定属性,value为这个bean对象的该属性赋值。

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

当一个对象需要另一个对象作参数时,该对象应当具有参数对象的set方法,Spring容器才能通过依赖注入的方法传递参数对象,同时参数对象应当在该对象之前创建于Spring容器,才能取得参数对象的id进行依赖注入

调用对象:
    private UserDao userDao;
    public void setUserDao(UserDao userDao){
    this.userDao = userDao
    }
    public void getUser(){
        userDao.getUser();
    }
创建对象:
<bean id="mySql" class="com.flagling.dao.MySqlImpl">
</bean>
<bean id="ServiceImpl" class="com.flagling.service.UserServiceImpl">
    <property name="userDao" ref="mySql"/>
</bean>     

ref:引用Spring容器中创建好的对象,引用数据类型

value:指定基本数据类型

  • 实例化对象

在Spring的xml配置文件中由实体类可以创建存储多个bean对象,由xml配置文件可以在程序中实例化并取得这些bean对象
在取得指定bean对象之前需要先实例化存储这些bean对象的一个容器,即获取Spring的上下文对象ApplicationContextApplicationContext作为IOC容器对象,因此又被称为Spring容器

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

通过向ApplicationContetxt构造函数提供一个或多个xml资源配置文件即可实例化Spring容器

Hello hello = (Hello) context.getBean("Hello")

在取得上下文对象容器后,即可由指定bean的id调用Spring对象的getBean方法,从实例化的容器中取得指定的实例化对象

Spring在实例化容器的同时容器中的所有bean对象同样被实例化,

由Spring创建并管理实例化对象,而非传统程序本身控制创建,变成被动的接收对象,这个过程被称为控制反转

  • 控制:即谁控制对象的创建,在Spring中由Spring创建对象
  • 反转:程序由主动创建对象转变为被动的接收对象

而在bean对象的创建中,属性property通过依赖注入的方法进行赋值,利用的是实体类中属性的set方法进行实现

3.Spring:IOC创建对象方式

  • 默认使用无参构造创建对象

  • 通过使用有参构造创建对象

    1. 下标赋值:index

      在实体类中创建有参构造方法,构造方法中的参数在Spring中默认以由0开始的下标序号标记,由index标记序号参数,为下标赋值,value用于基本数据类型,ref用于引用数据类型

      <bean id="user" class="com.flagling.pojo.User">
          <constructor-arg index="0" value="test"/>
      </bean>
      
    2. 类型赋值:type

      在实体类中创建有参构造方法,以参数的类型为标记,可以凭借类型为参数赋值,但仅限于参数中没有相同类型的参数,否则不能赋值成功,故不推荐使用

      <bean id="user" class="com.flagling.pojo.User">
          <constructor-arg type="java.lang.String" value="test"/>
          <constructor-arg type="int" value="1"/>
      </bean>
      
    3. 名称赋值:name

      在实体类中创建有参构造方法,以参数的定义名称为标记,凭借名称为参数赋值,由此来消除歧义

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

4.Spring-xml配置详解

  • 别名:alias

    对创建的bean对象起别名,在取得Spring的上下文对象后,getBean方法既可使用id取得对象,也可使用对象的别名取得

    <bean id="user" class="com.flagling.pojo.User">
        <constructor-arg name="name" value="FlagLing"
    </bean>
    <alias name="user" alias="userNew"/>
    
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    context.getBean("user");//以id取得对象
    context.getBean("userNew");//以别名取得对象,在内存中取得的是同一个对象
    
  • Bean的配置

    在XML配置的Spring容器中创建Bean对象,
    id为bean对象的唯一标识符,
    class为bean对象所对应的实体类的全限定类名,
    name同样为bean对象的别名设置,且较alias更高级,可同时设置多个别名,任意分割均可识别(空格、逗号、分号…)

    <bean id="user" class="com.flagling.pojo.User" name="user1,u1 u2;u3">
        <constructor-arg name="name" value="FlagLing"
    </bean>
    
  • import

    一般用于团队开发,不同的人编写不同的类注册在不同的ApplicationContext配置文件中,通过import可将多个配置文件导入合并到当前配置中,即由多个对象容器合并为一个总的对象容器,相同的bean对象会被合并

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

5.DL:依赖注入

  • 构造器注入

  • Set方式注入

    • 依赖:bean对象的创建依赖于容器

    • 注入:bean对象中的所有属性由容器来注入

    • 常规使用

      1. 基本数据类型:value

        <property name="name" value="FlagLing"/>
        
      2. 引用数据类型:ref

        <property name="address" ref="address"/>
        
      3. 数组:array

        <property name="books">
            <array>
                <value>西游记</value>
                <value>红楼梦</value>
                <value>水浒传</value>
            </array>
        </property>
        
      4. List集合:list

        <property name="hobbies">
            <list>
                <value>听歌</value>
                <value>看电影</value>
            </list>
        </property>
        
      5. Map集合:map

        <property name="card">
            <map>
                <entry key="身份证" value="123456789"/>
                <entry key="学生证" value="2017123456"/>
            </map>
        </property>
        
      6. Set集合:set

        <property name="games">
            <set>
                <value>魂斗罗</value>
                <value>冒险岛</value>
            </set>
        </property>
        
      7. Null值注入

        <property name="girlFirend">
            <null/>
        </property>
        
      8. info注入:prop

        <property name="info">
            <props>
                <prop key="学号">2017123456</prop>
                <prop key="宿舍">17栋6027</prop>
            </props>
        </property>
        

        info即相关信息,可在其中注入相关的一系列信息

  • 拓展方式注入

    • P标签

      在XML中引入P命名空间约束:

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

      P,即Property属性,P命名空间注入,针对Set方式注入优化,可以在定义bean的同时,直接注入属性的值

      <bean id="user" class="com.flagling.pojo.User" p:name="FlagLing" p:age="18"/>
      
    • C标签

      在XML中引入C命名空间约束:

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

      C,即Constructor构造器,C命名空间注入,针对构造器注入优化,

      <bean id="user" class="com.flagling.pojo.User" c:name="Flagling" c:age="18"/>
      

6.Bean的作用域

Bean Scope:在创建bean对象时可设置,默认隐式调用单例模式:Singleton

<bean id="user" class="com.flagling.pojo.User" scope="singleton/prototype/...."/>

singleton:单例模式

Spring容器中的对象可以被取出多次,但实际取得的是同一个对象实例,在内存中的地址不变

prototype:原型模式

以Spring容器中的对象为原型可取出多次,每次取得容器就会新创建一个对象实例,获得的对象在内存中地址不相同

request、session、application、websocket:在Spring Web开发中使用

7.Bean的自动装配:Autowire

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

  • 装配方式

    1. XML显式配置:在XML文件中设置bean,让Spring进行自动装配

    2. Java显式设置

    3. 隐式自动装配

      • xml手动配置指定:

        <bean id="cat" class="com.flagling.pojo.Cat"/>
        <bean id="dog" class="com.flagling.pojo.Dog"/>
        <bean id="people" class="com.flagling.pojo.People">
            <property name="name" value="FlagLing"/>
            <property name="cat" ref="cat"/>
            <property name="dog" ref="dog"/>
        </bean>
        
      • xml自动装配:

        <bean id="cat" class="com.flagling.pojo.Cat"/>
        <bean id="dog" class="com.flagling.pojo.Dog"/>
        <bean id="people" class="com.flagling.pojo.People" autowire="byName">
            <property name="name" value="FlagLing"/>
        </bean>
        

        byName:通过自动装配,自动在容器上下文中查找与配置对象实体类中set方法对应属性名称的bean的id,可忽略大小写,id与属性名不对应则装配失败,空指针异常

        <bean class="com.flagling.pojo.Cat"/>
        <bean class="com.flagling.pojo.Dog"/>
        <bean id="people" class="com.flagling.pojo.People" autowire="byType">
            <property name="name" value="FlagLing"/>
        </bean>
        

        byType:通过自动装配,自动在容器上下文中查找与配置对象实体类中set方法对应属性相同类型的bean,使用byType时被装配的bean的id可省略。也可自动装配成功。但需要该类型bean全局唯一,当具有多个相同类型的bean时,xml配置报红,无法成功装配。

      • 自动装配的注解实现

        1. 导入context约束:xmlns:context="http://www.springframework.org/schema/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>
          

          使用演示:

          xml配置文件:引入context命名空间,配置注解支持,仅进行简单配置bean

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns:context="http://www.springframework.org/schema/context"
                 xsi:schemaLocation="http://www.springframework.org/schema/beans
                  https://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/context
                  https://www.springframework.org/schema/context/spring-context.xsd">
                      <bean id="cat" class="com.flagling.pojo.Cat"/>
                      <bean id="dog" class="com.flagling.pojo.Dog"/>
                      <bean id="people" class="com.flagling.pojo.People"/>
          <context:annotation-config/>
          
          </beans>
          

          java代码:在参数对象上配置自动装配注解

          public class People {
              @Autowired
              private Cat cat;//配置注解
              @Autowired
              private Dog dog;
              private String name;
          
              public Cat getCat() {
                  return cat;
              }
              public Dog getDog() {
                  return dog;
              }
              public String getName() {
                  return name;
              }
          }
          

          测试调用:由xml获取Spring的上下文对象context,再由context取得people实例,实例化过程中cat对象与dog对象自动装配成功

          public class Test {
              public static void main(String[] args) {
                  ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
                  People people = context.getBean("people",com.flagling.pojo.People.class);
                  people.getCat().shout();
                  people.getDog().shout();
              }
          }
          

          测试结果:image-20201001162902913

          总结

          @Autowired注解可配置在属性上,或属性的set方法上;均可实现属性对象的成功装配,当设置在属性上时,属性的set方法可以省略,注解实现的方式既不是ByType,也不是ByName,而是默认优先ByType进行自动装配,当发现装配类型于spring容器中存在两个及以上实例时,会采用ByName的方式继续寻找对应的实例进行装配。

          拓展

          1. @Autowired注解显式的设置其required属性为false:@Autowired(required=false)

            标志着注解修饰的当前对象值可以为Null,而非抛出异常

          2. 当xml中注解的bean未满足要求自动装配失败时(即含有多个同类型的bean无法使用byType模式,含有的多个bean的id与属性名不对应无法使用byName方式),@Autowired注解可与@Qualifier注解配合使用,通过设置@Qualifier注解的value属性,采用byName的方式达到指定bean对象装配的目的

            @Autowired
            @Qualifier(value="dogtest")
            private Dog dog;
            //自动装配id为dogtest的bean对象
            
          3. @Resource注解:

            @Resource注解与@Autowired注解类似,同样用于实现自动装配,@Resource注解默认通过byName的方式实现,byName装配失败后则通过byType方式实现

            @Resource注解直接使用也装配失败,也可设置它的name属性达到@Autowired@Qualifier配合使用的效果,自动装配指定id的bean对象,故也可看做@Autowired注解与@Qualifier注解的整合形式,

            @Resource(name="dogtest")
            private Dog dog;
            //自动装配指定id的bean
            
          4. @Nullable注解,标记当前字段可以为Null

8.Spring注解开发

  • Spring4之后,使用注解开发必须保证maven项目引入AOP依赖,Spring-webmvc中已包含该包
  • xml配置中必须导入context命名空间,增加注解支持【见上文】
  • 注解详解
    • xml:<context:component-scan base-package="com.flagling.pojo"/>
      指定要扫描的包,包下含有@Controller、@Service、@Repository,@Component 注解的类会被指定为实体类在Spring容器中创建bean对象

    • bean对象注册
      @Component:组件注解,标记普通pojo类,使被标记的实体类实例化到Spring容器中,相当于xml配置文件中的bean对象注册,bean对象的id默认为首字母小写类名

      //等价于<bean id="user" class="com.flagling.pojo.User"
      @Component
      public class User {
          public String name="FlagLing";
      }
      
    • 对象属性注入
      @Value:属性注解,标记基本数据类型实体类属性或属性的set方法,注入指定的属性值 ,等价于bean对象注册中为基本数据类型属性赋值

      public class User {
          //等价于bean对象注册中<property name="name" value="FlagLing"/>
          @Value("FlagLing")
          public String name;
      }
      
    • @Component衍生注解:
      @Component注解在web开发中按照mvc三层架构分层,衍生出三个不同注解。但根本作用相同;即标记当前类,将当前类注册到Spring容器中,由Spring托管装配

      1. Dao层@Repository
      2. Service层@Service
      3. Controller层@Controller
    • 作用域注解
      @Scope:作用域注解,可设置类为单例模式singleton或原型模式prototype

          @Component
      @Scope("singleton")
          public class User {
              @Value("FlagLing")
           public String name;
          }
      
    • 小结

      • XML与注解
        • XML配置文件更加万能,适用于任何场合,维护简单方便
        • 注解不是自己类无法使用,即无法引用注解加载的bean;维护相对复杂
      • 最佳整合:
        XML配置文件仅负责管理bean的注册
        注解仅负责完成bean对象属性的注入

9.JavaConfig

即完全不使用XML配置文件,而是全部采用Java来配置Spring:JavaConfig
JavaConfig是Spring的一个子项目,在Spring4之后,JavaConfig变为Spring的一个核心功能,其较于xml配置文件更加灵活,在SpringBoot框架中被广泛使用

  • 简单使用方法:
    与创建XML配置文件类似,JavaConfig方式为创建一个Java类文件,用作配置文件
    //@Configuration注解标记当前类为一个Spring配置类
    @Configuration
    public class SpringConfig {
        //@Bean注解标记一个方法注册为bean对象,其中方法名为Spring容器中Bean对象的id,返回值类型为bean对象的实体类类型
        @Bean
        public User getUser(){
            return new User();
        }
    
    }
    
    与XML配置文件类似,JavaConfig需要先获取上下文对象context后再由getBean取得Spring容器中的bean对象
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    User user = (User) context.getBean("getUser");
    
    在Spring项目中可以同时存在多个JavaConfig配置文件,通过使用注解@import(JavaConfig.class),可以将其他的JavaConfig配置文件导入当前配置文件,最终取得一个整合的Spring上下文对象

10.代理模式

代理模式是SpringAOP的底层

image-20201003134437367

分类:

  • 静态代理
  • 动态代理

1.静态代理

模式分析:

抽象业务:真实角色提供的业务,一般由接口或抽象类解决

真实角色:被代理的角色

代理角色:代理真实角色,除取得真实角色对象后提供抽象业务操作,一般还提供一些附属操作

客户:访问代理角色对象,由代理角色对象完成真实角色对象的业务操作

代码分析:

抽象业务:一个定义基础业务的接口或抽象类

真实角色:一个实现基础抽象业务的实现类,具体实现了基础业务方法

代理角色:一个实现基础抽象业务的代理类,代理类中实例化一个私有实现类对象,一个实现类对象的有参构造,且抽象业务的方法实现为分别调用实现类对象的实现方法;在实现基础抽象业务的基础上对程序功能进行拓展,或实现公共业务

客户:实例化一个代理类对象,再由代理类对象完成抽象业务及代理类的拓展业务,抽象业务根本上仍是实现类完成的,调用上由代理类代理,但对客户隐藏了实现类对象

好处:

  • 使真实角色的操作更加纯粹,只负责提供抽象业务的实现,而不用关注一些公共业务的实现
  • 公共业务交给代理角色完成,实现了业务的分工
  • 公共业务发生拓展时,方便集中管理

缺点:

  • 每一个真实角色都会产生一个代理角色,代码量翻倍,使得开发效率降低

2.动态代理

动态代理与静态代理角色类似,但解决了静态代理的缺点;动态代理的代理类是动态生成的,而非手动编写

分类:

  • 基于接口的动态代理:JDK动态代理
  • 基于类的动态代理:cglib
  • Java字节码实现:JavaSist

类库

  • Proxy:代理类。提供了创建动态代理类和实例的静态方法,是所有由这些方法创建的静态代理类的超类

  • InvocationHandler:调用处理程序,是代理实例的调用处理程序实现的接口

    每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分配到其调用处理程序的invoke方法中

    • Invoke方法:处理代理实例上的方法调用并返回结果

简单实现:

一个抽象接口,一个抽象接口的单纯实现类

一个代理生成类:

//代理生成类:自动生成代理类,实现InvocationHandler接口
public class ProxyInvocationHandler implements InvocationHandler {
    //定义一个抽象接口及接口的set方法
    private Rent rent
    public void setrent(Rent rent){
        this.rent=rent;
    }
    //生成得到实现被代理接口的代理类
    public Object getProxy(){
        /*传递参数(this.getClass().getClassLoader:当前类的类加载器,
                   rent.getClass.getInterfaces():从实现接口的被代理对象获取实现的抽象接口
                   this:InvocationHandler,调用处理程序,即当前类
                   )调用Proxy类的静态方法,得到一个实现代理接口的代理类
        */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this)
    }
    //实现InvocationHandler接口中的抽象方法,
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        //传递houseOwner对象,之后代理对象调用被代理对象的方法时,均放在invoke方法中执行
        Object result = method.invoke(rent,args);
        result result;
    }
}

一个客户:

public class Client {
    public static void main(String[] args) {
        Rent rent = new HouseOwner();
        //取得代理生成类对象
        ProxyInvocation deal = new ProxyInvocation();
        //由代理生成类对象编写的getProxy方法自动生成并取得一个代理类对象
        deal.setRent(rent);
        Rent proxy= (Rent)deal.getProxy();
        proxy.rent();
    }
}

好处:

可使动态代理类代理一个接口,而非指定的实现类对象,此时代理对应的一类业务;此时一个动态代理类可以代理实现同一接口的多个类

11.AOP

  • AOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP(面向对象程序设计)的延续,利用AOP可以对业务逻辑的各个部分进行隔离,使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性。

使用Spring实现AOP

使用AOP织入,需先导入依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

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

image-20201004143323372

通过这5种横切方式,AOP在不改变原有代码的情况下,去增加新的程序功能

方式一:使用Spring的API接口
  1. 选择添加程序功能的横切方式,编写一个实现对应API接口的实现类,类需要重写一个接口方法,而这个接口方法中的功能即是我们预想横切加入的程序功能

    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()+"被执行");
        }
    
  2. 编写XML文件,注册bean对象,引入AOP的命名空间,添加AOP约束,开始编写AOP

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           
           xmlns:aop="http://www.springframework.org/schema/aop"
           
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
                               
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <bean id="userService" class="com.flagling.services.UserServiceImpl"/>
        <bean id="Log" class="com.flagling.Log.Log"/>
    
        <aop:config>
            <!--配置切入点:execution(表达式),表达式通配符设置切入点,如下设置一个UserServiceImpl类中所有方法的切入点-->
            <aop:pointcut id="log" expression="execution(* com.flagling.services.UserServiceImpl.*(..))"/>
            <!--执行环绕增加,即将添加方法添加到切入点-->
            <aop:advisor advice-ref="Log" pointcut-ref="log"/>
        </aop:config>
    
    </beans>
    
  3. 测试,获取上下文对象,以接口多态取得接口的实现类,测试运行

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService user = context.getBean("userService",UserService.class);
            user.add();
        }
    }
    
  4. 运行结果:前置通知运行成功

    image-20201004153201865

方式二:自定义类

创建一个添加程序功能的自定义类,类中方法为预期加入切入点的方法,其AOP设置如下

<!--注册自定义类bean对象-->
<bean id="diy" class="com.flagling.diy.DiyPointCut"/>
<!--AOP设置-->
<aop:config>
    <!--自定义切面,切入点切入一个切面,切面为被添加功能的类,即自定义类-->
    <aop:aspect ref="diy">
        <!--自定义切入点-->
        <aop:point id="point" experssion="execution(* com.flagling.service.UserServiceImpl.*(..))"/>
        <!--通知-->
        <!--在自定义的切入点选择各方法的通知方式,method后为引用自定义类中的方法-->
        <!--自定义类中before方法选择aop:before通知方式,切入点引用point,即在point前运行-->
        <aop:before method="before" pointcut-ref="point"/>
        <!--自定义类中after方法选择aop:after通知方式,切入点引用point,即在point后运行-->
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>
方式三:使用注解实现

编写一个自定义类,XML文件仅注册bean对象,以及开启AOP注解支持,

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


    <bean id="userService" class="com.flagling.services.UserServiceImpl"/>
    <bean id="annotationAop" class="com.flagling.Annotation.AnnotationAop"/>
    <!--开启AOP注解支持,隐藏参数:proxy-target-class="false",意义动态代理默认使用JDK,更改为true,动态代理使用cglib-->
    <aop:aspectj-autoproxy/>
</beans>

自定义类中,类注解@Aspect,标记当前类是一个切面,方法标记@After@Before@Around等环绕方式,同时注解传递参数excution表达式,想环绕方式注解传递切入点

@Aspect
public class AnnotationAOP{
    @Before("execution(* com.flagling.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前")
    }
}

在环绕增强@Around标记的方法中可获取参数ProceedingJoinPoint程序执行点,在方法中设置执行切入点方法的操作

@Around("execution(* com.flagling.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint point) throw Throwable{
    System.out.println("环绕前");
    Object proceed = point.procees();//执行切入点方法
    System.out.println("环绕后")
}

12.整合MyBatis

步骤

  • 相关Jar包:
    • junit
    • mybatis
    • mysql数据库
    • Spring
    • AOP织入
    • mybatis-spring
  • 编写配置文件
  • 测试

1.依赖配置

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.5</version>
    </dependency>
</dependencies>
<!--XML文件过滤-->
<build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

MyBatis原始操作:

  • 编写MyBatis-config.xml配置文件,连接数据库

  • 编写实体类

  • 编写Mapper接口,定义数据库查询方法

    public interface UserMapper {
        List<User> selectUser();
    }
    
  • 编写Mapper配置文件,定义查询方法的数据库命令

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.flagling.dao.UserMapper">
        <select id="selectUser" resultType="user">
            select * from user;
        </select>
    </mapper>
    
  • 测试方法

    从mybatis-config.xml配置文件获取SqlSessionFactory对象,获取sqlSession实例,由sqlsession实例执行数据库操作

    public class Test {
        @org.junit.Test
        public static void main(String[] args) {
             SqlSessionFactory sqlSessionFactory = null;
            try {
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
            SqlSession session = sqlSessionFactory.openSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
            List<User> userList = mapper.selectUser();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    }
    

MyBatis-Spring:

  1. 方式:SqlSessionFactory取得SqlSessionTemplate

编写Spring配置文件:

  • 添加一个Spring-Dao.xml配置文件,使用Spring提供的数据源替换MyBatis的配置,即配置一个DataSource的Bean

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
        <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=UTF8&amp;serverTimezone=Asia/Shanghai"/>
            <property name="username" value="root"/>
            <property name="password" value="637625"/>
        </bean>
    
    </beans>
    
  • 配置一个SqlSessionFactory的工厂Bean,引用上述数据源,然后可在工厂Bean中导入设置configLocation配置属性,导入MyBatis的配置文件,同时也可直接在工厂Bean中完成mybatis的配置设置,工厂Bean中含有mybatis配置文件所需的所有标签,如设置Mapper文件注册

     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <property name="mapperLocations" value="classpath:com/flagling/dao/*.xml"/>
        </bean>
    
  • SqlSessionTemplate注册:与MyBatis中的sqlsession是相同的作用,且无set方法故,属性构造器注入SqlSessionFactory的工厂Bean

    <bean id="SqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory"/>
    </bean>
    

编写Mapper的实现类

  • Spring与MyBatis整合后,加入面向对象思想,编写Mapper接口的实现类,注入从SqlSessionFactory工厂类中取得的sqlsession,取得Mapper接口,在接口的方法实现中返回mapper接口调用对应方法的结果

    public class UserMapperImpl implements UserMapper {
        //设置SqlSessionTemplate属性及set方法,便于向实现类注入取得的sqlsession
        private SqlSessionTemplate sqlSession;    
    
        public List<User> selectUser() {
           UserMapper mapper = sqlSession.getMapper(UserMapper.class);
           return mapper.selectUser();
        }
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
            this.sqlSession = sqlSession;
        }
    }
    

编写测试类:

  • 取得Spring上下文对象,取得UserMapperImpl实现类对象:

    public class Test {
        @Test
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("Spring-dao.xml");
            UserMapper userMapperImpl = context.getBean("UserMapperImpl",UserMapper.class);
            List<User> users = userMapperImpl.selectUser();
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    

    2.方式2:实现SqlSessionDaoSupport抽象支持类,调用getSqlSession方法获取SqlSessionTemplate

  • 接口实现类:

    public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
    /*
    将方式一中sqlsessionTemplate在XML配置文件中的注册放在实现类的父类SqlSessionDaoSupport中完成,在注册继承SqlSessionDaoSupport的接口实现类bean对象时,需要引入注册的SqlSessionFactory的bean,这样在实现类的方法中getSqlSession方法才能取得工厂类创建的SqlSession,
    */
        public List<User> selectUser() {
           /* SqlSession sqlSession = getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.selectUser();
            */
            return getSqlSession().getMapper(UserMapper.class).selectUser();
        }
    }
    
  • XML:xml中仍需配置DataSource数据源以及依靠数据源生成的SqlSessionFactory工厂

    <bean id="UserMapperImpl2" class="com.flagling.dao.UserMapperImpl2">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    

13.声明式事务

  • 事务:确保一组操作的完整性与一致性,要么都成功,要么都失败

  • ACID原则

    • 原子性:一个事务中多个业务被看作一个整体的操作,不能只执行其中的一部分
    • 一致性:一个事务中多个业务的提交状态最终应该保持一致,要么都成功,要么都失败
    • 持久性:一个事务一旦被提交,对数据库中数据的操作是持久而不因内存销毁而取消的
    • 隔离性:多个事务并发执行,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能相互干扰。
  • Spring事务管理:

    • MyBatis-Spring允许MyBatis参与到Spring的事务管理中,借助Spring的DataSourceTransactionManager来实现事务管理
    • 分类:
      1. 声明式事务:AOP思想横切,
      2. 编程式事务:在代码中进行事务的管理
  • 声明式事务配置:

    • 开启Spring的事务管理功能,需在Spring的配置文件中创建一个DataSourceTransactionManager对象,为事务管理器指定创建SqlSessionFactory对象相同的datasource数据源,否则事务管理器无法正常工作

      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
      </bean>
      
    • 结合AOP实现事务的织入:

      1. 导入事务tx的命名空间与约束

        命名空间:xmlns:tx="http://www.springframework.org/schema/tx"

        约束:

        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">
        
      2. 配置事务通知,为事务通知绑定创建的事务管理器,在事务通知中注册需要配置事务特的方法,如注册addUser方法,通配符"*"表示所有方法,当AOP切入事务后,在每一个切入点匹配注册方法,看是否被事务管理

        <tx:advice id="txAdvice" transaction-manager="transactionManager">
                <tx:attributes>
                    <tx:method name="addUser"/>
                    <tx:method name="*"/>
                </tx:attributes>
            </tx:advice>
        
      3. 方法的事务配置中还有其他选项的配置,如配置事务的传播特性propagation,默认为REQUIRED,表示支持当前事务,如果没有事务则新建一个事务,

        <tx:method name="addUser" propagation="REQUIRED"/>
        

        配置方法对数据库只可读,无法进行增删改操作

        <tx:method name="query" read-only="true"/>
        
    • 配置事务切入,execution表达式表示在dao包下所有类的所有方法均被切入已配置的事务通知txAdvice

       <aop:config>
              <aop:pointcut id="txPointCut" expression="execution(* com.flagling.dao.*.*(..))"/>
              <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> 
          </aop:config>
      

2020/10/6

ema/tx"`

   约束:

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

2. 配置事务通知,为事务通知绑定创建的事务管理器,在事务通知中注册需要配置事务特的方法,如注册addUser方法,通配符"*"表示所有方法,当AOP切入事务后,在每一个切入点匹配注册方法,看是否被事务管理

   ```xml
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
           <tx:attributes>
               <tx:method name="addUser"/>
               <tx:method name="*"/>
           </tx:attributes>
       </tx:advice>
   ```

3. 方法的事务配置中还有其他选项的配置,如配置事务的传播特性`propagation`,默认为REQUIRED,表示支持当前事务,如果没有事务则新建一个事务,

   ```xml
   <tx:method name="addUser" propagation="REQUIRED"/>
   ```

   配置方法对数据库只可读,无法进行增删改操作

   ```xml
   <tx:method name="query" read-only="true"/>
   ```
  • 配置事务切入,execution表达式表示在dao包下所有类的所有方法均被切入已配置的事务通知txAdvice

     <aop:config>
            <aop:pointcut id="txPointCut" expression="execution(* com.flagling.dao.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> 
        </aop:config>
    

2020/10/6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值