Spring笔记

spring框架

第一章

1.1简介

Spring是一个开源框架,它由[Rod Johnson](https://baike.baidu.com/item/Rod Johnson)创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

1.2spring的依赖

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.3.3version>
dependency>

1.3spring的优点

◆JAVA EE应该更加容易使用。

◆面向对象的设计比任何实现技术(比如JAVA EE)都重要。

◆面向接口编程,而不是针对类编程。Spring将使用接口的复杂度降低到零。

◆代码应该易于测试。Spring框架会帮助你,使代码的测试更加简单。

◆JavaBean提供了应用程序配置的最好方法。

◆在Java中,已检查异常(Checked exception)被过度使用。框架不应该迫使你捕获不能恢复的异常。

(1)spring是一个免费开源的框架

(2)spring是一个轻量级,非入侵式的框架

(3)控制反转,面向切面编程

(4)支持事务的处理,对框架整合的支持

总结:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

1.4spring的组成

img

Spring框架的7个模块

组成 Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

(1)核心模块

SpringCore模块是Spring的核心容器,它实现了IOC模式,提供了Spring框架的基础功能。此模块中包含的BeanFactory类是Spring的核心类,负责JavaBean的配置与管理。它采用Factory模式实现了IOC即依赖注入。谈到JavaBean,它是一种Java类,它遵从一定的设计模式,使它们易于和其他开发工具和组件一起使用。定义JavaBean是一种JAVA语言写成的可重用组件。要编写JavaBean,类必须是具体类和公共类,并且具有无参数的构造器。

(2)Context模块

SpringContext模块继承BeanFactory(或者说Spring核心)类,并且添加了事件处理、国际化、资源装载、透明装载、以及数据校验等功能。它还提供了框架式的Bean的访问方式和很多企业级的功能,如JNDI访问、支持EJB、远程调用、集成模板框架、Email和定时任务调度等。

(3)AOP模块

Spring集成了所有AOP功能。通过事务管理可以使任意Spring管理的对象AOP化。Spring提供了用标准Java语言编写的AOP框架,它的大部分内容都是基于AOP联盟的API开发的。它使应用程序抛开EJB的复杂性,但拥有传统EJB的关键功能。

(4)DAO模块

DAO是DataAccessObject的缩写,DAO模式思想是将业务逻辑代码与数据库交互代码分离,降低两者耦合。通过DAO模式可以使结构变得更为清晰,代码更为简洁。DAO模块提供了JDBC的抽象层,简化了数据库厂商的异常错误(不再从SQLException继承大批代码),大幅度减少代码的编写,并且提供了对声明式事务和编程式事务的支持。

(5)ORM映射模块

SpringORM模块提供了对现有ORM框架的支持,各种流行的ORM框架已经做得非常成熟,并且拥有大规模的市场,Spring没有必要开发新的ORM工具,它对Hibernate提供了完美的整合功能,同时也支持其他ORM工具。注意这里Spring是提供各类的接口(support),目前比较流行的下层数据库封闭映射框架,如ibatis,Hibernate等。

(6)Web模块

此模块建立在SpringContext基础之上,它提供了Servlet监听器的Context和Web应用的上下文。对现有的Web框架,如JSF、Tapestry、Structs等,提供了集成。Structs是建立在MVC这种公认的好的模式上的,Struts在M、V和C上都有涉及,但它主要是提供一个好的控制器和一套定制的标签库上,也就是说它的着力点在C和V上,因此,它天生就有MVC所带来的一系列优点,如:结构层次分明,高可重用性,增加了程序的健壮性和可伸缩性,便于开发与设计分工,提供集中统一的权限控制、校验、国际化、日志等等。

(7)MVC模块

pringWebMVC模块建立在Spring核心功能之上,这使它能拥有Spring框架的所有特性,能够适应多种多视图、模板技术、国际化和验证服务,实现控制逻辑和业务逻辑的清晰分离。说说MVC在JSP的作用,这里引入了“控制器”这个概念,控制器一般由Servlet来担任,客户端的请求不再直接送给一个处理业务逻辑的JSP页面,而是送给这个控制器,再由控制器根据具体的请求调用不同的事务逻辑,并将处理结果返回到合适的页面。因此,这个Servlet控制器为应用程序提供了一个进行前-后端处理的中枢。一方面为输入数据的验证、身份认证、日志及实现国际化编程提供了一个合适的切入点;另一方面也提供了将业务逻辑从JSP文件剥离的可能。业务逻辑从JSP页面分离后,JSP文件蜕变成一个单纯完成显示任务的东西,这就是常说的View。而独立出来的事务逻辑变成人们常说的Model,再加上控制器Control本身,就构成了MVC模式。实践证明,MVC模式为大型程序的开发及维护提供了巨大的便利。

【注意】:现在大多数公司都在使用springBoot进行快速开发,而学习springBoot的前提是把spring和SpringMVC完全掌握至关重要。

第二章 Ioc

1.配置mean项目

​ (1)首先用maven构建一个简单的工程,删除里面的src目录,为了创建更多的模块,让此项目作为一个父工程。注意在创建maven项目时,在setting中要配置好自己的maven仓库,以及找到maven中的conf目录中的setting.xml。

(2)在父工程的pom.xml中配置spring依赖,这里选用spring-webmvc,这个依赖可以导入spring很多核心jar包,例如spring-core、spring-beans…依赖项可以去maven仓库进行下载。

   <dependencies>

    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.3.3version>
    dependency>
dependencies>
2.传统方法的实现

1、先写一个UserDao接口

public interface UserDao {
public void getUser();
}

2、再去写Dao的实现类

public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
    System.out.println("hello spring");
}
}

