Spring笔记

1.IoC 控制反转

1.1 概念

spring的第一个核 心功能ioc
IoC (Inversion of Control) :控制反转,是一个理论, 概念,思想。
描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是有其它外部资源完成。
控制:
创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。由容器代替开发人员管理对象。创建对象,
给属性赋值.
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象。
public static void main (String args[]) {
student student = new student(); //在代码中,创建对象。 -- 正转。

}

容器:是一个服务器软件,一 个框架( spring )
为什么要使用ioc :目 的就是减少对代码的改动,也 能实现不同的功能。实现解耦合。

ioc的体现:

  • servlet: 创建类 继承HttpServelt
  • 在web . xml注册servlet,使用
<servlet-name> myservlet </servlet- name>
<servelt-class>com.cjj.controller.MyServlet1</servlet-class>
  • 没有创建Servlet对象,没有MyServlet myservlet = new MyServlet ()

  • Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器
    Tomcat作为容器:里面存放的有Servlet对象,Listener ,filter对象

Ioc的技术实现
DI是ioc的技术实现,
DI(DependencyInjection):依赖注入,只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建,赋值,查找都由容器内部实现。
spring是使用的di实现了ioc的功能,spring底层 创建对象,使用的是反射机制。

1.2 Spring使用步骤

1.在pom.xml中导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cjj</groupId>
    <artifactId>Spring-Study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring-01-hellospring</module>
    </modules>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!-- spring依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>

    </dependencies>
    <!--资源导出-->
    <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>

</project>

2.创建一个接口MyService

package com.cjj.service;

public interface MyService {
    public void doService();
}

3.创建实现类MyServiceImpl

package com.cjj.service.Impl;

import com.cjj.service.MyService;

public class MyServiceImpl implements MyService {

    @Override
    public void doService() {
        System.out.println("执行了doService方法");
    }
}

4.创建spring IoC容器applicationContext.xml配置文件,

并在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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
		告诉spring创建对 象
		声明bean, 就是告 诉spring要创建某个类的对象
		id:对象的自定义名称,唯一值。spring通过这 个名称找到对象
		class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
	-->
    
    <bean id="MyService" class="com.cjj.service.Impl.MyServiceImpl" />

</beans>
<!--
spring的配置文件
1.beans :是根标签,spring把java对象成为bean。
2. spring -beans.xsd是约束文件,和mybatis指定
dtd是一样的。
-->

5.在测试类中运行

package com.cjj;

import com.cjj.service.MyService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test1(){
        //创建spring容器对象ApplicationContext
        //ApplicationContext就是表示Spring容器,通过容器获取对象
        //ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取对象
        MyService myService = (MyService) context.getBean("MyService");
        //执行对象中的方法
        myService.doService();
    }
}

1.3 Spring创建对象的时机

spring默认创建对象的时机:在创建spring的容器时,会创建配置文件中所有对象,

spring创建对象:默认调用的是无参构造

1.我们在实现类中写上无参构造

package com.cjj.service.Impl;

import com.cjj.service.MyService;

public class MyServiceImpl implements MyService {

    public MyServiceImpl() {
        System.out.println("无参构造");
    }

    @Override
    public void doService() {
        System.out.println("执行了doService方法");
    }
}

2.在测试类中运行

@Test
public void test1(){
    //创建spring容器对象ApplicationContext
    //ApplicationContext就是表示Spring容器,通过容器获取对象
    //ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    MyService myService = (MyService) context.getBean("MyService");
    //执行对象中的方法
    myService.doService();
}

3.运行结果
结果显示无参构造被执行。

1.3.1 getBeanDefinitionCount();方法获取对象的数量

获取容器中对象的数量

1.3.2 getBeanDefinitionNames();方法获取对象的名称

获取容器中对象的名称

1.在测试类中使用着两个方法

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从容器中获取对象
        MyService myService = (MyService) context.getBean("MyService");
        //获取容器中对象的数量
        int nums = context.getBeanDefinitionCount();
        System.out.println("容器中对象的数量为:"+nums);
        //获取容器中对象的名称
        String[] names = context.getBeanDefinitionNames();
        System.out.print("容器中所有对象的名称为:");
        for (String name : names) {
            System.out.println(name);
        }
    }

1.4 创建非自定义对象

