spring 搭建项目环境

  • 资源文件夹没有的给他加上
  • image.png
  • 项目 jdk1.8
  • file encoding utf-8
  • 不用的文件可以删掉

image.png
1.添加spring依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>5.2.5.RELEASE</version>
  5. </dependency>

2.资源路径(在spring中可以不用?但是为了养成习惯还是加上)

<resources>
     <resource>
       <directory>src/mian/java</directory>
       <includes>
         <include>**/*.xml</include>
         <include>**/*.properties</include>
       </includes>
     </resource>

     <resource>
       <directory>src/mian/resources</directory>
       <includes>
         <include>**/*.xml</include>
         <include>**/*.properties</include>
       </includes>
     </resource>
   </resources>

基于xml文件的IOC

通过容器创建第一个对象并取出

applicationContext.xml


<!--    创建学生对象
            等同于 Student student = new Student();
            id:创建对象的名称
            class:创建对象的类型(全类名) 底层通过 反射构建对象
            启动容器的同时创建对象-->
    <bean id="student" class="com.bjpowernode.entity.Student"></bean>

Mytest

  @Test
    //通过容器创建对象
    public void student02(){
//     先创建对象 再取出对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//     取出对象
        Student studnet =(Student) context.getBean("student");
    }
//       打印对象信息
        System.out.println(studnet);

使用setter注入

setter 简单类型注入

  • 注分为简单类型注入(八大基本数据类型 和String) 以及引用类型注入
  • 简单类型注入使用value 属性
  • 引用类型注入使用ref属性
  • 注意:使用setter注入 必须要在类中给出setter 方法 和无参构造方法不然要报错的

1.xml文件

    <bean id="student" class="com.bjpowernode.entity.Student">
        <property name="name" value="张三"/>
<!--        就算是数值类型也是要 用“ ” 这是xml的格式-->
        <property name="age" value="18"/>

    </bean>

2.MyTest

  @Test
    //通过容器创建对象
    public void student02(){
//     先创建对象 再取出对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//     取出对象
        Student studnet =(Student) context.getBean("student");
//       打印对象信息
        System.out.println(studnet);//学生{name='张三', age=18}
    }

setter 引用类型注入

image.png
image.png
1.school
2.studnet

public class Student {
    private String name;
    private int age;
    private School school;

    public Student() {
        System.out.println("student 学生类的无参构造。。。");
    }

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

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

3.application.xml

          启动容器的同时创建对象-->
    <bean id="school" class="com.bjpowernode.entity01.School">
        <property name="name" value="重庆工商大学"/>
<!--        就算是数值类型也是要 用“ ” 这是xml的格式-->
        <property name="adress" value="重庆市南岸区"/>
    </bean>

<!--    创建学生加入 学校属性
       xml文件是一个bean工厂 对象加入引用类型要 用ref 并且是之前已经创建过得对象
        在xml  文件当中不分先后顺序-->
    <bean id="student" class="com.bjpowernode.entity01.Student">
        <property name="name" value="李四"></property>
        <property name="age" value="21"></property>
        <property name="school" ref="school"></property>
    </bean>

4.MyTest


@Test
    public void student (){
//        这里要注意 xml文件的路径
        ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
        Student student = (Student) ac.getBean("student");
        System.out.println(student);

    }

使用构造方法注入

使用构造方法的参数名进行注入

1.applicationContext.xml


<!--使用构造方法参数 参数名称注入值-->
    <bean id="school" class="com.bjpowernode.entity02.School">

<!--        这里的name 是构造方法中Strin name 的参数名 而不是 school 的属性名-->
        <constructor-arg name="name" value="清华大学"></constructor-arg>
        <constructor-arg name="adress" value="海淀区"></constructor-arg>
    </bean>

image.png

通过构造方法参数的下标index 进行注入

<!--创建student 对象,用 构造方法参数下标-->
    <bean id="student" class="com.bjpowernode.entity02.Student">
        <constructor-arg index="0" value="菜菜"></constructor-arg>
        <constructor-arg index="1" value="22"></constructor-arg>
        <constructor-arg index="2" ref="school"></constructor-arg>
    </bean>

通过构造方法参数的默认属性进行注入

<!--    创建student 对象 用构造方法属性的默认顺序(顺序不可变)-->
    <bean id="studentOrder" class="com.bjpowernode.entity02.Student">
        <constructor-arg value="大菜"></constructor-arg>
        <constructor-arg value="18"></constructor-arg>
        <constructor-arg ref="school"></constructor-arg>
    </bean>
  • 构造方法
  • image.png