3、然后去写UserService的接口

public interface UserService {
public void getUser();
}

4、最后写Service的实现类UserServiceImpl

public class UserServiceImpl implements UserService {

 //在业务层要调用Dao层内容,就必须通过对象new 出来,而且写的固定了
private UserDao userDao = new UserDaoImpl();

@Override
public void getUser() {
    userDao.getUser();
}
}

5、测试一下

public static void main(String[] args) {
     /**
         * 用户只需要调用业务层,dao层他们不需要接触。 */
          UserServiceImpl userService = new UserServiceImpl();
          userService.getUser();
}

这里只有一个实现类,客户往往需求都比较多。当使用这个方法时,客户需要修改需求,我们就要就要重新去编写一个实现类去实现里面的方法。

把Userdao的实现类增加一个UserDaoMySqlImpl .

public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
    System.out.println("MySql获取用户数据");
}
}

当编写完一个新的实现类后,紧接着如果我们要去使用UserDaoMySqlImpl实现类的话 , 我们就需要去service实现类里面修改对应的实现,重新new一个对象: private UserDao userDao = new UserDaoMySqlImpl();

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoMySqlImpl();

   @Override
   public void getUser() {
       userDao.getUser();
  }
}

(1)如果我们再增加一个Userdao的实现类 .那么我们又需要去service实现类里面修改对应的实现 .

(2)假设我们的这种需求非常大 , 这种方式就根本不适用了, 甚至反人类对吧 , 每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .

3.IOC原型的推导

传统的这种方法把对象固定死了,耦合性太高,每次增加一个dao实现类,就需要去serviceImpl实现类中修改代码,导致程序的主动权掌握在程序员手中,而且要是代码量及其庞大,这样就会导致代码的维护及其昂贵。那我们如何去解决呢 ?

我们可以把private UserDao userDao = new UserDaoMySqlImpl() , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下 .

public class UserServiceImpl implements UserService {
   private UserDao userDao;
// 利用set实现
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
  }

   @Override
   public void getUser() {
       userDao.getUser();
  }
}

现在去我们的测试类里 , 进行测试 ;

public static void main(String[] args) {
        /**
        *原先测试类:
         * 用户只需要调用业务层,dao层他们不需要接触。
         *  UserServiceImpl userService = new UserServiceImpl();
         *  userService.getUser();
          */

        //现在我们想要用什么实现类,只需要把实现类对象传进去就可以
        UserServiceImpl userService = new UserServiceImpl();
        
		//(1)userService.setUserDao(new UserDaoImpl()); 
        userService.setUserDao(new UserDaoMysqlImpl());
        userService.getUser();

        /**
         * 在之前的业务中,用户的需求可能会影响我们原来写的代码,我们需要根据用户的需求去修改源代码,
         * 如果程序有大量的代码,修改起来就会非常昂贵。
         */

    }

(1)在之前的业务中,用户的需求可能会影响我们原来写的代码,我们需要根据用户的需求去修改源代码, 如果程序有大量的代码,修改起来就会非常昂贵。

(2)以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .

(3)这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 .可以更加专注业务的实现,这也就是IOC的原型 !

4.IOC的本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

Spring笔记_第1张图片

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

container magic

Spring笔记_第2张图片

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

5.spring案例实现

(1)首先编写一个实体类Hello

package com.qyh.pojo;

public class Hello {
 private String name;

 public Hello() {
 }

 public Hello(String name) {
     this.name = name;
 }

 public String getName() {
     return name;
 }

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

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

(2)在resources文件夹下编写beans.xml

【注意】

​ bean=对象
​ id=变量名
​ class= new 的对象 (采用全限定名称"com.qyh.pojo.Hello")
​ property 相当于给对象中的属性赋值,其中name表示属性名,value是要填入的数据,这里填写的是“spring”


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">


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

beans>

(3)测试代码

1.ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”),这个是固定书写方式,里面的的参数是配置文件的名称。

