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

Spring笔记

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

Spring笔记

Spring

文章目录

Spring

1. Spring框架

1.1 概述1.2 优点1.3 Spring体系架构 2. IoC控制反转

2.1 概述2.2 创建一个Spring项目

2.2.1 创建maven项目,并导入spring依赖2.2.2 创建业务接口与实现类2.2.3 创建Spring配置文件2.2.4 创建测试类 2.3 基于XML的DI

2.3.1 set注入

简单类型(Java中的基本类型和String类型)引用类型: 2.3.2 构造注入(了解)2.3.3 引用类型的自动注入

byNamebyType 2.3.4 为应用指定多个 Spring 配置文件 2.4 基于注解的DI

2.4.1 定义 Bean 的注解@Component2.4.2 简单类型属性注入@Value2.4.3 byType 自动注入@Autowired2.4.4 byName 自动注入@Autowired 与@Qualifier2.4.5 JDK 注解@Resource 自动注入

byType 注入引用类型属性byName 注入引用类型属性 2.5 注解与 XML 的对比 3. AOP面向切面编程

3.1 动态代理3.2 AOP概述3.3 AOP的相关术语3.4 AOP的实现3.5 AspectJ 对 AOP 的实现

3.5.1 AspectJ 的通知类型3.5.2 AspectJ 的切入点表达式3.5.3 AspectJ 的开发环境3.5.4 AspectJ 基于注解的 AOP 实现

定义业务接口与实现类 3.5.5 AspectJ 通知注解

@Before 前置通知@AfterReturning 后置通知@Around 环绕通知@AfterThrowing 异常通知(了解)@After最终通知(了解)@Pointcut 定义切入点 3.5.6设置AspectJ 实现 AOP的方式 4. Spring集成 MyBatis

4.1 概述4.2 流程

MySQL 创建数据库,创建表加入依赖实体类StudentDao接口和sql映射文件mybatis主配置文件Service接口和实现类spring的配置文件(重要)测试 5. Spring事务

5.1 Spring 事务管理 API

5.1.1 事务管理器接口5.1.2 定义接口

事务隔离级别事务传播行为事务超时时间 5.2 电商项目

创建数据库表添加依赖创建实体类定义 dao 接口定义 dao 接口对应的 sql 映射文件定义异常类定义 Service 接口定义 Service 的实现类定义 Spring 配置文件内容定义测试类 5.3 使用 Spring 的事务注解管理事务

声明事务管理器开启注解驱动业务层 Service 的 public 方法加入事务属性 5.4 使用 AspectJ 的 AOP 配置管理事务

添加依赖添加事务管理器配置事务通知配置增强器 6. Spring与Web

添加依赖注册监听器在Sevlet中获取ApplicationContext对象

1. Spring框架 1.1 概述

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

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

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

官网: https://spring.io/

1.2 优点
    轻量:Spring的所需要的jar包都非常小,一般1M以下,几百kb。核心功能所需要的jar包总共3M左右。
    Spring框架运行占有资源少,运行效率高,不依赖其他jar。、针对接口编程,解耦合AOP 编程的支持方便集成各种优秀框架
1.3 Spring体系架构

Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、提供JVM的代理 (Instrumentation)、消息发送(Messaging)、 核心容器(Core Container)和测试(Test)。

2. IoC控制反转 2.1 概述

控制反转(IoC,Inversion of Control):是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

控制:创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,属性赋值。

正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

public static void main(String args[]){
	Student student = new Student(); // 在代码中, 创建对象。--正转。
}

容器:是一个服务器软件, 一个框架(spring)

使用 ioc 目的: 减少对代码的改动,也能实现不同的功能。 实现解耦合。

java中创建对象有哪些方式:

    构造方法 , new Student()反射序列化克隆ioc :容器创建对象动态代理

IoC的技术实现:
依赖注入(DI,Dependency Injection):依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,赋值,查找都由容器内部实现。

spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。

spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。

