Loading

Spring初步学习记录

Spring学习

1 参考资料

参考了尚硅谷-Spring5框架最新版教程(idea版)

2 Spring概述

2.1 Spring框架概述

  1. Spring是一个轻量级的开源JavaEE框架

  2. Spring可以解决企业应用开发的复杂性

  3. Spring有两个核心部分:IoC和AOP

    • IoC:控制反转,将创建对象过程交给Spring进行管理
    • AOP:面向切面,不修改源代码的情况下进行功能增强
  4. Spring的特点:
    (1). 方便解耦,简化开发
    (2). AOP编程的支持
    (3). 方便程序的测试
    (4). 方便和其他框架进行整合
    (5). 方便进行事务
    (6). 降低API开发难度

2.2 Spring5模块

线程转换状态

2.3 Spring5入门案例

  1. 将Spring导入到项目

  2. 创建普通类

    public class Test1 {
        public void helloSout() {
            System.out.println("Hello, Spring5!");
        }
    }
    
  3. 创建Spring配置文件,在配置文件创建对象(基于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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--配置对象创建-->
        <bean id="user" class="Test1"></bean>
    </beans>
    
  4. 测试

    package com.learning.create.testdemo;
    
    import com.learning.create.SpringCreateTest1;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test2 {
        public static void main(String[] args) {
            // 1.导入配置文件
            ApplicationContext context1 = new ClassPathXmlApplicationContext("Test1.xml");
            // 通过路径导入
            ApplicationContext context2 = new FileSystemXmlApplicationContext("Test1.xml");
    
            // 2.获取配置创建的对象
            Test2 t = context.getBean("user", Test1);
    
            // 3.测试
            System.out.println(t);
            t.helloSout();
        }
    }
    

3 IoC(容器)

3.1 IoC底层原理

  1. 什么是IoC

    • 控制反转(Inversion of Control,缩写为IoC),把对象创建和对象之间的调用过程,交给Spring进行管理
    • 使用IoC的目的:为了降低耦合度
    • 2.3 入门案例就是IoC实现
  2. IoC底层原理

    • xml解析、工厂设计模式、反射
    • 实现过程
      IoC实现过程

3.2 IoC接口(BeanFactory)

  1. IoC思想基于IoC容器完成,IoC容器底层就是对象工厂

  2. Spring提供IoC容器实现两种方式:(两个接口)

    • BeanFactory:IoC容器基本实现,是Spring内部的使用接口,不提供开发人员使用

      • 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
    • ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

      • 加载配置文件时候就会把在配置文件对象进行创建
  3. Application接口有实现类

    • ClassPathXmlApplicationContext:默认会去 classPath 路径下找。classPath 路径指的就是编译后的 classes 目录。如果是绝对路径,就需要加上 “file:” 前缀,不可缺省。
    • FileSystemXmlApplicationContext:默认是去项目的路径下加载,可以是相对路径,也可以是绝对路径,若是绝对路径,“file:” 前缀可以缺省。

3.3 IoC操作Bean管理

  1. 什么是Bean管理

    • 指的是两种操作
      • Spring创建对象
      • Spring注入属性
  2. Bean管理操作有两种方式
    (1). 基于xml配置文件方式实现
    (2). 基于注解方式实现

3.3.1 基于xml方式

1 基于xml方式创建对象
<bean id="user" class="类全路径"></bean>
  • 在Spring配置文件中,使用bean标签,标签里边添加对应属性,就可以实现对象的创建

  • bean标签有很多属性,常用的属性

    • id:唯一标识
    • class:类全路径(包类路径)
  • 创建对象时,默认会使用无参构造器创建对象

2 基于xml方式注入属性
  • DI:依赖注入,就是注入属性

    • 第一种方式:使用set方法进行注入

      • 类中声明set方法

      • 在xml文件配置属性注入

        <!-- 使用set方法注入属性 -->
        <bean id="test" class="类全路径">
            <!-- 使用property完成属性注入 
                name : 类里面属性名称
                value : 向属性注入的值
            -->
            <property name="属性名" value="属性值"></property>
        </bean>
        
    • 第二种方式:使用有参构造器进行注入

      • 在类中声明有有参构造器

      • 在xml文件配置属性注入

        <!-- 使用有参构造器注入属性 -->
        <bean id="test" class="类全路径">
            <!-- 使用constructor-arg完成属性注入
                name : 类里面属性名称
                value : 向属性注入的值
                index : 索引值,代表第i个属性
                -->
            <constructor-arg name="属性名" value="属性值"></constructor-arg>
        </bean>
        
    • 第三种方法:p名称空间注入(了解)

      • 可以简化基于xml配置方式

      • 底层还是基于set方法

        <!-- 在beans标签中声明p空间 -->
        <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:p="http://www.springframework.org/schema/p"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
            <!-- 使用p名称空间注入属性 -->
            <bean id="test" class="类全路径" p:属性名="属性值"></bean>
        </beans>
        
    • 注入其他类型属性

      • 字面量:默认值
        • null
        • 属性值中包含特殊符号
      <bean id="test" class="类全路径">
          <!-- 注入null -->
          <property name="属性名">
              <null/>
          </property>
          <!-- 属性值中包含特殊符号,比如<Test>
              1. 可以使用转义
              2. 把带特殊符号内容写到CDATA,<![CDATA[内容]]>
          -->
          <!-- 使用转义 -->
          <property name="属性名" value="&lt;Test&gt;"></property>
      
          <!-- 把带特殊符号内容写到CDATA -->
          <property name="属性名">
              <value><![CDATA[<Test>]]></value>
          </property>
      </bean>
      
3 注入属性————外部Bean

举例:

  • 创建两个类service类和dao类
  • 在service中调dao类
// Service.java
package com.test.service;

public class Service {
    public void show() {
        System.out.println("show");
        //  旧方法
        //    1. 创建UserDaoImpl的对象
        UserDao userDao = new UserDaoImpl();
        userDao.update();
        
    }
}
//UserDao接口
package com.test.dao;

public interface UserDao {
    public void update();
}

//UserDao实现类
public class UserDaoImpl implements UserDao {
    @Override
    public void update() {
        System.out.println("update");
    }
}
  • 在Spring配置文件中进行配置

采用注入属性的方法:

// Service.java  新方法
package com.test.service;

public class Service {
    // 1. 创建UserDao属性
    private UserDao userDao;
    // 2. 创建set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void show() {
        System.out.println("show");

    }
}