2.通过context.getBean(“hello”)方法获取配置文件中绑定的id,参入想要的bean对象,会返回一个实体类对象,可以调用其中的方法。

import com.qyh.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取spring上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //我们的对象都在spring中管理了,我们要去使用,直接去里面去就可以
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}
  • Hello 对象是谁创建的 ?

    hello 对象是由Spring创建的

  • Hello 对象的属性是怎么设置的 ?

    hello 对象的属性是由Spring容器设置的

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收,可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .

6.IOC创建对象

(1)ioc默认是通过无参构造方法来创建对象的

(2)使用有参构造有三种方式创建。

通过下标创建,index代表有参构造中参数的索引,从零开始。

<bean id="hello" class="com.qyh.pojo.Hello">
    <constructor-arg index="0" value="刘晓明"/>
bean>

通过类型(不推荐使用)

<bean id="hello" class="com.qyh.pojo.Hello">
    <constructor-arg type="java.lang.String" value="刘编辑"/>
bean>

推荐使用】通过参数名,name代表有参构造中参数的名字,value用来填写值。

<bean id="hello" class="com.qyh.pojo.Hello">
    <constructor-arg name="name" value="张三"/>
bean>

【总结】:每写一个java类,就要写一个bean对象,在配置文件加载时,容器管理的对象就会被初始化了,放在spring容器中,要用的时候就会被调用。

7.spring的配置
1.bean的配置

id】: bean的唯一标识,也相当与对象名

class】:bean对象对应的全限定名 ,包名+类名

<bean id="hello" class="com.qyh.pojo.Hello">
       <property name="name" value="Spring"/>
bean>
2.import

一般一个项目有许多人来完成,每个人负责的类不同,取的bean名不一样,就要通过import来导入,最后将所有人的bean.xml整合到一个大的applicationContext.xml文件中,然后我们使用只需要加载一个applicationContext.xml即可。

ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);

<import resource="beans.xml"/>

第三章、依赖注入

1.构造器注入
<bean id="hello" class="com.qyh.pojo.Hello">
    <constructor-arg name="name" value="张三"/>
bean>
2.set注入【重点】

依赖注入:set注入

(1)依赖:bean对象的创建依赖于容器

(2)注入:bean对象中的所有属性,由容器来注入

【环境搭建】

(1)复杂类型

package com.qyh.pojo;

public class Address {
    private String address;

    public Address() {
    }

    public Address(String address) {
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

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

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

(2)真实测试对象

package com.qyh.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] book;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

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

    public String[] getBook() {
        return book;
    }

    public void setBook(String[] book) {
        this.book = book;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", book=" + Arrays.toString(book) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

(3)beans.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.qyh.pojo.Student">

    <property name="name" value="javaEE"/>
bean>

beans>

(4)测试类

import com.qyh.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");

        System.out.println(student.getName());
    }
}

测试类方法的概述:

通过这行代码ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”)创建一个spring容器,并且绑定相应的beans.xml文件,然后用context的对象调用getBean()方法,参入beans.xml中java实体类通过bean标签绑定的唯一值Id,来找寻需要的java实体类对象。最后通过getBean获取的实体类对象,调用里面的get.set方法,等等。

【set注入不同值的方法

2.1普通值的注入

第一种普通值注入 , 利用property标签,写入实体类的属性名name,和value值就可以

<bean id="student" class="com.qyh.pojo.Student">

    <property name="name" value="javaEE"/>
bean>
2.2bean对象的注入
<bean id="address" class="com.qyh.pojo.Address"/>
<bean id="student" class="com.qyh.pojo.Student">

    <property name="address" ref="address"/>
bean>
2.3数组注入

    <property name="book" >
        <array>
            <value>三国演义value>
            <value>水浒传value>
            <value>红楼梦value>
            <value>西游记value>
        array>
    property>
2.4List注入

    <property name="hobbys">
        <list>
            <value>唱歌value>
            <value>跑步value>
            <value>看书value>
            <value>敲代码value>
        list>
    property>
2.5map注入

    <property name="card">
        <map>
            <entry key="身份证" value="361245369852365214"/>
            <entry key="银行卡" value="457845869852365214"/>
        map>
    property>
2.6set注入

    <property name="games">
        <set>
            <value>LOLvalue>
            <value>BOBvalue>
        set>
    property>
2.7NUll注入

    <property name="wife">
        <null/>
    property>
2.8Properties注入

    <property name="info">
        <props>
            <prop key="学号">188456325prop>
            <prop key="性别">prop>
        props>
    property>