<bean id="mydate" class="java.util.Date"/>
@Test
public void test3(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //从容器中获取对象
    Date mydate = (Date) context.getBean("mydate");
    System.out.println(mydate);
}

image-20210427161138204

2.设置注入

2.1 set注入(设值注入)

set注入使用标签赋值

2.1.1简单类型

简单类型:spring中规定,java的基本数据类型和String都是简单类型。

1.简单的set注入

一个property只能给一个属性赋值 < / bean>

创建一个类

package com.cjj.pojo;

public class Student {

    private String name;
    private int age;

    public Student() {
    }

    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 +
                '}';
    }
}

在applicationContext.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="Student" class="com.cjj.pojo.Student">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
    </bean>

</beans>

测试

package com.cjj;

import com.cjj.pojo.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("Student");
        System.out.println(student);
    }

}

2.1.2 引用类型

引用类型赋值使用 ref = “ ”

<bean id="Student" class="com.cjj.pojo.Student">
    <property name="name" value="张三"/>
    <property name="age" value="18"/>
    <property name="school" ref="School"/>
</bean>

<bean id="School" class="com.cjj.pojo.School">
    <property name="name" value="豫章师范学院"/>
    <property name="address" value="南昌市"/>
</bean>

2.2 构造注入(name/index)

使用实体类的有参构造注入属性值,有两种方式,name和index。

构造注入使用标签赋值

1.给实体类加上有参构造

public Student(String name, int age, School school) {
    this.name = name;
    this.age = age;
    this.school = school;
}

2.在applicationContext.xml中赋值

<!--方法一:使用name赋值-->
<bean id="Student" class="com.cjj.pojo.Student">
    <constructor-arg name="name" value="张三"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg name="school" ref="School"/>
</bean>

<!--方法二:使用index赋值-->
    <bean id="Student" class="com.cjj.pojo.Student">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="18"/>
        <constructor-arg index="2" ref="School"/>
    </bean>

<bean id="School" class="com.cjj.pojo.School">
    <property name="name" value="豫章师范学院"/>
    <property name="address" value="南昌市"/>
</bean>

3.测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("Student");
    System.out.println(student);
}

2.3 引用类型的自动注入

spring框架可以根据某些规则给引用类型赋值,不用手动给引用类型赋值了。

使用的规则常用的是byName, byType.

2.3.1 byName

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

简单类型属性赋值

<bean id="Student" class="com.cjj.pojo.Student" autowire="byName">
    <property name="name" value="张三"/>
    <property name="age" value="18"/>
</bean>

<bean id="school" class="com.cjj.pojo.School">
    <property name="name" value="豫章师范学院"/>
    <property name="address" value="南昌市"/>
</bean>
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("Student");
    System.out.println(student);
}

2.3.2 byType

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

java类中引用类型的数据类型和bean的class的值是一样的。

java类中引用类型的数据类型和bean的class的值父子类关系的。

java类中引用类型的数据类型和bean的class的值接口和实现类关系的

<bean id="Student" class="com.cjj.pojo.Student" autowire="byType">
    <property name="name" value="张三"/>
    <property name="age" value="18"/>
</bean>

<bean id="School" class="com.cjj.pojo.School">
    <property name="name" value="豫章师范学院"/>
    <property name="address" value="南昌市"/>
</bean>
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) context.getBean("Student");
    System.out.println(student);
}

3.使用注解开发

2.基于注解的di:通 过注解完成java对象创建,属性赋值。

学习的注解:

1.@Component

2.@Respotory

3.@Service

4.@Controller

5.@Value

6.@Autowired

7.@Resource

3.1 使用注解的步骤:

1.加入maven的依赖spring-context ,在你加入spring-context的同时,间接 加入spring-aop的依赖。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

使用注解必须使用spring-aop依赖
2.在类中加入spring的注解(多个不同功能的注解)

@Component:创建对象的,等同于的功能
属性:value就是对象的名称,也就是bean的id值,
value的值是唯一的,创建的对象在整个spring容器中就个
位置:在类的上面
@Component(value = "myStudent ")等同于