    三层架构(案例)

    使用传统的三层架构

    spring02

    引入 spring 容器

    spring02-xml

    基于注解的IOC(也叫DI)

    使用@Component 注解创建对象

    image.png
    1.applicationContext。xml
    <!--    @component只是 有一个标识可以注解生成对象 ,要实际生成还要包扫描!!!-->
      <context:component-scan base-package="com.bjpowernode.s01"></context:component-scan>
    
    2.studnent类 ```java

//注解生成对象默认是类名,可以修改对象名@Component(“修改的对象名”) @Component public class Student { private String name; private int age;

public Student() {
    System.out.println("studenr的 无参构造方法");
}

@Override
public String toString() {
    return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
3.test类
```java
@Test
    public void Test (){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
    }

简单类型注入

image.png
image.png

引用类型注入

image.png

引用类型按类型注入

school类

@Component
public class School {
    @Value("清华大学")
    private String name ;
    @Value("海淀区")
    private  String age;

    public School() {
        System.out.println("school 的无参构造");

    }

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

student

@Component
public class Student {
    @Value("李四")
    private String name;
    @Value("18")
    private  String age;
//    引用类型用autoweird

    @Autowired
    private School school;

    public Student() {
        System.out.println("student的无参构造方法");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", school=" + school +
                '}';
    }
}
  • 在配置文件中 依旧包扫描

    引用类型按名称注入

    school.java ```java @Component(“qinghua”)//按名称注入,这里的名字跟后面的 @Qualifier(“qinghua”)一样 public class School { @Value(“清华大学”) private String name ; @Value(“海淀区”) private String age;

    public School() {

      System.out.println("school 的无参构造");
    

    }

    @Override public String toString() {

      return "School{" +
              "name='" + name + '\'' +
              ", age='" + age + '\'' +
              '}';
    

    } }

student.java
```java
@Component("lisi")
public class Student {
    @Value("李四")
    private String name;
    @Value("18")
    private  String age;
//    引用类型用autoweird

    @Autowired //这个autowired不能少 不然不能注入引用类型school的值
    @Qualifier("qinghua")
    private School school;

    public Student() {
        System.out.println("student的无参构造方法");
    }

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

父子关系下 按名称注入和按类型注入

1.当存在父子关系的情况下,使用类型注入 那意味着有多个可注入的对象,此时会按名称进行儿子筛选(最终选择与被注入对象属性名一样的对象,例如school)
image.png
image.png

2.当存在父子关系的时候,最好用@Qualifier 直接用名称进行注入

  • 父类

image.png

  • 子类

image.png

  • 被注入对象

image.png

  • 结果
  • image.png
  • 注:无论注入父类还是子类 子类的无参构造从都是会调用的

    基于注解的三层项目改造