2.2 创建一个Spring项目 2.2.1 创建maven项目,并导入spring依赖

导入依赖:


    
    
      junit
      junit
      4.12
      test
    
     
    
      org.springframework
      spring-context
      5.2.5.RELEASE
    

2.2.2 创建业务接口与实现类

接口:

public interface SomeService {
    void doSome();
}

实现类:

public class SomeServiceImpl implements SomeService {
    
    @Override
    public void doSome() {
        System.out.println("====SomeServiceImpl业务方法doSome=====");
    }
}

ps:spring默认调用无参数构造方法创建对象

2.2.3 创建Spring配置文件

在 src/main/resources/目录下创建一个 xml 文件,文件名随意,但Spring 建议的名称为 applicationContext.xml。 IDEA已经设计好了Spring配置文件的模板:
右击resources–>new–>XML configuration file–>Spring Config

配置配置文件:




    
    

    
    



声明bean ,告诉spring要创建某个类的对象:

id:对象的自定义名称,唯一值。 spring通过这个名称找到对象

class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

scope:指定bean对象的作用域(对象的存在范围和可见性)。可取值:

单例:singleton , 默认值,表示叫这个名称的对象在spring容器中只有一个。原型:prototype , 表示每次使用getBean()都创建一个新的对象。

spring就完成 SomeService someService = new SomeServiceImpl();

spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
springMap.put(id的值, 对象);

例如 :springMap.put("someService", new SomeServiceImpl());

一个bean标签声明一个对象。

2.2.4 创建测试类
@Test
public void test01(){

    //定义Spring的配置文件, 配置文件是在类路径的根目录之下
    String config = "applicationContext.xml";

    //创建Spring的容器对象.根据Spring配置文件的位置,使用接口的不同实现类
    //1.如果Spring的配置文件是在类路径(classpath),使用ClassPathXmlApplicationContext
    //2.如果Spring的配置文件是放在项目的根之下(与src、target同级目录),使用FileSystemXmlApplicationContext
    //创建Spring容器,会读取配置文件中的bean标签,并创建对应的对象。
    ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

    //从容器中获取对象 使用getBean("的id")
    SomeService service = (SomeService) ctx.getBean("someService");

    //调用业务方法
    service.doSome();
}

2.3 基于XML的DI

通过在xml配置文件对对象的属性进行赋值。

di的实现有两种:

    在spring的配置文件中, 使用标签和属性完成,叫做基于XML的di实现

    使用spring中的注解,完成属性赋值, 叫做基于注解的id实现

di的语法分类:

    set注入(设置注入): spring调用类的set方法,在set方法可以实现属性的赋值。
    80左右都是使用的set注入

    构造注入,spring调用类的有参数构造方法,创建对象。在构造方法中完成赋值。

2.3.1 set注入

set注入也叫设值注入是指,通过setter方法传入被调用者的实例。这种注入方式简单、直观,因而在Spring的依赖注入中大量使用。

简单类型(Java中的基本类型和String类型)

实体类:

public class Student {

    private String name;
    private int age;

    
   public void setName(String name) {
        this.name = name.toUpperCase();
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    public void setEmail(String email){
        System.out.println("setEmail="+email);
    }
}   

applicationContext.xml:


    
    
    

引用类型:

当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref 的 值必须为某 bean 的 id 值。

实体类:(都有set方法)

public class School {
    private String name;
    private String address;
}
public class Student {
    private String name;
    private int age;