spring中@Component功能致,创建对象的注解还有:

  1. Repository (用在持久层类的上面) :放在dao的实现类上面
    表示创建dao对象, dao对象是能访问数据库的。
  2. @Service(用在业务层类的上面) :放在service的实现类上面,
    创建service对象, service对象是做业务处理,可以有事务等功能的。
  3. @Controller(用在控制器的上面) :放在控制器(处理器)类的上面,创建控制器对象的,
    控制器对象,能够接受用户提交的参数,显示请求的处理结果。
    以上三个注解的使用语法@Component一样的。
    都能创建对象,但是这三个注解还有额外的功能。
    @Repository,@service, @Controller是给项且的对象分层的。
package com.cjj;

import org.springframework.stereotype.Component;

//方法一:@Component(value = "Student")
//常用方法二:
@Component("Student")
//方法三(对象名默认为类名小写):@Component
public class Student {

    private String name;
    private int age;

    public Student() {
        System.out.println("无参构造创建对象");
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

3.在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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--声明组件扫描器(component-scan),组件就是java对象
base-package :指定注解在你的项目中的包名。
component-scan工作方式: spring会扫描遍历base-package指定的包,
把包中和子包中的所有类,找到类中的注解, 按照注解的功能创建对象,或给属性赋值。
加入了component -scan标签,配置文件的变化:
1.加入一个新的约束文件spring context.xsd
2.给这个新的约束文件起个命名空间的名称
-->
    <context:component-scan base-package="com.cjj"/>

</beans>

4.测试

package com.cjj;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test1(){

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("Student");
        System.out.println(student);
    }

}

5.运行结果

image-20210501143016898

3.2 扫描多个包的方式

<!--指定多个包的三种方式-->
<!--第一种方式:使用多次组件扫描器,指定不同的包-->
<context : component-scan base package="com. bjpowernode .ba01" />
<context : component-scan base- package=”com. bipowernode.ba02"/>
<!--第二种方式:使用分隔符( ;或,)分隔多个包名-->
<context : component -scan base package= ”com. bjpowernode .ba01 ;com bjpowernode.ba02" />
<!--第三种方式:指定父包-->
<context : component-scan base - package="com. bjpowernode" />

3.3 使用注解给简单类型赋值@Value

@Value :
简单类型的属性赋值
属性: value是string类型的 ,表示简单类型的属性值
位置: 1. 在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面

//在属性上使用@Value赋值,也可以在set方法上赋值
//@Value(value = "李四")
@Value("李四")
private String name;
//@Value(value = "20")
@Value("20")
private int age;

image-20210501151016541

3.4 使用注解给引用类型赋值@Autowired

引用类型
@Autowired: spring 框架提供的注解,实现引用类型的赋值。
spring中通过注解给引用类型赋值,使用的是自动注入原理, 支持byName, byType
@Autowired:默认使用的是byType自动注入。
位置:1 )在属性定义的上面,无需set方法,推荐使用
2 )在set方法的上面

3.4.1 使用byType

1.再创建一个类School

package com.cjj;

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

@Component("school")
public class School {
    @Value("豫章师范学院")
    private String name;
    @Value("南昌市")
    private String address;

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

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

2.在student类中引入该属性并使用@Autowired赋值

//@Value(value = "李四")
@Value("李四")
private String name;
//@Value(value = "20")
@Value("20")
private int age;
@Autowired
private School school;

3.测试

    @Test
    public void test1(){

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("Student");
        System.out.println(student);
    }

}

4.结果

image-20210501152802545

3.4.2 使用byName

如果要使用byName方式,需要做的是:
1.在属性上面加入@Autowired
2.在属性上面加入@Qualifier(value= "bean的id") : 表示使用指定名称的bean完成赋值。

@Autowired
//@Qualifier(value = "school")
@Qualifier("school")
private School school;

3.4.3@Autowired的required属性

属性:required , 是一个boolean类型的,默认true
required=true :表示引用类型赋值失败,程序报错,并终止执行。
required=false :引用类型如果赋值失败,程序正常执行 ,引用类型是null

建议使用true

3.4.4 外部配置文件+注解

1.创建properties文件

myname=zhangsan
myage=21

2.在spring配置文件中引入

<context:property-placeholder location="classpath:test.properties" ignore-unresolvable="true"/>

3.在类中使用

@Value("${myname}")
private String name;
@Value("${myage}")
private int age;