    java代码 ```java @Repository public class UserMapperImpl implements UsersMapper {
@Override
public int addUsers(Users users) {
    System.out.println(users.getUname()+"创建了");
    return 1;
}

}

@Service public class UsersServiceImpl implements UsersService { //在业务层里面调用 数据访问层一定要创建对象 @Autowired private UsersMapper usersMapper = new UserMapperImpl();

@Override
public int addUsers(Users users) {
    return usersMapper.addUsers(users);
}

}

@Controller public class UsersController {

// 在控制层里面处理业务 要创建 业务对象 @Autowired public UsersService usersService = new UsersServiceImpl(); public int addUsers(Users users){ return usersService.addUsers(users); } }

<a name="f8V3W"></a>
# 包扫描的方式
  1)单个包扫描(推荐使用)<br />    <context:component-scan base-package="com.bjpowernode.controller"></context:component-scan><br />    <context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan><br />    <context:component-scan base-package="com.bjpowernode.dao"></context:component-scan>

  2)多个包扫描,多个包之间以逗号或空格或分号分隔<br />    <context:component-scan base-package="com.bjpowernode.controller com.bjpowernode.service ,com.bjpowernode.dao"></context:component-scan>

  3)扫描根包(不推荐)<br />    <context:component-scan base-package="com.bjpowernode"></context:component-scan><br />    会降低容器启动的速度,导致多做无用功.

<a name="ik291"></a>
# 为应用指定多个spring配置文件
  1)拆分配置文件的策略<br />    A.按层拆<br />      applicationContext_controller.xml        <br />        <bean id="uController" class="com.bjpowernode.controller.UsersController"><br />        <bean id="bController" class="com.bjpowernode.controller.BookController"><br />      applicationContext_service.xml        <br />        <bean id="uService" class="com.bjpowernode.controller.UsersService"><br />        <bean id="bService" class="com.bjpowernode.controller.BookService"><br />      applicationContext_mapper.xml        <br />        <bean id="uMapper" class="com.bjpowernode.controller.UsersMapper"><br />        <bean id="bMapper" class="com.bjpowernode.controller.BookMapper">

    B.按功能拆<br />      applicationContext_users.xml        <br />        <bean id="uController" class="com.bjpowernode.controller.UsersController"><br />        <bean id="uService" class="com.bjpowernode.controller.UsersService"><br />        <bean id="uMapper" class="com.bjpowernode.controller.UsersMapper"><br />      applicationContext_book.xml      <br />        <bean id="bController" class="com.bjpowernode.controller.BookController"><br />        <bean id="bService" class="com.bjpowernode.controller.BookService"><br />        <bean id="bMapper" class="com.bjpowernode.controller.BookMapper">
<a name="i0lDn"></a>
# 基于xml文件下的配置文件拆分
1.整合文件 名 一定要区别与其他文件名<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25708362/1649753649032-f87f14e8-61c4-4cfc-bda8-336f681522ad.png#clientId=u3aab11fd-1358-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=286&id=uf970cf78&margin=%5Bobject%20Object%5D&name=image.png&originHeight=357&originWidth=494&originalType=binary&ratio=1&rotation=0&showTitle=false&size=29919&status=done&style=none&taskId=ud98e1ea4-6a45-4514-bf6c-5654303c476&title=&width=395.2)<br />2.报红影响 一样可以运行<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25708362/1649753698741-0445414b-b110-4b42-818a-1ecd5c28ec96.png#clientId=u3aab11fd-1358-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=207&id=uc28e2a79&margin=%5Bobject%20Object%5D&name=image.png&originHeight=259&originWidth=1147&originalType=binary&ratio=1&rotation=0&showTitle=false&size=38615&status=done&style=none&taskId=ufd352813-0b02-491a-89f7-5ec68239925&title=&width=917.6)

3.整合文件(单个)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/25708362/1649753906980-42ae3583-bb58-4954-8729-871bc3e47254.png#clientId=u3aab11fd-1358-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=271&id=u731d60d8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=339&originWidth=1470&originalType=binary&ratio=1&rotation=0&showTitle=false&size=66902&status=done&style=none&taskId=u63fa54b9-c068-468b-a1a9-3876a22a0d5&title=&width=1176)
```xml
   <import resource="applicatoinContext_mapper.xml"></import>
    <import resource="applicatoinContext_service.xml"></import>
    <import resource="applicatoinContext_controller.xml"></import>

4.整合文件批量导入