    //声明一个引用类型
    private School school;
}

applicationContext.xml:


	


    
    
    
    




	
    

2.3.2 构造注入(了解)

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。

给实体类添加构造器:

//定义有参数构造方法
public  Student(String myname, int myage, School mySchool){
    System.out.println("Student有参数构造方法");
    //给属性完成赋值
    this.name = myname;
    this.age  = myage;
    this.school = mySchool;
}

applicationContext.xml:

标签:一个表示构造方法一个参数。
标签属性:

name:表示构造方法的形参名index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序value:构造方法的形参类型是简单类型的,使用valueref:构造方法的形参类型是引用类型的,使用ref

	
    
        
        
        
    

    
    
        
        
        
    

    
    
        
        
        
    
    
    
        
        
    
2.3.3 引用类型的自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签 设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据自动注入判断标准的不同,可以分为两种:

byName:根据名称自动注入byType: 根据类型自动注入 byName

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用 byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。

java类中引用类型的属性名和spring容器中(配置文件)的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。

  
     简单类型属性赋值
  

    
    
    
    




    
    

byType

byType(按类型注入) :java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系的,这样的bean能够赋值给引用类型.

同源就是一类的意思:

    java类中引用类型的数据类型和bean的class的值是一样的。java类中引用类型的数据类型和bean的class的值父子类关系的。java类中引用类型的数据类型和bean的class的值接口和实现类关系的

    简单类型属性赋值


注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的

    
    
    
    




    
    




2.3.4 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变 得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。

包含关系的配置文件: 多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java 代码中只需要使用总配置文件对容器进行初始化即可。例如:

spring-total表示主配置文件:包含其他的配置文件的,主配置文件一般是不定义对象的。

语法:

关键字:“classpath:” 表示类路径(class文件所在的目录)




2.4 基于注解的DI

    加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。使用注解必须使用spring-aop依赖

    在类中加入spring的注解

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

//@Component: 创建类的对象,等同于,默认是单例对象
@Component("myStudent")
public class Student {

    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

    在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置
	
    
    

    
    
    
    

    
    

    
    

2.4.1 定义 Bean 的注解@Component

@Component: 创建类的对象,等同于,默认是单例对象
属性: value 表示对象的名称(的id)
位置: 在类定义的上面,表示创建此类的对象。

例如:@Component(value = "myStudent")等价于

另外,Spring 还提供了 3 个创建对象的注解:

@Repository 用于对 DAO 实现类进行注解

@Service 用于对 Service 实现类进行注解

@Controller 用于对 Controller 实现类进行注解

这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service 创建业务层对象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求

@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。

@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。

//使用value属性,指定对象名称
@Component(value = "myStudent")

//省略value
@Component("myStudent")

//不指定对象名称,由spring提供默认名称: 类名的首字母小写
@Component
2.4.2 简单类型属性注入@Value

@Value: 简单类型的属性赋值
属性: value 是String类型的, 表示简单类型的属性值
位置:

    在属性定义的上面, 无需set方法,常用的方式

    在set方法上面

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {

    @Value("张三01")
    private String name;

    private int age;

    public void setName(String name) {
        this.name = name;
    }

    @Value("28")
    public void setAge(int age) {
        System.out.println("setAge:"+age);
        this.age = age;
    }
    
}
2.4.3 byType 自动注入@Autowired

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配Bean的方式。

使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。 举例:

School类:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("mySchool")
public class School {
   @Value("人民大学")
   private  String name;
   @Value("北京的海淀")
   private  String address;
}

Student类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {

    @Value("张三")
    private String name;

	@Value("23")
    private int age;

    
    //byType
    @Autowired
    private School school;

}

ps: 为了防止空指针异常的产生,推荐@AutoWired注解加在带参构造器上

2.4.4 byName 自动注入@Autowired 与@Qualifier

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。举例:
如果Student要自动注入2.4.3中的School类,只需在school属性前添加注解:

//byName
@Autowired
@Qualifier("mySchool")//与School类的@component注解value值相同
private School school;

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运 行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

2.4.5 JDK 注解@Resource 自动注入

Spring提供了对jdk中@Resource注解的支持。@Resource注解既可以按名称匹配Bean, 也可以按类型匹配Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。 @Resource 可在属性上,也可在 set 方法上。

byType 注入引用类型属性

@Resource 注解若不带任何参数,采用默认按类型的方式注入,按名称不能注入 bean, 则会按照类型进行 Bean 的匹配注入。

如果Student要自动注入2.4.3中的School类,只需在school属性前添加注解:

@Resource
private School school;
byName 注入引用类型属性

@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

如果Student要自动注入2.4.3中的School类,只需在school属性前添加注解:

@Resource("mySchool")
private School school;
2.5 注解与 XML 的对比

注解优点是:

方便

直观

高效(代码少,没有配置文件的书写那么复杂)。

其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 方式优点是:

配置和代码是分离的

在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

3. AOP面向切面编程 3.1 动态代理

实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口

cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的

动态代理的作用:

    在目标类源代码不改变的情况下,增加功能。

    减少代码的重复

    专注业务逻辑代码

    解耦合,让你的业务功能和日志,事务非业务功能分离

3.2 AOP概述

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。 AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理,AOP可以看作动态代理的规范化与标准化。

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

Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。

Orient:面向, 对着。

Programming:编程

ps:oop:面向对象编程

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到 主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、 事务、日志、缓存等。 若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事 务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占 比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大 大干扰了主业务逻辑—转账。

3.3 AOP的相关术语

    切面(Aspect)
    通知和切入点共同组成了切面。切面泛指交叉业务逻辑。实际就是对主业务逻辑的一种增强。

    连接点(JoinPoint)
    连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

    切入点(Pointcut)
    切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

    目标对象(Target)
    目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。

    通知(Advice)
    通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。Advice 也叫增强。通知就是你想要的功能,在特定的连接点,AOP框架执行的动作。

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {}

    @Override
    public void doOther() {}
}

doSome,doOther为切入点如果开发过程中,只对doSome方法进行增强,doSome为连接点 3.4 AOP的实现

AOP的技术实现框架:

    Spring:spring在内部实现了aop规范,能做aop的工作。spring主要在事务处理时使用aop。实际项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。aspectJ:一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
3.5 AspectJ 对 AOP 的实现

aspectJ框架实现AOP有两种方式:

    使用xml配置文件,一般用于配置全局事务;

    使用注解。一般在项目开发中使用这种方式。

3.5.1 AspectJ 的通知类型

AspectJ 中常用的通知有五种类型,体现在五个不同的添加在切面的注解:

    前置通知后置通知环绕通知异常通知最终通知
3.5.2 AspectJ 的切入点表达式

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

execution ( [modifiers-pattern]  访问权限类型
	 ret-type-pattern 返回值类型
	  [declaring-type-pattern]  全限定性类名 
	  name-pattern(param-pattern) 方法名(参数类型和参数个数) 
	 [throws-pattern]  抛出异常类型 )

以上表达式共 4 个部分。

execution(访问权限 方法返回值 方法声明(参数) 异常类型) 加粗字体不可省略

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。

在其中可以使用以下符号:

*:0至多个任意字符

..: 用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包

+:用在类名后,表示当前类及其子类;用在接口名后,表示当前接口及其实现类

举例:

execution(public * *(.sqt.)) 
指定切入点为:任意公共方法。
execution(* set*(.sqt.)) 
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(.sqt.)) 
指定切入点为:定义在 service 包里的任意类的任意方法。
3.5.3 AspectJ 的开发环境

创建Maven项目时,引入AspectJ依赖:


  junit
  junit
  4.12
  test



  org.springframework
  spring-context
  4.3.16.RELEASE



  org.springframework
  spring-aspects
  4.3.16.RELEASE

3.5.4 AspectJ 基于注解的 AOP 实现
    定义业务接口与实现类
public interface SomeService {
	void doSome(String name, int age);
}
public class SomeServiceImpl implements SomeService {

	@Override
	public void doSome(String name,int age) {
    	System.out.println("SomeSeviceImpl的业务方法doSome");
	}
}
    定义切面类

@Aspect : 是aspectj框架中的注解。

作用:表示当前类是切面类。切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码位置:在类定义的上面

定义方法,方法是实现切面功能的。

方法的定义要求:

    公共方法 public

    方法没有返回值

    方法名称自定义

    方法可以有参数,也可以没有参数。

    如果有参数,参数不是自定义的,有几个参数类型可以使用。