xml配置文件:

<!-- 进行属性注入 -->
<!-- 1. service和dao对象创建 -->
<bean id="userService" class="com.test.service.Service">
    <!-- 注入对象
        ref : 创建UserDaoImpl对象bean标签id值
        -->
    <property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.test.dao,UserDaoImpl"></bean>
4 注入属性————内部Bean
  • 一对多关系:例如部门和员工
  • 在实体类之间表示出一对多的关系
    部门类:
//Dept.java 
package com.test.bean;

public class Dept {
    private String dname;

    public void setDname(String dname) {
        this.dname = dname;
    }
}

员工类:

//Emp.java
package com.test.bean;

public class Emp {
    private String ename;
    private String gender;
    private Dept dept;

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
}
  • 在Spring配置文件中进行配置
<!-- 内部Bean -->
<bean id="emp" class="com.test.bean.Emp">
    <!-- 设置普通属性 -->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
    <!-- 设置对象属性 -->
    <property name="dept">
        <bean id="dept" class="com.test.bean.Dept">
            <property name="dname" value="人事部"></property>
        </bean>
    </property>
</bean>
5 注入属性————级联赋值
  • 以内部bean中例子为例
<!-- 级联赋值 -->
<!-- 第一种写法 -->
<bean id="emp" class="com.test.bean.Emp">
    <!-- 设置普通属性 -->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
    <!-- 级联赋值 -->
    <property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.test.bean.Dept">
    <property name="dname" value="人事部"></property>
</bean>