 <import resource="applicatoinContext_*.xml"></import>

基于xml文件引用类型的自动注入

applicationContext.xml

    <bean id="student" class="com.bjpowernode.entity01.Student" AutoWeird="byType">
        <property name="name" value="李四"></property>
        <property name="age" value="21"></property>
       // <property name="school" ref="school"></property>
    </bean>

基于注解的配置文件拆分

image.png
image.png

面向切面编程AOP

切面:公共的、通用的、重复的功能成为切面,面向切面就是把切面提取出来单独开发,在调用方法中通过动态代理的方式进行植入。
image.png

1.静态代理

image.png
image.png

  • Agent.java ```java public class Agent extends BookServiceImpl{ //设计成员变量的类型为接口,可以灵活切换对象目标 public Service target; //用构造方法传入目标对象 public Agent(Service target) {

      this.target = target;
    

    }

    @Override public void buy(){

      try {
    

    //切面功能

          System.out.println("事务开启。。。");
    

    // 主业务

          target.buy();
    

    // 事务关闭

          System.out.println("事务关闭。。。");
      } catch (Exception exception) {
          System.out.println("事务回滚。。。");
      }
    

    } }


- test.java

2.![image.png](https://cdn.nlark.com/yuque/0/2022/png/25708362/1649815977277-10824d40-b4de-4925-a917-a0a07a1ffd9c.png#clientId=u6347398b-2a1c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=655&id=ubed7aac5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=819&originWidth=1878&originalType=binary&ratio=1&rotation=0&showTitle=false&size=538610&status=done&style=none&taskId=udde665cb-e4d3-4f9d-9ddb-821233d6a7e&title=&width=1502.4)
<a name="aRvHm"></a>
## 多个切面
```java
    @Test
    public void test01(){
        Service service = new Agent(new BookServiceImpl(),new TransAop());
        Service service1 = new Agent(service,new LogAop());
        service1.buy();

image.png

aop动态代理

public class ProxyFactory {
    public static Object getAgent(Service target,AOP aop){
        return Proxy.newProxyInstance(
                //        类加载
                target.getClass().getClassLoader(),
//                目标对象实现的所有接口
                target.getClass().getInterfaces(),
//                代理功能实现
                new InvocationHandler() {
                    @Override
                    public Object invoke(
        //                        生成的代理对象
                            Object proxy,
                    //正在被调用的目标方法
                            Method method,
                     //目标参数的方法
                            Object[] args) throws Throwable {
                        Object obj=null;
//                  切面
                        try {
                            aop.before();
//                        业务
                           obj=   method.invoke(target,args);
//                        切面
                            aop.after();
                        } catch (Exception exception) {
                            aop.excepton();
                        }
//                        切面

                    return obj;


                    }
                }
        );


    }

}

AOP 常用术语

image.png
image.png

切入点表达式

1.规范的公式:
execution(访问权限 方法的返回值 方法声明(参数) 异常参数)
简化之后的公式:
execution (方法返回值 方法声明(参数))

用到的符号:
* 代表任意个的字符(通配符)
.. 如果在方法的参数中,表示任意参数
如果出现在路径中,代表本路径极其子路径

前置通知实现

切面方法实现前置通知

1.SomeService接口

public interface SomeService {
    String doSome(String name ,int age);
}

2.SomeService接口

//业务实现类
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String name, int age) {
        System.out.println("dosome的业务实现类。。。");
        return "abcd";
    }
}

3.test类

@Aspect //交给aspectj框架去识别切面类
public class MyAspect {
    /**
     * 所有切面的功能都是切面方法实现的
     * 可以将各种切面都在此类中进行开发
     *
     *
     * 前置通知的切面方法的规范
     * 1.访问权限是public
     * 2.方法的返回值是void
     * 3.方法名称自定义
     * 4.方法没有参数,如果有也只能是joinPoint类型
     * 5.必须使用@Before注解来声明切入的时机是前切入功能和切入点
     * 参数value  指定切入点表达式
     *
     * 业务方法
     *  String doSome(String name ,int age);
     */
    @Before(value = "execution(public String com.bjpowernode.s01.SomeServiceImpl.doSome(String,int))")
    public  void MyBefore(){
        System.out.println("切面方法中的前置通知功能实现。。。");

    }
}

注解方法实现前置通知

1.application.xml


<!--    用注解首先进行包扫描-->
    <context:component-scan base-package="com.bjpowernode.s01"></context:component-scan>

2.mytest