3.5 jdk提供的注解@Resource

引用类型
@Resource:来自jdk中的注解, spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
使用的也是自动注入原理,支持byName,byType . 默认是byName
位置: 1. 在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面
默认是byName :先使用byName自动注入,如果byName赋值失败,再使用byType

@Resource(name = "school")
private School school;
@Component("school")
public class School {
    @Value("豫章师范学院")
    private String name;
    @Value("南昌市")
    private String address;

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

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

4.aop

1.动态代理
实现方式: jdk动态代理, 使用jdk中的Proxy, Method, Invocai tonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承 目标类,创建子类。
子类就是代理对象。要求 目标类不能是final的,方 法也不能是final的
2.动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.Aop:

面向切面编程,基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。|

4.AOP (Aspect Orient Programming) 面向切面编程
Aspect:切面,给你的目标类增加的功能,就是切面。像.上面用的日志,事务都是切面。.
切面的特点:一般都是非业务方法,独立使用的。
Orient:面向,对着。
Prograrming:编程

怎么理解面向切面编程?
1)需要在分析项目功能时,]J找出切面。
2)合理的安排切面的执行时间(在目标方法前,还是目标方法后).
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

术语:

  1. Aspect: 切面,表示增强的功能,就是一 堆代码,完成某个一个功能。非业务功能,
    常见的切面功能有日志,
    事务,统计信 息,
    参数检查,权 限验证。
  2. JoinPoint:连接点,连接业务方法和切面的位置。就某类中的业务方法
    Pointcut :切入点,指多个连接点方法的集合。多个方法:
    4)目标对象:给哪个类的方法增加功能,这个类就是目标对象
    5)Advice:通知,通知表示切面功能执行的时间。
    说一个切面有三个关键的要素:
    1)切面的功能代码,切面干什么
    2)切面的执行位置,使用pointcut表示切面执行的位置
    3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

5.aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:

1.spring: spring在 内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop
我们项目开发中很少使用spring的aop实现。因为spring的aop比较笨重.

2.aspectJ: 一 个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件:配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解,aspectj有5个注解。

4.1 aspectj框架的使用

学习aspectj框架的使用。
1)切面的执行时间,这个执行时间在规范中叫做Advice (邇知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签|

  1. @Before
  2. @AfterReturning
  3. @Around
  4. @AfterThrowing
  5. @After

4.1.1切入点语法表达式

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

image-20210503142755189

4.1.2 使用步骤(@Before)

1.新建maven项目
2.加入依赖
1 ) spring依赖
2 ) aspectj依赖
3 ) junit单元测试

<dependencies>
    <!--junit依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <!-- spring依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--aspectj依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
</dependencies>

3.创建目标类:接口和他的实现类。
要做的是给类中的方法增加功能

接口类

package com.cjj.ba01;

public interface SomeService {
    public void dosome(String name,Integer age);
}

实现类

package com.cjj.ba01;

public class SomeServiceImpl implements SomeService{
    @Override
    public void dosome(String name,Integer age) {
        System.out.println("=======执行了dosome()方法=======");
    }
}

4.创建切面类:普通类
1 )在类的上面加入@Aspect
2 )在类中定义方法
方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()

package com.cjj.ba01;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Date;

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


@Aspect
public class MyAspect {
/*
    定义方法,方法是实现切面功能的。
方法的定义要求:
1.公共方法public
2.方法没有返回值
3.方法名称自定义
4.方法可以有参数,也可以没有参数。
如果有参数,参数不是自定义的, 有几个参数类型可以使用。
*/

    @Before(value = "execution(public void com.cjj.ba01.SomeService.dosome(String,Integer))")
    public void myBefore(){
        System.out.println("前置通知,切面功能:在目标方法执行之前输出时间:"+new Date());
    }

}

5.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者xm]配置文件

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--把对象交给spring容器,由spring容器统一创建,管理对象-->

    <bean id="SomeServiceImpl" class="com.cjj.ba01.SomeServiceImpl" />

    <bean id="MyAspect" class="com.cjj.ba01.MyAspect"/>
    
	<!--声明自动代理生成器:使用aspectj框架内部的功能, 创建目标对象的代理对象。
创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象
所以目标对象就是被修改后的代理对象-->
    <aop:aspectj-autoproxy />