@Aspect
public class TestAspect {

	@Before("execution(public * *(.sqt.))")
	public void printTime(){
    	System.out.println("方法执行时间=" + new Date());
	}
}
    在applicationContext.xml中, 声明目标对象与切面类对象,注册AspectJ的自动代理

通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。







    测试(注意获取代理对象根据目标对象的id)
@Test
public void test01(){
   ApplicationContext ac = new ClassPathXmlApplicationContext("test.xml");
   SomeService proxy = (SomeService) ac.getBean("someService");

   proxy.doSome("zhangsan",25);
}
3.5.5 AspectJ 通知注解

一共有五种通知类型,对应了五种通知注解。

@Before 前置通知

@Before前置通知-方法有JoinPoint参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。

JoinPoint:业务方法,要加入切面功能的业务方法

作用:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。如果你的切面功能中需要用到方法的信息,就加入JoinPoint.

这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数

@Before(value = "execution(* *.sqt.SomeServiceImpl.doSome(.sqt.))")
public void myBefore(JoinPoint jp){

    //获取方法的定义
    System.out.println("连接点的方法定义:"+jp.getSignature());
    System.out.println("连接点的方法名称:"+jp.getSignature().getName());
    //获取方法执行时的参数
    Object args [] = jp.getArgs();
    for (Object arg : args) {
    System.out.println(arg);
}
@AfterReturning 后置通知

@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

例如,在业务接口中定义一个有返回值的抽象方法:

String doOther(String name,int age);

在业务类中实现:

@Override
public String doOther(String name, int age) {
    System.out.println("SomeSeviceImpl的业务方法doOther");
    return "abcd";
}

在切面类中定义一个切面:


@AfterReturning(value = "execution(* *.sqt.SomeServiceImpl.doOther(.sqt.))",
                    returning = "res")
public void myAfterReturing(JoinPoint jp,Object res){
    // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
    System.out.println("后置通知:方法的定义"+ jp.getSignature());
    System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res);
    if(res.equals("abcd")){
        //做一些功能
    } else{
        //做其它功能
    }

    //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果
    if( res != null){
        res = "Hello Aspectj";
    }
}
@Around 环绕通知

@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前之后执行。

被注解为环绕增强的方法要有返回,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 继承于JoinPoint,因此可以根据它获取方法的信息。其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

接口增加方法:

String doFirst(String name,int age);

实现类:

@Override
public String doFirst(String name, int age) {
    System.out.println("SomeSeviceImpl的业务方法doFirst");
    return "doFirst";
}

增加切面:

@Aspect
public class MyAspect {
    

    
    @Around(value = "execution(* *.sqt.SomeServiceImpl.doFirst(.sqt.))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {

        String name = "";
        //获取第一个参数值
        Object args [] = pjp.getArgs();
        if( args!= null && args.length > 1){
              Object arg=  args[0];
              name =(String)arg;
        }

        //实现环绕通知
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date());
        //1.目标方法调用
        if( "zhangsan".equals(name)){
            //符合条件,调用目标方法
            result = pjp.proceed(); //method.invoke(); Object result = doFirst();

        }

        System.out.println("环绕通知:在目标方法之后,提交事务");
        //2.在目标方法的前或者后加入功能

        //修改目标方法的执行结果, 影响方法最后的调用结果
        if( result != null){
              result = "Hello AspectJ AOP";
        }

        //返回目标方法的执行结果
        return result;
    }
}
@AfterThrowing 异常通知(了解)

@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

在效果上相当于一个try…catch语句。目标方法的方法体在try语句块中,而切面方法的方法体放在了catch子句中。

切面:

@AfterThrowing(value = "execution(* *.sqt.SomeServiceImpl.doSecond(.sqt.))",
               throwing = "ex")
public void myAfterThrowing(Exception ex) {
    System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());
    //发送邮件,短信,通知开发人员
}
@After最终通知(了解)