    @Test
    public void test01(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someServiceImpl");
        System.out.println(someService.getClass());
        someService.show();
    }

前置方法参数joinPoint(他是一个接口有实现类)

    @Before(value = "execution( * com.bjpowernode.s01.*.*(..))")
    public  void MyBefore(JoinPoint joinPoint){

        System.out.println("切面方法中的前置通知功能实现。。。");
//        获取参数
        System.out.println(Arrays.toString(joinPoint.getArgs()));
//        获取标签
        System.out.println(joinPoint.getSignature());

    }

切入点表达式的各种用法

1.someService.jva

public interface SomeService {
    String doSome(String name ,int age);
    void show();
}

2.myaspects

    @Before(value = "execution( * com.bjpowernode.s01.*.*(..))")
    public  void MyBefore(){
        System.out.println("切面方法中的前置通知功能实现。。。");

    }

image.png
3.myTest.java

public class MyTest {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        SomeService someService = (SomeService) ac.getBean("someService");
        someService.show();

    }
}

JDK动态代理与CGLib动态代理切换

1.jdk动态代理

public class MyTest {
    @Test
    public void test(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        SomeService someService = (SomeService) ac.getBean("someService");
        someService.show();

    }
}
//这里转换的类型是接口对象 而不是接口实现类的对象如果用实现类的对象来接会报错

image.png
application.xml

<!--创建业务对象-->
    <bean id="someService" class="com.bjpowernode.s01.SomeServiceImpl"></bean>
<!--    创建切面对象-->
    <bean id="myaspect" class="com.bjpowernode.s01.MyAspect"></bean>
<!--    绑定-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
//用接口来接

3.控制台
image.png
2.CGLib动态代理模式
xml文件

    <!--    CGlib动态代理-->
    <!--创建业务对象-->
    <bean id="someService" class="com.bjpowernode.s01.SomeServiceImpl"></bean>
    <!--    创建切面对象-->
    <bean id="myaspect" class="com.bjpowernode.s01.MyAspect"></bean>
    <!--    绑定-->
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
//可以用接口实现类来接 也可以用接口来接

MyTest.java

    @Test
    public void test01(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someService");
        System.out.println(someService.getClass());
        someService.show();
    }
}

image.png

后置通知实现

基本功能

1.myaspect.java

@Aspect
@Component
public class Myaspect {
    /**1.访问权限是public
     * 2.返回值是void
     * 3.方法有没有参数取决于目标方法有没有返回值
     * 4.使用@AfterReturning 注解表示后置通知
     * 参数:
     *      value:指定切入点表达式
     *      returning 指定目标方法的返回值 名称与切面方法的参数名称一致

     */
    @AfterReturning(value = "execution(* com.bjpowernode.s02.*.*(..))",returning = "obj")
    public void myAfterReturning (Object obj){
        System.out.println("后置通知功能实现。。。。");
        if (obj!=null){
            if (obj instanceof String ){
                obj = obj.toString().toUpperCase();
                System.out.println("将返回值改为大写:"+obj);
            }
            if (obj instanceof Student){
                Student student = (Student) obj;
                student.setName("李四");

                System.out.println("学生的姓名是:"+student);
            }
        }
    }
}