</beans>

6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的 功能增强。

package com.cjj;

import com.cjj.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {

    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        SomeService someService = (SomeService) context.getBean("SomeServiceImpl");
        someService.dosome("张三",18);
    }

}

4.1.3 JoinPoint(获取要切入的方法执行的信息)

指定通知方法中的参数: JoinPoint
JoinPoint:业务方法,要加入切面功能的业务方法
作用是:可以在通知方法中获取方法执行时的信息,例如方法名称 ,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数

使用JoinPoint

在切面类中使用

@Before(value = "execution(public void com.cjj.ba01.SomeService.dosome(String,Integer))")
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);
    }
    //切面要执行的功能
    System.out.println("前置通知,切面功能:在目标方法执行之前输出时间:"+new Date());
}

image-20210505144347301

4.1.4 @AfterReturning

@AfterReturning:后置通知
属性:

​ 1.value切入点表达式

​ 2.returning自定义的变量,表示目标方法的返回值的。
​ 自定义变量名必须和通知方法的形参名一样。
​ 位置:在方法定义的上面
​ 特点:
​ 1.在目标方法之后执行的。
​ 2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能

​ 3.可以修改这个返回值

代码实现

目标方法

@Override
public Object doOther(String name, Integer age) {
    System.out.println("===========执行了doOther()方法==========");
    return "abcd";
}

通知方法

@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
public void myAfterReturning(Object res){
    System.out.println("后置通知,在目标方法执行之后,获得返回值是:"+res);
}

4.1.5 环绕通知 @Around

环绕通知方法的定义格式

1.public
2.必须有一个返回值,推荐使用object
3.方法名称自定义
4.方法有参数,固定的参数ProceedingJoinPoint

@Around:环绕通知
属性:value切入点表达式
位置:在方法的定义什么
特点:
1.它是功能最强的通知
2.在目标方法的前和后都能增强功能。
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果。 影响最后的调用结果

环绕通知,等同于jdk动态代理的, InvocationHandler接口
参数:
ProceedingJoinPoint就等同于Method
作用:执行目标方法的
返回值:就是目标方法的执行结果,可以被修改。

代码实现

目标方法

@Override
public String doFirst(String name, Integer age) {
    System.out.println("===========执行了doFirst()方法==============");
    return "doFirst";
}

通知方法

@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
    //前置通知
    System.out.println("环绕通知,在目标方法执行之前,输出时间"+new Date());
    //目标方法调用
    Object proceed = pjp.proceed();
    //后置通知
    System.out.println("环绕通知,在目标方法执行之后,提交事务");
    //返回目标方法的执行结果
    return proceed;
}

测试

@Test
public void test3(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    SomeService someService = (SomeService) context.getBean("SomeServiceImpl");
    someService.doFirst("张三",18);
    System.out.println(someService.getClass().getName());
}

运行结果

image-20210505163953696

4.1.6 异常通知@AfterThrowing

@AfterThrowing:异常通知
属性:1. value切入点表达式

  1. throwinng 自定义的变量,表示目标方法抛出的异常对象。
    变量名必须和方法的参数名一样
    特点:
    1.在目标方法抛出异常时执行的
  2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。
    如果有异常,可以发送邮件,短信进 行通知

4.1.7 最终通知@After

@After : 最终通知
属性: value切入点表达式
位置:在方法的上面
特点:
1.总是会执行
2.在目标方法之后执行的

4.1.8 @Pointcut

@Pointcut:定义和管理切入点,如果 你的项目中有多个切入点表达式是重复的,可以复用的。
可以使用@Pointcut
属性:value切入点表达式
位置:在自定义的方法,上面
特点:
当使用Pointcut定义在一个方法的上面,此时这 个方法的名称就是切入点表达式的别名。
其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了。

5.spring整合mybatis

步骤:
1.新建maven项目

2.加入maven的依赖
1 ) spring依赖
2 ) mybatis依赖
3 ) mysql驱动
4 ) spring的事务的依赖
5 ) mybatis和spring集成的依赖: mybatis官方体用的 ,用来在spring项目中创建mybatis
的SqlSesissonFactory,dao对象的

<dependencies>
    <!--junit依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <!-- spring依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
    </dependency>
    <!--spring事务-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--mybatis和spring集成依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
    </dependency>