<!-- 第二种写法 -->
<!-- 需要在Emp.java中声明getDept()方法-->
<bean id="emp" class="com.test.bean.Emp">
    <!-- 设置普通属性 -->
    <property name="ename" value="lucy"></property>
    <property name="gender" value="女"></property>
    <!-- 级联赋值 -->
    <property name="dept" ref="dept"></property>
    <property name="dept.dname" value="人事部"></property>
</bean>
<bean id="dept" class="com.test.bean.Dept">
    <property name="dname" value="人事部"></property>
</bean>
6 注入集合属性
  • 注入数组类型属性
  • 注入List集合类型属性
  • 注入Map集合类型属性
  • 注入Set集合类型属性
  • 集合类型注入对象属性
//Student.java 
package com.test.spring;

public class Student {
    // 1 数组类型属性
    private String[] courses;

    // 2 List集合类型属性
    private List<String> list;

    // 3 Map集合类型属性
    private Map<String, String> maps; 

    // 4 Set集合类型属性
    private Set<String> sets;

    // 5 集合类型注入对象属性
    private List<Course> courseList;

    public void setCourses(String[] courses){
        this.courses = courses;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }
    public void setSets(Set<String> sets) {
        this.sets = sets;
    }
    public void setSets(List<Course> courseList) {
        this.courseList = courseList;
    }
}
//Course.java
package com.test.spring;

public class Course {
    private String cname;
    
    public void setCname(String cname) {
        this.cname = cname;
    }
}
  • 配置文件配置
<!-- 集合类型属性注入 -->
<bean id="student" class="com.test.spring.Student">
    <!-- 数组类型属性注入 -->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库基础</value>
        </array>
    </property>

    <!-- List类型属性注入 -->
    <property name="list">
        <list>
            <value>张三</value>
        </list>
    </property>

    <!-- Map类型属性注入 -->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>

    <!-- Set类型属性注入 -->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>

    <!-- 集合类型注入对象属性 -->
    <property name="courseList">
        <list>
            <ref bean="course1"></ref>
            <ref bean="course2"></ref>
        </list>
    </property>
</bean>

<!-- 创建多个course对象 -->
<bean id="course1" class="com.test.springCourse">
    <property name="cname" value="Spring5"></property>
</bean>
<bean id="course2" class="com.test.springCourse">
    <property name="cname" value="MyBatis"></property>
</bean>
  • 将集合注入部分提取出来

    1. 在Spring配置文件中引入名称空间util
    2. 使用util标签提取list集合类型属性注入
    <!-- 以之前Student.java为例 -->
    <!-- 1 引入名称空间util -->
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:p="http://www.springframework.org/schema/p"
            xmlns:util="http://www.springframework.org/schema/util"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
            <!-- 使用util标签提取list集合类型属性注入
                对象则使用ref标签,值用value
                -->
            <util:list id="list1">
                <value>张三</value>
                <value>李四</value>
            </util:list>
    
            <bean id="student" class="com.test.spring.Student">
                <property name="list" ref="list1"></property>
            </bean>
    
    </beans>
    
7 基于FactoryBean
  1. Spring有两种类型bean,一种是普通bean,另一种是工厂bean。两者区别:普通bean在配置文件中定义的bean类型就是返回类型;工厂bean在配置文件中定义的bean类型可以和返回类型不同。

  2. 创建工厂bean

    • 第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
    • 第二步:实现接口里面的方法,在实现的方法中定义返回bean类型
    //Example.java
    
    public class Example implements FactoryBean<String> {
    
        // 定义bean返回类型
        @Override
        public String getObject() throws Exception {
            return "aaa";
        }
    
        @Override
        ...
    }
    
8 bean的作用域
  • 在Spring里面,设置创建bean实例是单实例还是多实例
  • 在Spring里面,默认情况下,bean是单实例对象
  • 如何设置单实例还是多实例
    • 在Spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
      • scope属性值
        • singleton,默认值,单实例对象
        • prototype,多实例对象
      • 两者区别:
        • scope值是singleton时,加载Spring配置文件时候就会创建单实例对象
        • scope值是prototype时,不是在加载Spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象