2.9测试结果
 public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");

        System.out.println(student.toString());
        /**
         * Student{name='javaEE',
         * address=Address{address='中国'},
         * book=[三国演义, 水浒传, 红楼梦, 西游记],
         * hobbys=[唱歌, 跑步, 看书, 敲代码],
         * card={身份证=361245369852365214, 银行卡=457845869852365214},
         * games=[LOL, BOB],
         * wife='null',
         * info={学号=188456325, 性别=男}}
         * Process finished with exit code 0
         */
    }
3.Bean的作用域(Scope)

img

(1)被声明为singleton的bean

如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。

Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,singleton作用域是Spring中的缺省作用域(默认)。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

<bean id="..." class="..." scope="singleton">bean>
(2)被声明为prototype的bean

当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例。声明为prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

从图上可以看出在SingletonBeanA中设置的参数值在SingletonBeanB就获取不到了,说明这两个对象现在返回的就不是同一个对象实例。

<bean id="SingletonBean" class="com.spring.demo.SingletonBean" scope="prototype">bean>
(3)请求作用域

请求作用域参考如下的Bean定义

 <bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

(1)Spring容器会在每次用到loginAction来处理每个HTTP请求的时候都会创建一个新的LoginAction实例。也就是说loginActionBean的作用域是HTTP Request级别的。
(2)当http请求调用作用域为request的bean的时候,每增加一个HTTP请求,Spring就会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。开发者可以随意改变实例的状态,因为通过loginAction请求来创建的其他实例根本看不到开发者改变的实例状态,所有创建的Bean实例都是根据独立的请求来的。

(4)会话作用域

会话作用域参考如下的Bean定义

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

(1)Spring容器会在每次调用到userPreferences时,在一个单独的HTTP会话周期来创建一个新的UserPreferences实例。换言之,userPreferencesBean的作用域是HTTP Session级别的。

(2)Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。 在request-scoped作用域的Bean上,开发者可以随意的更改实例的状态。同样,使用从同一个userPreferences bean定义创建的其他HTTP Session实例在看不到不是自己的内部状态的修改,因为他们是单个的HTTP会话。每个Session请求都会创建新的userPreferences实例,所以开发者更改一个Bean的状态,对于其他的Bean仍然是不可见的。

(5)全局作用域

全局作用域参考如下的Bean定义

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

(1)Spring容器会在整个web应用范围使用到appPreferences的时候创建一个新的AppPreferences的实例。也就是说,appPreferencesBean是在ServletContext级别的,作为常规的ServletContext属性。这种作用域在一些程度上来说和Spring的单例作用域相似,但是也有如下不同之处:

1.application作用域是每个ServletContext中包含一个,而不是每个SpringApplicationContext之中包含一个(某些应用中可能包含不止一个ApplicationContext)。

2.application作用域仅仅作为ServletContext的属性可见,单例Bean是ApplicationContext可见。

接下来再来简单的学习下在Spring当中如何自定义作用域:

在Spring 2.0中,Spring的Bean作用域机制是可以扩展的,这意味着,你不仅可以使用Spring提供的预定义Bean作用域,还可以定义自己的作用域,甚至重新定义现有的作用域(不提倡这么做,而且你不能覆盖内置的singleton和prototype作用域)

(6)自定义作用域

除了使用Spring已经定义好的作用域之外,还可以自定义bean的作用域。

1.首先需要实现自定义Scope类。

首先要先实现org.springframework.beans.factory.config.Scope这个接口,要将自定义scope集成到Spring容器当中就必须要实现这个接口。接口中有两个常用的方法,分别用于底层存储机制获取和删除这个对象。

2.在实现一个或多个自定义Scope并测试通过之后,接下来便是如何让Spring容器来识别新的作用域。registerScope方法就是在Spring容器中用来注册新的作用域。

void registerScope(String scopeName, Scope scope);

其中:第一个参数是与作用域相关的全局唯一的名称,第二个参数是准备实现的作用域的实例,就是实现Scope接口的实例。

比如实现Scope接口的类为SimpleThreadScope,要实现的自定义的bean的作用域的名称为“thread”,那就可以这么写。

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope(“thread”, threadScope);
3.在实现和注册自定义的scope类之后,就可以通过如下类似的Bean定义来使用自定义的Scope:

 <bean id="..." class="..." scope="thread">

另外,在自定义的Scope中,开发者也不限于仅仅通过编程方式来实现自定义的bean的作用域,也可以在Spring的配置文件中配置和使用自定义作用域和,比如配置CustomScopeConfigurer实例实现自定义的作用域,声明作用域名称为“thread”,就可以在xml文件中做如下类似的定义。

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="thread">
                <bean class="org.springframework.context.support.SimpleThreadScope"/>
            entry>
        map>
    property>
bean>
 
<bean id="bar" class="x.y.Bar" scope="thread">
    <property name="name" value="Rick"/>
    <aop:scoped-proxy/>