</dependencies>

3.创建实体类

package com.cjj.pojo;

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

    public Student() {
    }

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

    public int getId() {
        return id;
    }

    public void setId(int 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 int getAge() {
        return age;
    }

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

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

4.创建dao接口和mapper文件

dao接口

package com.cjj.dao;

import com.cjj.pojo.Student;

import java.util.List;

public interface StudentDao {
    List<Student> getStudentList();
}

mapper文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cjj.dao.StudentDao">

    <select id="getStudentList" resultType="com.cjj.pojo.Student">
        select * from mybatis.student
    </select>

</mapper>

5.创建mybatis主配置文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--settings:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    <typeAliases>
        <package name="com.cjj.pojo" />
    </typeAliases>

    <mappers>
        <mapper resource="com/cjj/dao/StudentDao.xml"/>
    </mappers>
</configuration>

6.创建Service接口和实现类,属性是dao。

Service接口

package com.cjj.service;

import com.cjj.pojo.Student;

import java.util.List;

public interface StudentService {
    List<Student> getStudentList();
}

Service实现类

package com.cjj.service;

import com.cjj.dao.StudentDao;
import com.cjj.pojo.Student;

import java.util.List;

public class StudentServiceImpl implements StudentService{
    private StudentDao studentDao;
    //使用set注入,赋值
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    @Override
    public List<Student> getStudentList() {
        List<Student> studentList = studentDao.getStudentList();
        return studentList;
    }
}

7.创建spring的配置文件:声明mybatis的对象交给spring创建
1)数据源

​ 2 ) sqlSessionFactory
​ 3) Dao对象
​ 4 )声明自定义的service

<?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">

    <!--声明DataSource,作用是连接数据库-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入,给DruidDataSource提供连接数据库信息-->
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
        <property name="username" value="root"/>
        <property name="password" value="cjj17879392776"/>
        <property name="maxActive" value="20"/>
    </bean>

    <!--声明mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="dataSource"/>
        <!--mybatis主配置文件的位置
            configLocation属性是Resource类型,读取配置文件
            它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--
        创建dao对象,使用SqlSession的getMapper(studentDao.class)
        MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定包名,包名是dao接口所在的包名。
            MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次
            getMapper()方法,得到每个接口的dao对象。
            创建好的dao对象放入到spring的容器中。dao对象的默认名称是 接口名首字母小写
        -->
        <property name="basePackage" value="com.cjj.dao"/>
    </bean>
	
    <!--在bean中创建Service实现类对象,给studentDao属性赋值-->
    <bean id="StudentService" class="com.cjj.service.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>

8.创建测试类, 获取Service对象,通过service调用dao完成数据库的访问

@Test
public void getStudentList2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    StudentService studentService = (StudentService) context.getBean("StudentService");
    List<Student> studentList = studentService.getStudentList();
    for (Student student : studentList) {
        System.out.println(student);
    }
}

9.使用属性配置文件jdbc.properties

6.Spring事务

6.1 概念

1.什么是事务
讲mysql的时候,提出了事务。事务 是指一组sql 语句的集合,集合中有多条sql语句
可能是insert,
update,select,delete,我们希望这些多个sql语句都能成功,
或者都失败,这 些sql语句的执行是一致的, 作为一个整体执行。

2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert, update, delete. 需要保证
这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。
在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句

3.通常使用JDBC访问数据库,还是mybatis访问数据库怎么处理事务
jdbc访 问数据库,处理事务Connection conn ; conn . comnit() ; conn. rollback() ;
mybatis访问数据库,处理事务,SqlSession. comnit() ; SqlSession. rollback() ;
hibernate访问数据库,处理事务,Session. commnit() ; Session. rollback() ;

4.3问题中事务的处理方式,有什么不足
1)不同的数据库访问技术,处理事务的对象,方法不同,
需要了解不同数据库访问技术使用事务的原理
2)掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
3)处理事务的多种方法。
总结:就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

5.怎么解决不足
spring提供一种处理事务的统一模型,能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

6.处理事务,需要怎么做,做什么.
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了
1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit, rollback
事务管理器是一个接口和他的众多实现类。
接口: PlatformTransactionManager ,定义了事务重要方法comrnit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库---spring创建好的是DataSourceTransactionManager
hibernate访问数据库---- spring创建的是HibernateTransactionManager