后置通知中不能改变返回值(简单类型)以及可以改变简单类型的情况(引用类型)

1.serviceimpl

@Service
public class SomeServiceImpl implements SomeService {

    @Override
    public String dosome(String name, int age) {
        System.out.println("切面方法实施了。。。。。");
        return "abcd";
    }

    @Override
    public Student change() {
        return new Student("张三");
    }
}

2.myaspect


@Aspect
@Component
public class Myaspect {
    /**1.访问权限是public
     * 2.返回值是void
     * 3.方法有没有参数取决于目标方法有没有返回值
     * 4.使用@AfterReturning 注解表示后置通知
     * 参数:
     *      value:指定切入点表达式
     *      returning 指定目标方法的返回值 名称与切面方法的参数名称一致

     */
    @AfterReturning(value = "execution(* com.bjpowernode.s02.*.*(..))",returning = "obj")
    public void myAfterReturning (Object obj){
        System.out.println("后置通知功能实现。。。。");
        if (obj!=null){
            if (obj instanceof String ){
                obj = obj.toString().toUpperCase();
                System.out.println("将返回值改为大写:"+obj);
            }
            if (obj instanceof Student){
                Student student = (Student) obj;
                student.setName("李四");

                System.out.println("学生的姓名是:"+student);
            }
        }
    }
}

3.Test

public class MyTest02 {
    @Test
    public void myTest(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
        SomeService someServiceImpl = (SomeService) ac.getBean("someServiceImpl");
        String s = someServiceImpl.dosome("张三", 22);
        System.out.println(s);

    }
    @Test
    public void myTest01(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
        SomeService someServiceImpl = (SomeService) ac.getBean("someServiceImpl");
        Student s = someServiceImpl.change();
        System.out.println(s);

    }
}

环绕通知实现

通知实现

myaround

@Aspect
@Component
public class MyAround {
    /**
     * 环绕通知方法的规范
     * 1.访问权限是public
     * 2.切面方法有返回值,此返回值就是目标方法的返回值
     * 3.方法名称自定义
     * 4.方法有参数,参数就是目标方法
     * 5.回避异常Throwable
     * 6.用@Around注解什么是环绕通知 value指定切入点表达式
     *
     *
     */
    @Around(value = "execution(* com.bjpowernode.s03.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp)throws Throwable{
//    前切功能实现
    System.out.println("环绕通知中的前置功能实现。。。。");
//    目标方法调用
    Object obj = pjp.proceed(pjp.getArgs());
//    后切功能实现
    System.out.println("环绕通知中的后置功能实现。。。");

//    改变目标方法的返回值
    return obj.toString().toUpperCase();
}

}

最终通知

  • 无论功能是否正常执行,最终通知都会 执行 ```java @Aspect @Component public class MyAfter { /**
    • 最终通知方法的规范
    • 1.访问权限是public
    • 2.方法没有返回值
    • 3.方法名称自定义
    • 4.方法没有参数,有也只能是joinpoint
    • 参数:value(切入点) / @After(value = “execution( com.bjpowernode.s04..(..))”) public void myAfter(){ System.out.println(“最终通知功能。。。”); } }
![image.png](https://cdn.nlark.com/yuque/0/2022/png/25708362/1649929562646-798b9840-e7aa-457b-bc9f-97a919cf5683.png#clientId=u1f0c1da9-a44e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=399&id=u104f65ab&margin=%5Bobject%20Object%5D&name=image.png&originHeight=499&originWidth=1274&originalType=binary&ratio=1&rotation=0&showTitle=false&size=110140&status=done&style=none&taskId=u23e3939a-0eaa-4f65-a35b-74929a944be&title=&width=1019.2)
<a name="ZEXQo"></a>
## 为一个方法添加多重切面(通知)
aspect
```java
    @After(value = "execution(* com.bjpowernode.s04.*.*(..))")
    public void myAfter(){
        System.out.println("最终通知功能。。。");
    }
    @Before(value = "execution(* com.bjpowernode.s04.*.*(..))")
    public void myBefore(){
        System.out.println("前置通知功能通知功能。。。");
    }

    @AfterReturning(value = "execution(* com.bjpowernode.s04.*.*(..))",returning = "obj")
    public void myAfterReturning(Object obj){
        System.out.println("后置通知功能通知功能。。。");
    }

    @Around(value = "execution(* com.bjpowernode.s04.*.*(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知中的前置通知功能通知功能。。。");
        Object obj = pjp.proceed(pjp.getArgs());
        System.out.println("环绕通知中的后置通知的功能。。。");
        return obj;
    }
}