9 bean 的生命周期
  • 生命周期:从对象创建到对象销毁的过程

  • 具体过程:

    1. 创建:通过构造器创建bean实例
    2. 为bean的属性设置值和对其他bean的引用(调用set方法)
    3. 调用初始化方法(需要声明初始化方法,配置bean标签中的init-methode)
    4. 使用bean(对象获取)
    5. 销毁:容器关闭时,调用bean的销毁的方法(需要声明销毁的方法,配置bean标签中的destory-method)
  • bean的后置处理器

    • 加上后置处理器后,bean的生命周期有七步:
      1. 创建:通过构造器创建bean实例
      2. 为bean的属性设置值和对其他bean的引用(调用set方法)
      3. 把bean实例传递给bean后置处理器的方法(接口BeanPostProcessor中的postProcessBeforeInitialization方法)
      4. 调用初始化方法(需要声明初始化方法,配置bean标签中的init-methode)
      5. 把bean实例传递给bean后置处理器的方法(接口BeanPostProcessor中的postProcessAfterInitialization方法)
      6. 使用bean(对象获取)
      7. 销毁:容器关闭时,调用bean的销毁的方法(需要声明销毁的方法,配置bean标签中的destory-method)
10 自动装配
  • 根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

    • 根据属性名称注入

      <!--实现自动装配
      bean标签 : autowire,自动装配
      autowire属性常用两个值:
          byName根据属性名称注入,注入值bean的id值和类属性名称要一样
          byType根据属性类型注入,
      -->
      <bean id="emp" class="com.learning.spring.autowire.Emp" autowire="byName"></bean>
      <bean id="dpt" class="com.learning.spring.autowire.Dpt"></bean>
      
    • 根据属性类型注入

      <!--实现自动装配
      bean标签 : autowire,自动装配
      autowire属性常用两个值:
          byName根据属性名称注入,注入值bean的id值和类属性名称要一样
          byType根据属性类型注入,相同类型只能定义一个
      -->
      <bean id="emp" class="com.learning.spring.autowire.Emp" autowire="byType"></bean>
      <bean id="dpt" class="com.learning.spring.autowire.Dpt"></bean>
      
11 外部属性文件
  • 直接配置数据库信息

    • 举例:置德鲁伊连接池

      <!--直接配置连接池-->
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
          <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
          <property name="username" value="root"></property>
          <property name="password" value="root"></property>
      </bean>
      
  • 引入外部属性文件配置数据库连接池

    • 创建一个外部属性文件,properties格式文件,写数据库信息
      jdbc.properties:

      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/userDb
      prop.userName=root
      prop.password=root
      
      • 把外部的properties属性文件引入到Spring配置文件中(引入context名称空间)
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xmlns:util="http://www.springframework.org/schema/util"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
          <!--引入外部属性文件-->
          <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
      
          <!--配置连接池-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${prop.driverClass}"></property>
              <property name="url" value="${prop.url}"></property>
              <property name="username" value="${prop.userName}"></property>
              <property name="password" value="${prop.password}"></property>
          </bean>
      </beans>
      

3.3.2 基于注解方式

1 什么是注解
  • 注解是代码特殊标记,格式:@注解名称(属性名称=属性值[,属性名称=属性值,...])
  • 使用注解,注解作用再类上面,方法上面,属性上面
  • 目的:简化xml配置
2 Spring针对Bean管理中创建对象提供注解

下面四个注解功能是一样的,都可以用来创建bean实例:

  • @Componret : 建议用在bean层
  • @Service : 建议用在service层
  • @Controller : 建议用在controller层
  • @Repository : 建议用在dao层
3 基于注解方式实现对象创建
  • 第一步:引入依赖(引入spring-aop jar包)
  • 第二步:开启组件扫描
<!-- 先要引入context名称空间 -->
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
  • 第三步:创建类,在类上面添加创建对象注解
//UserService.java

//在注解里面value属性值可以省略不写
//默认值是类名称,首字母小写
//UserService -> userService
@Component(value = "userService")   //<bean id="userService" class=""/>
public class UserService {