怎么使用:你需要告诉spring你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类,在spring的配置文件中使用声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中

2)你的业务方法需要什么样的事务,说明需要事务的类型。

说明方法需要的事务:
1)事务的隔离级别:有4个值。
DEFAULT:采用DB默认的事务隔离级别。MySql的默认为REPEATABLE READ: Oracle默认 为READ COMMITTED 。
READ_ UNCOMMITTED: 读未提交。未解决任何并发问题。
READ_ COMMITTED: 读已提交。解决脏读,存在不可重复读与幻读。
REPEATABLE_ READ: 可重复读。解决脏读、不可重复读,存在幻读
SERIALIZABLE: 串行化。不存在并发问题。
2)事务的超时时间:表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒,整数 值,
默认是 -1.

3)事务的传播行为:控制业务方法是不是有事务的,是什么祥的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。
PROPAGATION_ REQUIRED
PROPAGATION_ REQUIRES_ NEW
PROPAGATION_ SUPPORTS
以上三个需要掌握的
PROPAGATION_ MANDATORY
PROPAGATION_ NESTED
PROPAGATION_ NEVER
PROPAGATION_ NOT_ SUPPORTED

4)事务提交事务,回滚事务的时机
1)当你的业务方法,执行成功,没有异常抛出,当彷法执行完毕,spring在方 法执行后提交事务。事务管理器commit
2)当你的业务方法抛出运行时异常或ERROR,spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的 子类都是运行时异常,例 如NullPointException,NumberFormatException
3)当你的业务方法抛出非运行时异常,主要 是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, sQLException

总结spring的事务
1.管理事务的是事务管理和他的实现类

2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时

你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

6.2代码实现(使用注解)

1.在application中配置事务管理器

<!--使用spring的事务处理-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接数据库,指定数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--开始事务注解驱动,告诉spring使用注解管理事务,创建代理对象
transaction-manager:事务管理器对象的id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>

2.在需要使用事务的业务service方法上加上@Transactional注解

@Transactional
@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方法结束========");

}

6.3 使用xml(aspectj)配置事务

实现步骤:
都是在:xmn1配置文件中实现。
1)要使用的是aspectj框架,需要加入依赖

<dependency>
<groupId>org . springframework</groupId>
<arti factId>spring-aspects</arti factId>
<version>5.2. 5. RELEASE</version>
</ dependency>

2)声明事务管理器对象

<!--使用spring的事xml务处理-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接数据库,指定数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>

3)声明方法需要的事务类型(配置方法的事务属性[隔离级别,传播行为,超时])

<!--声明业务方法的事务属性(隔离级别,传播行为,超时时间)
    id:自定义名称
    transaction-manager:事务管理器对象的id(指定事务管理器)
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!--
        tx:method:给具体的方法配置事务属性,可以有多个,给不同的方法设置事务属性
        name:方法名称,1)完整的方法名称,不带有包和类。
                     2)方法可以使用通配符,*代表任意字符
        propagation:传播行为,枚举类
        isolation:隔离级别
        rollback-for:你指定的异常类名,全限定类名。发生异常一定回滚。
    -->
    <tx:attributes>
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                   rollback-for="java.lang.NullPointerException,com.cjj.excep.NotEnoughException"/>

        <!--使用通配符,一次指定很多方法-->
        <!--添加-->
        <tx:method name="add*" propagation="REQUIRED" />
        <!--查询-->
        <tx:method name="select*" propagation="REQUIRED"/>
        <!--修改-->
        <tx:method name="update*" propagation="REQUIRED"/>
        <!--删除-->
        <tx:method name="delete*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

4)配置aop: 指定哪类要创建事务。

<!--配置aop-->
<aop:config>
    <!--配置切入点表达式,指定哪些包中的类,要使用事务
        id:切入点表达式的名称,唯一值。
        expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象。
    -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
    <!--配置增强器:关联advice和pointcut
        advice-ref:通知,上面tx:advice那里的配置
        pointcut-ref:切入点表达式的id
    -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
posted @ 2022-04-06 20:42  路人假helloWorld  阅读(44)  评论(0编辑  收藏  举报