bean>
 
<bean id="foo" class="x.y.Foo">
    <property name="bar" ref="bar"/>
bean>
4.Bean的自动装配

自动装配是spring满足bean依赖一种方式

Spring会在上下文中自动寻找,并自动给bean装配属性

【spring有三种装配方式】:

(1)在xml中显示的配置

(2)在java中显示配置

(3)隐式的自动装配bean【重要】

4.1环境搭建

一个人可以有两个宠物

(1)实体类

package com.qyh.pojo;

public class People {
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.qyh.pojo;

public class Cat {
    public void shout(){
        System.out.println("瞄,瞄~");
    }
}
package com.qyh.pojo;

public class Dog {
    public void shout(){
        System.out.println("汪,汪~");
    }
}

(2)配置beans.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="cat" class="com.qyh.pojo.Cat">bean>
    <bean id="dog" class="com.qyh.pojo.Dog">bean>
    <bean id="people" class="com.qyh.pojo.People">
        <property name="name" value="刘明"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    bean>

beans>

(3)测试

import com.qyh.pojo.People;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        People people = context.getBean("people", People.class);
        people.getDog().shout();
        people.getCat().shout();
    }
}
4.2自动装配

通过autowire=“byName”,会自动在容器上下文中查找,和自己对象set方法后面的值(setDog,setCat)对应的的bean的id.【必须保证bean的id唯一,否则会报错】

<bean id="people" class="com.qyh.pojo.People" autowire="byName">
    <property name="name" value="刘新师"/>
bean>

通过autowire=“byType”,会自动在容器上下文中查找,和自己对象属性类型相同的bean的id.【必须保证bean的class唯一,否则会报错】

<bean id="people" class="com.qyh.pojo.People" autowire="byType">
    <property name="name" value="刘新师"/>
bean>

手动配置的差异

<bean id="people" class="com.qyh.pojo.People">
        <property name="name" value="刘明"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    bean>
5注解实现自动装配
1.如何使用注解实现自动装配?

(1)导入约束

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

(2)配置注解支持 < context:annotation-config/> 【重要】


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

beans>

(3)注解代码 @Autowired

public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
    }

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <bean id="cat" class="com.qyh.pojo.Cat">bean>
    <bean id="dog" class="com.qyh.pojo.Dog">bean>
    <bean id="people" class="com.qyh.pojo.People"/>
beans>

【总结】:

(1) @Autowired 直接在属性上使用即可,也可以在set方法上。

(2)使用@Autowrite可以省略set方法,但是前提是你这个自动装配的属性在IOC容器中存在,而且符合名字byname。

(3)@nullable 字段标明了这个注解,说明这个字段可以为null。

public @interface Autowired {
    boolean required() default true;
}
//如果 required=false,说明可以为空null.

(4)常用】如果@Autowired 自动装配环境复杂,无法通过@Autowired实现的时候,可以使用@Qualifier(value = “dog”)去配合@Autowired 的使用,指定一个唯一的bean对象的注入。

(5)@Resource注解也可以使用在属性字段上,实现自动装配,默认通过byname来实现,如果找不到名字,就通过Bytype来实现自动装配,如果都没有就报错。

6.使用注解开发

(1)在spring4之后,要使用注解开发,必须导入aop包 (spring-aop-5.3.3.jar)

(2)导入context约束,增加注解的支持


<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        https://www.springframework.org/schema/context/spring-context.xsd">  
    <context:annotation-config/> 
beans>

(3)如何注解 @Component组件

//等价于
//@Component组件
@Component
public class User {
    // 
    @Value("不一样的代码人")
    public String name;
}

(4)衍生的注解

@Component组件有几个衍生注解,在我们web开发中,会按照Mvc三层架构分层

(1)dao层

@Repository
public class UserDao {
}

(2)service层(业务层)

@Service
public class UserService {
}

(3)controller层 -》sprigMVC

@Controller
public class UserController {
}

这四个注解的功能都是一样的,都代表每个类注册到spring中,装配bean

(5)作用域Scope

(6)xml与注解的区别:

​ xml更加万能,适用于任何场合,维护简单方便

​ 注解不是自己的类使用不了,维护相对复杂

xml用来管理bean,而注解负责完成属性的注入方式最佳

注意:让注解生效,必须开启注解的支持


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.qyh.pojo"/>

    <context:annotation-config/>
beans>
7.javaConfig实现配置

(1)我们可以完全不使用Spring的xml配置了,全权交给java来做。

(2)javaConfig是Spring的一个子项目,在spring4之后变成了核心功能.

package com.qyh.pojo;

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

//这里的这个注解的意思,就是说明这个类被Spring接管了,注册到了容器
@Component
public class User {
    private String name;