    public void add() {
        System.out.println("add...");
    }
}
4 开启组件扫描细节配置
<!-- 示例1
    use-defaultfilters="false"表示现在不适用默认filters,自己配置filter
    context:include-filter:设置扫描哪些内容
    -->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/><!--代表只扫描Controller注解的类-->
</context:component-scan>

<!-- 示例2
    下面配置扫描包所有内容
    context:exclude-filter:设置哪些内容不进行扫描
    -->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/><!--表示Controller注解的类之外一切都进行扫描-->
</context:component-scan>
5 实现属性注入
  • @Autowired:根据属性类型进行自动装配

    • 第一步 把service和dao对象创建,在service和dao类添加创建对象注解

    • 第二步 在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解

      //UserService.java
      @Service
      public class UserService {
      
          // 1.定义dao类型属性
          // 2.不需要添加set方法
          // 3.添加注入属性注解
          @Autowired
          private UserDao userDao;
          public void add() {
              System.out.println("Service add...");
              userDao.add();
          }
      }
      
      //UserDao实现类  UserDaoImpl.java  
      @Repository
      public class UserDaoImpl implements UserDao{
          @Override
          public void add() {
              System.out.println("add...");
          }
      }
      
  • @Qualifier:根据属性名称进行注入,@Qualifier注解的使用,和上面@Autowired一起使用

@Autowired
//根据名称进行注入(目的在于区别同一接口下有多个实现类,根据类型就无法选择,从而出错!)
@Qualifier(value = "userDaoImpl1")
private UserDao userDao;
  • @Resource:可以根据类型注入,可以根据名称注入(它属于javax包下的注解,不推荐使用!)
//@Resource   //根据类型注入
@Resource(name = "userDaoImpl1")    //根据名称注入
private UserDao userDao;
  • @Value:注入普通类型属性
@Value(value = "abc")
private String name;
6 完全注解开发
  • 创建配置类,替代xml配置文件
@Configuration  // 作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.learning.spring"})
public class SpringConfig {
}
  • 编写测试类
@Test
public void testService2() {
    // 加载配置类
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    System.out.println(userService);
    userService.add();
}

4 AOP

4.1 AOP基本概念

  1. 面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
  3. 使用登录例子说明 AOP
    登录例子

4.2 AOP底层原理

  • AOP 底层使用动态代理 ,动态代理有两种情况:
    1. 有接口情况,使用JDK动态代理:创建接口实现类代理对象,增强类的方法
      AOP有接口情况
    2. 没有接口的情况,使用CGLIB动态代理:创建子类的代理对象,增强类的方法
      AOP没有接口情况

4.2.1 AOP底层原理——JDK动态代理实现

  1. 使用JDK动态代理,使用Proxy类里面的方法来创建对象

    • 调用newProxyInstance方法
    public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    
    • 方法有三个参数:
      • 第一参数,类加载器
      • 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
      • 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
  2. 编写JDK动态代理代码

    • 第一步:创建接口,定义方法
    public interface UserDao {
    
        public int add(int a, int b);
    
        public String update(String id);
    
    }
    
    • 第二步:创建接口实现类,实现方法
    public class UserDaoImpl implements UserDao{
        @Override
        public int add(int a, int b) {
            return a + b;
        }
    
        @Override
        public String update(String id) {
            return id;
        }
    }
    
    • 第三步:使用Proxy类创建接口代理对象
    public class JDKProxy {
        public static void main(String[] args) {
            //创建接口代理类
            Class[] interfaces = {UserDao.class};
            UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new UserDaoImpl()));
    
            int result = dao.add(1 ,2);
            System.out.println(result);
        }
    }
    
    class UserDaoProxy implements InvocationHandler {
    
        // 1. 把创建的是谁的代理对象,把谁传递过来
        // 有参构造传递
        private Object obj;
    
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
    
        // 增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            // 方法之前
            System.out.println("方法之前执行 ** " + method.getName() + " ** " + Arrays.toString(args));
    
            // 被增强的方法执行
            Object res = method.invoke(obj, args);
    
            //方法之后
            System.out.println("方法之后执行 ** " + obj);
    
            return res;
        }
    }
    