无论目标方法是否抛出异常,该增强均会被执行。

在执行效果上,相当于将切面方法的方法体放在了try…catch…finally…语句发finally子句中。

@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。 AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均 可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。 方法体内部也无需添加代码。

@Aspect
public class MyAspect {

    @After(value = "mypt()")
    public  void  myAfter(){
        System.out.println("执行最终通知,总是会被执行的代码");
        //一般做资源清除工作的。
     }

    @Before(value = "mypt()")
    public  void  myBefore(){
        System.out.println("前置通知,在目标方法之前先执行的");
    }

    
    @Pointcut(value = "execution(* *.sqt.SomeServiceImpl.doThird(.sqt.))" )
    private void mypt(){
        //无需代码,
    }
}
3.5.6设置AspectJ 实现 AOP的方式

在Spring配置文件中,通过的proxy-target-class属性设置选择通过JDK动态代理还是cglib动态代理实现AOP。



4. Spring集成 MyBatis 4.1 概述

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。

实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理

Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。

4.2 流程 MySQL 创建数据库,创建表

加入依赖

    
    
        junit
        junit
        4.11
        test
    
    
    
        org.springframework
        spring-context
        5.2.5.RELEASE
    
    
    
        org.springframework
        spring-tx
        5.2.5.RELEASE
    
    
        org.springframework
        spring-jdbc
        5.2.5.RELEASE
    
    
    
        org.mybatis
        mybatis
        3.5.1
    
    
    
        org.mybatis
        mybatis-spring
        1.3.1
    
    
    
        mysql
        mysql-connector-java
        5.1.9
    
    
    
        com.alibaba
        druid
        1.1.12
    



    
    
        
            src/main/java
            
                ***.xml
            
            false
        
    
    
    
        
            maven-compiler-plugin
            3.1
            
                1.8
                1.8
            
        
    

实体类Student
public class Student {
    //属性名和列名一样。
    private Integer id;
    private String name;
    private String email;
    private Integer age;

    public Student() {
    }