    public String getName() {
        return name;
    }

@Value("Spring") //属性注入值
    public void setName(String name) {
        this.name = name;
    }

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

package com.qyh.config;

import com.qyh.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//这个也会被Spring容器托管,注册到容器中,因为它也是一个@Component组件
//@Configuration 代表这是一个配置类,和我们之前的beans.xml
@Configuration
@ComponentScan("com.qyh.pojo")
@Import(qyhConfig2.class)//当有多个配置时,用这个来导入
public class qyhConfig {

    //注册一个@Bean,就相当于我们之前写的一个bean标签
    // 这个方法的名字相当于bean标签中的id属性
    //方法的返回值,相当于bean标签中的class属性
    @Bean
    public User getUser(){
        return new User(); //返回要注入到bean的对象
    }
}
import com.qyh.config.qyhConfig;
import com.qyh.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类,我们只能通过AnnotationConfigApplicationContext来获取容器,
        //通过配置类的class对象来加载
       ApplicationContext context = new AnnotationConfigApplicationContext(qyhConfig.class);
        User getUser = (User) context.getBean("getUser");
        System.out.println(getUser.getName());
    }
}

第四章 AOP

1.代理模式

为什么要学习代理模式?

因为这个是SpringAOP的底层实现

代理模式得到分类:
静态代理

例如租房

抽象角色:一般会使用接口或者抽象类来解决

package com.qyh.demo01;

//租房
public interface Rent {
    public void rent();
}

真实角色:被代理的角色

package com.qyh.demo01;
//房东
public class Host implements Rent{

    public void rent() {
        System.out.println("房东要出租房子");
    }
}

代理的角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

package com.qyh.demo01;
//代理,中介
public class proxy implements Rent{
    private Host host;

    public proxy() {
    }

    public proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        host.rent();
    }
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

}

客户:访问代理对象的人

package com.qyh.demo01;
//客户
public class Client {
    public static void main(String[] args) {
        proxy proxy = new proxy(new Host());
        proxy.rent();
        proxy.seeHouse();
    }
}

代理的好处:】

(1)可以使真实角色的操作更加纯粹,不用去关注一些公共的业务。

(2)公共业务交给了代理角色,实现业务的分工。

(3)公共业务发生业务扩展的时候,方便集中管理。

代理的缺点:】

一个真实角色会产生一个代理角色,代码量会翻倍,开发效率低。

静态代理再次深入理解:

(1)Crud的接口

package com.qyh.demo02;

public interface Crud {
    public void select();
    public void  insert();
    public void update();
    public void delete();
}

(2)编写一个类CrudImpl来实现这个Crud接口

package com.qyh.demo02;

public class CrudImpl implements Crud{
    public void select() {
        System.out.println("数据的查询");
    }

    public void insert() {
        System.out.println("数据的添加");
    }

    public void update() {
        System.out.println("数据的修改");
    }

    public void delete() {
        System.out.println("数据的删除");
    }
}

(3)测试代码

package com.qyh.demo02;

public class MyTest {
    public static void main(String[] args){
        Crud crud = new CrudImpl();
        crud.delete();
        crud.insert();
        crud.update();
        crud.delete();
    }
}

(4)结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpH02027-1617329022935)(C:\Users\86188\AppData\Roaming\Typora\typora-user-images\image-20210331104015415.png)]

这样确实可以实现,但是如果要给每一个方法添加一个日志,此时如果没有代理的概念,就需要回到接口的实现类,对源代码进行修改,如果方法实现有成千上万个,修改起来及其麻烦,如下面的形式:

package com.qyh.demo02;

public class CrudImpl implements Crud{
    public void select() {
        System.out.println("实现类select方法");
        System.out.println("数据的查询");
    }

    public void insert() {
        System.out.println("实现类select方法");
        System.out.println("数据的添加");
    }

    public void update() {
        System.out.println("实现类select方法");
        System.out.println("数据的修改");
    }

    public void delete() {
        System.out.println("实现类select方法");
        System.out.println("数据的删除");
    }
}

这样虽然可以实现,但是改动源代码是一个大忌,不被推荐,此时我们就需要使用的代理模式的概念,来避免这种事件的出现。

写一个代理,实现接口的方法

package com.qyh.demo02;

public class CrudImplProxy implements Crud{
    private CrudImpl crud;

    //spring建议用set方法注入

    public void setCrud(CrudImpl crud) {
        this.crud = crud;
    }

    public void select() {
        log("select");
        crud.select();

    }

    public void insert() {
       log("insert");
        crud.insert();

    }

    public void update() {
       log("update");
        crud.update();

    }

    public void delete() {
        log("delete");
        crud.delete();

    }
    //添加一个日志方法
    public void log(String msg){
        System.out.println("实现类"+msg+"方法");
    }
}
package com.qyh.demo02;