4.3 AOP术语

  1. 连接点:类里面哪些方法可以被增强,这些方法称为连接点

  2. 切入点:实际被真正增强的方法称为切入点

  3. 通知(增强):实际增强的逻辑部分称为通知,且分为以下五种类型:

    • 前置通知:方法之前执行增强部分
    • 后置通知:方法之后执行增强部分
    • 环绕通知:方法前后执行增强部分
    • 异常通知
    • 最终通知:类似finally
  4. 切面:把通知应用到切入点过程

4.4 AOP操作

4.4.1 准备工作

  1. Spring框架一般都是基于AspectJ实现AOP操作

    • 什么是AspectJ:AspectJ不是Spring组成部分,独立AOP框架,一般把 AspectJ和Spirng框架一起使用,进行AOP操作
  2. 基于AspectJ实现AOP操作

    • 基于xml配置文件实现
    • 基于注解方式实现(使用)
  3. 引入相关jar包

  4. 切入点表达式,如下:

//(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强 
//(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )  其中权限修饰符可以省略,但返回类型不能省略(*匹配所有返回类型)
//(3)例子如下:

//例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))
//例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
//例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))

4.4.2 AspectJ注解

  1. 创建类,在类里边定义方法

    // 被增强的类
    public class User {
        public void add() {
            System.out.println("add...");
        }
    }
    
  2. 创建增强类(编写增强逻辑)

    • 在增强类中创建方法,让不同方法代表不同通知类型
    // 增强的类
    public class UserProxy {
    
        // 前置通知
        public void before() {
            System.out.println("before...");
        }
    }
    
  3. 进行通知的配置
    1、在Spring配置文件中,开启注解扫描,开启生成代理对象

    <?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"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--开启注解扫描-->
        <context:component-scan base-package="com.atguigu"></context:component-scan>
    
        <!-- 开启生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    或者用配置类

    @Configuration
    @ComponentScan(basepackage = {"com.atguigu"})
    @EnableAspectJAutoProxy(proxyTargetClass = true)   //不写,默认是true
    public class ConfigAop {
    
    }
    

    2、使用注解创建User和UserProxy对象,在增强类上面添加注解@Aspect

    // 被增强的类
    @Component
    public class User
    
    @Component
    @Aspect
    public class UserProxy
    

    3、配置不同类型的通知
    (1)在增强类的里边,在作为通知方法上面添加通过类型注解,使用切入点表达式

    // 增强的类
    @Component
    @Aspect
    public class UserProxy {
    
        // 相同切入点抽取
        @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
        public void pointdemo() {
    
        }
    
        // 前置通知
        // @Before注解表示作为前置通知
        @Before(value = "pointdemo()")  // 相同切入点使用
        public void before() {
            System.out.println("before...");
        }
    
        //后置通知(返回通知)
        @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
        public void afterReturning() {
            System.out.println("afterReturning.........");
        }
    
        //最终通知
        @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
        public void after() {
            System.out.println("after.........");
        }
    
        //异常通知
        @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
        public void afterThrowing() {
            System.out.println("afterThrowing.........");
        }
    
        //环绕通知
        @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕之前.........");
    
            //被增强的方法执行
            proceedingJoinPoint.proceed();
    
            System.out.println("环绕之后.........");
        }
    }
    
  4. 有多个增强类对同一个方法进行增强,设置增强类优先级

//(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{ }

4.4.3 AspectJ配置文件

<!--1、创建两个类,增强类和被增强类,创建方法(同上一样)-->
<!--2、在 spring 配置文件中创建两个类对象-->
<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

<!--3、在 spring 配置文件中配置切入点-->
<!--配置 aop 增强-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
    <!--配置切面-->
    <aop:aspect ref="bookProxy">
        <!--增强作用在具体的方法上-->
        <aop:before method="before" pointcut-ref="p"/>
    </aop:aspect>
</aop:config>

5 JdbcTemplate

5.1 JdbcTemplate概念及使用

  1. Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
posted @ 2021-04-22 09:59  哈啤丶  阅读(99)  评论(0)    收藏  举报