    public Student(Integer id, String name, String email, Integer age) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", email='" + email + ''' +
                ", age=" + age +
                '}';
    }
}

Dao接口和sql映射文件
public interface StudentDao {

    int insertStudent(Student student);
    List selectStudents();
}




    
        insert into student values(#{id},#{name},#{email},#{age})
    

    
        select id,name,amount,price from goods where id = #{gid}
    

    
        update goods set amount = amount - #{amount} where id = #{id}
    

定义异常类

定义 service 层可能会抛出的异常类 NotEnoughException

public class NotEnoughException extends RuntimeException{
    public NotEnoughException() {
        super();
    }
    public NotEnoughException(String message) {
        super(message);
    }
}
定义 Service 接口

定义 Service 接口 BuyGoodsService

public interface BuyGoodsService {
    void buy(Integer goodsId,Integer nums);
}
定义 Service 的实现类

定义 Service 层接口的实现类 BuyGoodsServiceImpl

public class BuyGoodsServiceImpl implements com.sqt.service.BuyGoodsService {

    private SaleDao saleDao;
    private GoodsDao goodsDao;

    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }

    @Override
    public void buy(Integer goodsId, Integer nums) {
        System.out.println("=====buy方法的开始====");
        //记录销售信息,向sale表添加记录
        Sale sale  = new Sale();
        sale.setGid(goodsId);
        sale.setNums(nums);
        saleDao.insertSale(sale);

        //更新库存
        Goods goods  = goodsDao.selectGoods(goodsId);
        if( goods == null){
            //商品不存在
            throw  new  NullPointerException("编号是:"+goodsId+",商品不存在");
        } else if( goods.getAmount() < nums){
            //商品库存不足
            throw new NotEnoughException("编号是:"+goodsId+",商品库存不足");
        }
        //修改库存了
        Goods buyGoods = new Goods();
        buyGoods.setId( goodsId);
        buyGoods.setAmount(nums);
        goodsDao.updateGoods(buyGoods);
        System.out.println("=====buy方法的完成====");
    }
}
定义 Spring 配置文件内容

applicationContext.xml:




    
    

    
    
        
        
        
        
        
        
    

    
    
        
        
        
        
    

    
    
        
        
        
        
    

    
    
        
        
    

jdbc.properties:

jdbc.url=jdbc:mysql://localhost:3306/test2
jdbc.username=root
jdbc.passwd=123456
jdbc.max=30
定义测试类

定义测试类 MyTest。现在就可以在无事务代理的情况下运行了。

public class Mytest {
    @Test
    public void test01(){
        String config = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        BuyGoodsService service = (BuyGoodsService) ac.getBean("buyService");
        service.buy(1002,200);
    }
}
5.3 使用 Spring 的事务注解管理事务

在电商项目的基础上改动

声明事务管理器

    
    

其中property标签的ref是配置文件中数据源对象的id属性值

开启注解驱动

业务层 Service 的 public 方法加入事务属性
//    @Transactional(propagation = Propagation.REQUIRED,
//            isolation = Isolation.DEFAULT, timeout = 20,
//            rollbackFor = {NullPointerException.class,NotEnoughException.class})
@Transactional

@Transactional 的所有可选属性如下所示:

propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。

isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。

readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值 为 false。

timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为 -1,即没有时限。在实际业务开发中一般不设置。

rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有 一个异常类时,可以不使用数组。

rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。 当然,若只有一个异常类时,可以不使用数组。

noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若 只有一个异常类时,可以不使用数组。

noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空 数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该 方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。 若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

5.4 使用 AspectJ 的 AOP 配置管理事务

在电商项目的基础上改动

添加依赖

新加入 aspectj 的依赖坐标

 
    org.springframework 
    spring-aspects 
    5.2.5.RELEASE 

添加事务管理器

    
    

配置事务通知

为事务通知设置相关属性。用于指定要将事务以什么方式织入给哪些方法。


    
    
        
        

        
        
        
        
        
        
        
        
    

配置增强器

指定将配置好的事务通知,织入给谁。 在上一步中我们指定了事务管理的方法。现在来指定这些方法所在的类


    
    

    
    

6. Spring与Web

代码实现:https://github.com/sqtsqt/spring-web

解决不同Servlet中重复创建ApplicationContext对象,造成内存浪费的问题。

解决这个问题的一个思路是,创建一个ServletContextListener,在ServletContext初始化的时候创建ApplicationContext对象,并将它保存在ServletContext中。

这样,在每个servlet中,只要调用当前servlet的ServletContext对象getAttribute方法就可以获取这个webapp中共享的一个ApplicationContext对象。

spring-web框架已经帮我们创建好了这样一个监听器。我们只需要在web.xml注册这个监听器就可以使用了。

添加依赖

  org.springframework
  spring-web
  4.3.16.RELEASE

注册监听器

在注册监听器时需要为监听器提供Spring的配置文件路径信息

配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。
用户发起请求就可以直接使用对象了。

监听器被创建对象后,会读取/WEB-INF/spring.xml

为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径

可以修改默认的文件位置,使用context-param重新指定文件的位置

在web.xml中:


    
    contextConfigLocation
    
    classpath:spring.xml


    org.springframework.web.context.ContextLoaderListener

在Sevlet中获取ApplicationContext对象
WebApplicationContext ctx = null;
String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr = getServletContext().getAttribute(key);
if( attr != null){
    ctx = (WebApplicationContext)attr;
}

webApplicationContext是ApplicationContext的子类,是在web项目中使用的Spring容器对象。

为了不使用框架给出的难记的key值获取webApplicationContext,这个框架还提供了一个工具类。使用工具类获取webApplicationContext:

//获取ServletContext中的容器对象,spring提供了一个方法,获取容器对象
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(
getServletContext());

注:参考动力节点的spring视频及笔记,此文章只作为自己的学习笔记用

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

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

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