public class MyTest {
    public static void main(String[] args){
        CrudImplProxy crudImplProxy = new CrudImplProxy();
        //要代理谁就把他传进去
        crudImplProxy.setCrud(new CrudImpl());
        crudImplProxy.select();
    }
}

上面的代码就是增加了一个代理模式,不用去修改原来的代码

(1)在公司修改源代码,是大忌

我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

聊聊AOP:纵向开发,横向开发

img

OOP七大原则

1.开闭原则
对扩展开放,对修改关闭。

当需求发生变化时,在不修改软件实体源代码的情况下,可以进行功能扩展,使其满足新需求。

2.里氏替换原则
继承必须确保超类所拥有的性质在子类中仍然成立。

通俗来讲就是,子类可以扩展父类的功能,但不能改变父类原有的功能,即不可以重写父类的方法。

如果通过重写父类方法扩展新功能,写起来可能会很简单,但这会使整个继承体系的可复用性变得很差,特别是运用多态比较频繁时,程序很有可能会运行错误。

典型案例,“正方形不是长方形”,“鸵鸟不是鸟”。

3.依赖倒置原则
要面向接口编程,不要面向实现编程。

原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

依赖倒置原则是实现开闭原则重要途径之一。

4.单一职责原则
控制类的粒度大小,将对象解耦,提高其内聚性。

职责指类变化的原因,一个类应当有且只有一个引起它变化的原因,若有多个则该类需要拆分。如果一个对象承担了太多职责,至少会存在以下两个缺点:

一、当一个职责变化时,可能会影响该类实现其他职责的能力。

二、当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

单一职责同样也适用于方法。一个方法应该尽可能做好一件事情,如果一个方法处理的事情太多,其颗粒度会变得很粗,不利于重用。

5.接口隔离原则
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

6.迪米特法则
只与你的直接朋友交谈,不跟“陌生人”说话。

简单来说就是,如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

但过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

7.合成复用原则
软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。组合是“has a”关系,继承是“is a”关系。

组合就是A类的对象是B类的成员变量,相当于 A类是B类对象的一个属性。
例如,Dog类和Animal类应该使用继承关系,因为狗是动物;Person类和Head类应该使用组合关系,因为头是组成人体的一部分,即人有头。

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

动态代理

(1)动态代理的角色和静态代理的一样 .

(2)动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

(3)动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

(3)基于接口的动态代理----JDK动态代理

(4)基于类的动态代理–cglib

(5)现在用的比较多的是 javasist 来生成动态代理

​ Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

(6)我们这里使用JDK的原生代码来实现,其余的道理都是一样的!

(7)JDK的动态代理需要了解两个类

核心 : InvocationHandler (调用处理程序) 和 Proxy(代理) , 打开JDK帮助文档看看

  • public interface InvocationHandler
    

    InvocationHandler是通过一个代理实例零调用处理程序实现的接口。

    每个代理实例都有一个相关的调用处理程序。当一个方法是在一个代理实例调用,调用的方法进行编码并派遣其调用处理程序的invoke方法。

    • Proxy创建动态代理类的实例提供了静态方法,也是所有动态代理类的父类的方法创建。

      创建一个接口Foo代理:

      InvocationHandler处理器=新myinvocationhandler(…);class<> proxyclass =代理,动态代理类(foo。类。getclassloader(),foo类);Foo f =(Foo)proxyclass。getconstructor(InvocationHandler。类)。newInstance(处理);
      

      或者更简单的:

      Foo f =(Foo)代理。代理实例(foo。getclassloader()类,新班<> [ ] { foo.class },处理程序);
      
package com.qyh.demo03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//代理调用处理程序
//用这类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy(){
        /**
         * this.getClass().getClassLoader() 加载类的位置
         * rent.getClass().getInterfaces() 代理的接口
         */
     return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制
        Object invoke = method.invoke(rent, args);
        return invoke;
    }
}
package com.qyh.demo03;

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色,现在没有

        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的的接口对象
        pih.setRent(host);
        Rent proxy = (Rent)pih.getProxy();//proxy就是动态生成的
        proxy.rent();
    }
}
2.AOP实现

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

img

Aop在Spring中的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解下:

横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(PointCut):切面通知 执行的 “地点”的定义。

连接点(JointPoint):与切入点匹配的执行点。

img

img

1.使用Spring实现Aop

(1)首先导入spring依赖环境

<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-webmvcartifactId>
        <version>5.3.3version>
    dependency>
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.9.4version>
    dependency>
dependencies>

(2)


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.qyh.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.qyh.log.BeforeLog"/>


    <aop:config>
         
        <aop:pointcut id="pointcut" expression="execution(* com.qyh.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
    aop:config>