image.png

切入点表达式起别名(@PointCut)

image.png

事务

添加注解事务效果掩饰

service.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"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--导入mapper文件,可以直接获取service.xml-->
    <import resource="applicationContext_mapper.xml"></import>
    <context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
<!--    事务处理-->
<!--    1.添加事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        事务必须关联数据库处理,所以要配置数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--    2.添加事务的驱动-->
    <tx:annotation-driven transaction-manager="transactionManager" ></tx:annotation-driven>
</beans>

serviceimpl.java


/**
 * @author may
 * @date 2022/4/15 13:10
 */
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class AccountsServiceImpl implements AccountsService {
//    数据访问层对象
    @Autowired
AccountsMapper accountMapper;
    @Override
    public int save(Accounts accounts) {
        int num =0;
        num = accountMapper.save(accounts);
        System.out.println("增加账户成功!"+num);
//        手动抛出异常
        System.out.println(1/0);
        return num;
    }
}
  • 添加事务,要是有异常就算数据已经在操作 也不会添加到数据库中

image.png

添加不回滚属性设置

1,在后面添加norollbackforClassname 不然就是norollbackfor=ArithmeticException.classimage.png
2.forname 代表异常的名字如果有多个异常可以用{“”,“”}
image.png
3.这样就算有异常依然能够将操作的数据添加到数据库
image.png
image.png

@Transactional 常用参数

@Transactional(propagation = Propagation.REQUIRED,//事务的传播特性
noRollbackForClassName = “ArithmeticException”, //指定发生什么异常不回滚,使用的是异常的名称
noRollbackFor = ArithmeticException.class,//指定发生什么异常不回滚,使用的是异常的类型
rollbackForClassName = “”,//指定发生什么异常必须回滚
rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚
timeout = -1, //连接超时设置,默认值是-1,表示永不超时
readOnly = false, //默认是false,如果是查询操作,必须设置为true.
isolation = Isolation.DEFAULT//使用数据库自已的隔离级别
)

spring事务的两种处理方式

1)注解式的事务
使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定.此注解可添加到方法上,只是对此方法执行事务的处理.

2)声明式事务(必须掌握),在配置文件中添加一次,整个项目遵循事务的设定.

事务的管理级别

1).未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2).提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
3).可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
4).串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
5).使用数据库默认的隔离级别isolation = Isolation.DEFAULT
MySQL:mysql默认的事务处理级别是’REPEATABLE-READ’,也就是可重复读
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别是READ COMMITTED,也就是读已提交

为什么添加事务管理器

JDBC: Connection con.commit(); con.rollback();
MyBatis: SqlSession sqlSession.commit(); sqlSession.rollback();
Hibernate: Session session.commit(); session.rollback();

事务管理器用来生成相应技术的连接+执行语句的对象.
如果使用MyBatis框架,必须使用DataSourceTransactionManager类完成处理



项目中的所有事务,必须添加到业务逻辑层上.

spring 事务传播的特性

多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决.
1. 常用
PROPAGATION_REQUIRED:必被包含事务(增删改必用)
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
2. 不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务

事务传播 特性解析

image.png
Spring笔记.txt