beans>
import com.qyh.service.UserService;
import com.qyh.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("applicaion.xml");
       //动态代理的是接口
        UserService userService =(UserService)context.getBean("userService");
        userService.add();
    }
}
package com.qyh.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {
    /**
     *
     * @param method  要执行的目标对象方法
     * @param objects  参数
     * @param o    目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("MethodBeforeAdvice");
    }
}
2.自定义来实现aop[主要是切面]

1、execution(): 表达式主体 (必须加上execution)。

2、第一个*号:表示返回值类型,*号表示所有的类型。

3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,cn.smd.service.impl包、子孙包下所有类的方法。

4、第二个*号:表示类名,*号表示所有的类。

5、*(…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

书写的注意事项:execution(* cn.smd.service.impl..(…))


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.qyh.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.qyh.log.BeforeLog"/>

    <bean id="diyPointCut" class="com.qyh.diy.DiyPointCut"/>
   <aop:config>
       <aop:aspect ref="diyPointCut">
        
           <aop:pointcut id="point" expression="execution(* com.qyh.service.UserServiceImpl.*(..))"/>
         
           <aop:before method="before" pointcut-ref="point"/>
           <aop:after method="after" pointcut-ref="point"/>
       aop:aspect>
   aop:config>
beans>
3.注解实现
package com.qyh.service;

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

//注解实现aop
@Aspect //标明这个类是切面
public class AnnotationPointCut {
    @Before("execution(* com.qyh.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("==方法执行==");
    }
}

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.qyh.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.qyh.log.BeforeLog"/>
    <bean id="annotationPointCut" class="com.qyh.service.AnnotationPointCut"/>

    <aop:aspectj-autoproxy/>

beans>

第五章 Mybatis整合

1、导入相关jar包

(1)junit

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
dependency>

(2)mybatis

<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.2version>
dependency>

(3)mysql-connector-java

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>5.1.47version>
dependency>

(4)spring相关

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.1.10.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.1.10.RELEASEversion>
dependency>

(5)aspectJ AOP 注入器


<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>

(6)mybatis-spring整合包 【重点】

<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatis-springartifactId>
    <version>2.0.2version>
dependency>

(7)配置Maven静态资源过滤问题!

<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>
    resources>
build>
2、mybatis的回顾

(1)编写实体类

package com.qyh.pojo;

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    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 getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

(2)定义接口

package com.qyh.mapper;

import com.qyh.pojo.User;

import java.util.List;

public interface UserMapper {
    public List<User> selectUser();
}

(3)xml文件接口实现类




<mapper namespace="com.qyh.mapper.UserMapper">
    <select id="selectUser" resultType="com.qyh.pojo.User">
        select * from user
    select>
mapper>

(4)编写核心配置文件mybatis-config.xml.并且绑定mapper.xml文件



<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="188207236"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        <mapper resource="com/qyh/mapper/UserMapper.xml"/>
    mappers>
configuration>

(5)代码的测试

import com.qyh.mapper.UserMapper;
import com.qyh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    @Test
    public void test01() throws IOException {
        String resource="mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(true);

        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
        session.close();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rg1UURzi-1617329022938)(C:\Users\86188\AppData\Roaming\Typora\typora-user-images\image-20210401085528741.png)]

3、MyBatis-spring

在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要——因为本手册中不会提供二者的基本内容,安装和配置教程。

MyBatis-Spring 需要以下版本:

MyBatis-Spring MyBatis Spring Framework Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

(1)编写数据源

(2)SqlSessionFactory

(3)SqlSessionTemplate


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

        
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="188207236"/>
    bean>

      
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    bean>


    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">

        <constructor-arg index="0" ref="sqlSessionFactory"/>
    bean>

    <bean id="UserMapper" class="com.qyh.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    bean>

beans>

(4)需要给接口加实现类,这个实现类来操作以前mybatis的操作。

package com.qyh.mapper;

import com.qyh.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {

    //我们以前所有的操作都是通过SqlSession来执行,现在只需要通过SqlSessionTemplate
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

(5)将自己的实现类,注入到spring中



<configuration>

    
    <mappers>
        <mapper resource="com/qyh/mapper/UserMapper.xml"/>
    mappers>
configuration>

(6)测试使用

@Test
public void test02(){
   ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
    UserMapper userMapper = context.getBean("UserMapper", UserMapper.class);
    List<User> users = userMapper.selectUser();
    for (User user : users) {
        System.out.println(user);
    }
}

第六章 spring事务

1.回顾事务

(1)事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

(2)事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

(3)事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

2.事务四个属性ACID

【原子性(atomicity)】

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

【一致性(consistency)】

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

【隔离性(isolation)】

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

【持久性(durability)】

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

你可能感兴趣的:(笔记)