Spring学习笔记


1、简介

  • Spring是一个开源的免费的框架(容器)
  • Spring是一个轻量级的、非入侵式的框架
  • 控制反转((IOC),面向切面编程(AOP)
  • 支持事务的处理,对框架整合的支持

总结一句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。



    org.springframework
    spring-webmvc
    5.3.14

2、IOC本质

  • 控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有lOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
  • 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
  • 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在 Spring 中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,Dl)。

3、XML创建实体对象

3.1 创建实体类Hello

package pojo;

// 只有无参构造函数
public class Hello {

    private String str;
    
    /*
        public Hello(String name){
            this.str = name;
        }
    */

    public String getStr() {
        return str;
    }

    public void setStr( String str ) {
        this.str = str;
    }

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

3.2 Spring配置文件Beans.xml

<?xml version="1.0" encoding="UTF-8" ?>


    
    
        
        
    
    
        
        
    

    
    
    
        
    
    
    
        
    
    
    
        
    


  • 编写完成后记得配置应用程序上下文

3.3 测试运行

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

public class MyTest {

    @Test
    public void Test() {
        // 获取Spring的上下文对象!
        ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" );
        // 我们的对象现在都在Spring中的管理了,我们要使用,直接去里面取出来就可以!
        Hello hello = (Hello) context.getBean( "hello" );
        System.out.println( hello.toString() );
        
        //输出:Hello{str='Spring'}
    }
}

4、Spring配置

4.1 别名

  • 别名和原名都能够取到对象



ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" );
//	以下两种均可
Hello hello = (Hello) context.getBean( "user" );
Hello hello = (Hello) context.getBean( "userNew" );

4.2 Bean的配置



    

4.3 import

这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个配置文件。

假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中。

我们可以利用import将所有人的beans.xml合并为一个总的!




使用的时候,使用总的配置就可以了

正式的总配置文件名为applicationContext.xml

5、依赖注入

5.1 构造器注入

见3.2 Spring配置文件Beans.xml

5.2 set方式注入(重点)

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List hobbys;
    private Map card;
    private Setgames;
    private String wife;
    private Properties info;
}

    
    

    
    

    
    
        
            红楼梦
            西游记
            水浒传
            三国演义
        
    

    
    
        
            听歌
            敲代码
            看电影
        
    

    
    
        
            
            
        
    
    
    
    
        
            LOL
            coc
            BOB
        
    

    
    
        
    
    
    

    
    
        
            xxx
            xxx
            root
            123456
        
    

5.3 拓展方式注入

5.3.1 p命名空间注入



    
    
    
    


  • 注意导入schema:xmlns:p="http://www.springframework.org/schema/p"

5.3.2 C命名空间注入



    
    

    
    
        
        
        
    

    
    


  • 注意导入schema:xmlns:c="http://www.springframework.org/schema/c"

5.4 Bean的作用域

  • 单例模式 Singleton(Spring默认机制)

    
    
  • 原型模式 Prototype :每次从容器中getBean的时候,都会产生一个新对象!

    
    
  • 其余的request、session、application只能在Web开发中使用到。

6、Bean的自动装配

  • 自动装配是 Spring 满足 bean 依赖的一种方式!
  • Spring 会在上下文中自动寻找,并自动给 bean 装配属性!
  • 在 Spring 中有三种装配的方式
    • 在 XML 中显式配置
    • 在 Java 中显式配置
    • 隐式的自动装配 bean


    
    
    
        
        
        
    

6.1 ByName自动装配


    
    
    
    
    
        
    

6.2 ByType自动装配


    
    
    
    
    
        
    

小结:

  • byName 的时候,需要保证所有 bean 的 id 存在且唯一,并且这个 bean 需要和自动注入的属性的 set 方法的值一致!

  • byType 的时候,需要保证所有 bean 的 class 存在且唯一,并且这个 bean 需要和自动注入的属性的类型一致!

6.3 使用注解实现自动装配

使用注解须知:

  1. 导入约束

  2. 配置注解的支持:

    <?xml version="1.0" encoding="UTF-8"?>
    
        
        
    
     
            ...
        
    
    

6.3.1 @Autowired

直接在属性上使用!也可以在set方式上使用!
使用@Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性Type在 Spring 容器中存在,且符合名字byName!


    
    
    
    
    

public class People {
    
    @Autowired
    private cat cat;
    
    //@Autowired无法实现时,要配合@qualifier(value="")来针对某一个bean进行装配
    @Autowired
    @Qualifier(value="dog222")
    private Dog dog;
    
    private string name;
}

6.3.2 @Resource

public class People {
    @Resource(name = "cat11")
    private cat cat;
    
    @Resource
    private Dog dog;
}

@Resource 和 @Autowired 的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 先通过byType的方式实现,后通过byName,而且必须要求这个对象存在!【常用】
  • @Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】

小结:

  • @Autowired 先byType,如果同类型个数大于1,再byName。

  • dog只注册了一个的时候,dog的id可以随便取都能自动装配相当于byType。当dog注册了多个时,只能绑定id和person属性名一样的bean相当于byName,如果多个都没有对应的id就报错

  • 如果 @Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value="xxx") 去配置 @Autowired 的使用,指定一个唯一的bean对象注入!

    这个value对应的是bean的id,相当于byName。

7、使用注解开发

在 Spring 4 之后,要使用注解开发。必须要保证 aop 的包导入了!

使用注解需要导入 context 约束,增加注解的支持。

<?xml version="1.0" encoding="UTF-8"?>


    
    
    


7.1 常用注解

7.1.1 @Autowired

  • 通过类型、名字,自动装配
  • 如果 @Autowired 不能唯一装配上属性,则需要通过 @Qualifier( value = "xxx" ) 指定唯一bean

7.1.2 @Resource

  • 通过名字、类型,自动装配

7.1.3 @Nullable

  • 字段标记了这个注解,说明这个字段可以为null

7.1.4 @Component

  • 组件,放在类上,说明这个类被 Spring 管理了,成为Bean。
//等价于:
@Component
public class User {

    public string name;

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

7.1.5 @Value

  • @Value( ) 放在属性或set方法上均可。
@component
public class User {
    
    //相当于 
    @Value( "kuangshen2")
    public string name;
    
    public void setName( String name ) {
        this.name = name;
    }
}

7.1.6 @Scope

@component
//确定作用域:单例或原型
@scope("prototype")
public class User {
    
    public string name;
    
    @Value( "kuangshen2")
    public void setName( String name ) {
        this.name = name;
    }
}

7.2 衍生的注解

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

    • dao【@Repository】
    • service【@Service】
    • controller【@Controller】

    这四个注解功能都是一样的,都是代表将某个类注册到 Spring 容器中,装配Bean。

8、使用 Java 的方式(注解)配置 Spring

我们现在要完全不使用 Spring 的 XML 配置了,全权交给 Java来做!

JavaConfig 是 Spring 的一个子项目,在 Spring 4 之后,它成为了一个核心功能

package config;

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

// 这个也会由Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的applicationContext.xml一样
@configuration	//相当于
@ComponentScan( "pojo" )	//扫描某个包下的所有@Component
@Import(AppConfig2.class)
public class AppConfig {
    // 注册一个bean ,就相当于我们之前写的一个标签
    // 这个方法的名字,就相当于bean标签中的id属性
    // 这个方法的返回值,就相当于bean标签中的class属性 
    @Bean
    public User user() {
        return new User();
    }
}
  • 测试
public class MyTest {
    public static void main(string[] args) {
        // 如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器
        // 通过配置类的class对象加载!
        Applicationcontext context = new
            AnnotationConfigApplicationContext(Appconfig.class);
        User user = (user) context.getBean("user");
        System.out.println(user.getName());
    }
}

9、代理模式

9.1 静态代理

  • 代理对象和真实对象都有共同的接口
  • 客户直接通过代理对象直接调用方法
  • 在代理类中静态扩展代码

9.2 动态代理

  • 动态代理和静态代理角色是一样的。
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口 —— JDK 动态代理
    • 基于类 —— cglib
    • Java字节码实现 —— Javassist

需要了解两个类: Proxy:代理 Invocationhandler:调用处理程序

//等我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private object target;
    
    public void setTarget(object target) {
        this.target = target;
    }
    
    //生成得到代理类
    public object getProxy(){
        return Proxy.newProxyInstance(this.getclass().getclassLoader(),
                                      target.getclass().getInterfaces(), this);
    }
    
    //处理代理实例,并返回结果:
    public object invoke(0bject proxy,Method method,object[] args) throws Throwable {
        log(method.getName());
        object result = method.invoke(target, args);
        return result;
    }
    public void log(string msg){
        system.out.println("执行了" + msg + "方法");
    }
}
public class client {
    public static void main( string[ ] args) {
        //真实角色 (实现类)
        UserServiceImpl userService = new UserServiceImpl();
        //创建代理生成类的实例
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userservice); //设置要代理的对象
        //代理角色 (动态生成代理类)
        UserService proxy = (UserService) pih.getProxy();
        proxy.query();
    }
}

动态代理相对于静态代理的好处:

无需编写代理类:对于静态代理,每个类都要写对应的代理类,会多出许多冗余的类;而动态代理,仅需通过一个代理生成类,动态生成对应类的对应代理对象,减少了代码量。

10、AOP

10.1 图解AOP

  • 什么叫面向切面编程?这就是:

10.2 Spring 实现 AOP



    org.aspectj
    aspectjweaver
    1.9.7

10.2.1 使用原生 Spring API













    
    
    
    
    

10.2.2 自定义类实现





    
    
        
        
        
        
        
    

10.2.3 注解实现AOP




//方式三:使用注解方式实现AOP
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;

@Aspect //标注这个类是一个切面
public class AnnotationPointcut {

    @Before( "execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
        system.out.println( "=======方法执行前======" );
    }

    @After( "execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
        system.out.println( "=======方法执行后======" );
    }

    // 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
    // 可以获取切入点的相关信息。
    @Around( "execution(*com.kuang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        
        system.out.println("环绕前");
        
        object proceed = jp.proceed();	//执行方法
        
        system.out.println("环绕后");
    }
}

10.3 Spring AOP 五大通知

  • Before 前置通知

  • AfterReturning 后置通知

  • Around 环绕通知

  • AfterThrowing 异常通知

  • After 最终通知

五种通知的执行顺序

  1. 在目标方法没有抛出异常的情况下

    前置通知 → 环绕通知的调用目标方法之前的代码 → 目标方法 → 环绕通知的调用目标方法之后的代码 →

    后置通知 → 最终通知

  2. 在目标方法抛出异常的情况下
    前置通知 → 环绕通知的调用目标方法之前的代码 → 目标方法 → 抛出异常 异常通知 → 最终通知

11、整合Mybatis

? 详细内容可以查看文档

11.1 基础方式

  1. 导入相关Jar包

    • Junit
    • Mybatis
    • MySQL数据库
    • Spring 相关
    • AOP 织入
    • mybatis-spring 【new】
  2. 编写 Spring 、Mybatis 配置文件

    
    
    <?xml version="1.0" encoding="UTF-8"?>
    
    
        
        
            
            
            
            
        
    
        
        
            
            
            
            
        
    
        
        
            
            
        
    
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    
        
        
        
        
            
        
    
    
    
  3. 写 Mapper.xml(sql语句)

    ......

  4. 写 Mapper 的实现类 MapperImpl

    import org.mybatis.spring.sqlSessionTemplate;
    import java.util.List;
    public class UserMapperImpl implements UserMapper {
        
        private sqlsessionTemplate sqlsession;
        
        public void setSqlSession(sqlSessionTemplate sqlSession) { 
            this.sqlSession = sqlSession;
        }
        
        public List selectUser() {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.selectuser();
        }
    }
    

11.2 SqlSessionDaoSupport

  • 实现类去掉属性 sqlSession 和 set 方法
  • 直接通过getSqlSession()获取sqlSession,无需注入依赖
import org.mybatis.spring.sqlSessionTemplate;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {

    public List selectUser() {
        return getSqlSession().getMapper(UserMapper.class).selectuser();
    }
}

同时,在 spring-dao.xml 中去掉:



    
    

并修改:



    




    
    

12、事务

12.1 什么叫事务?

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!·确保完整性和一致性;
  • 事务ACID原则:
    • 原子性:一个事务要么全部执行,要么不执行。
    • 一致性:数据库事务不能破坏关系数据的完整性及业务逻辑上的一致性。
    • 隔离性:多个业务可能操作同一个资源,防止数据损坏。
    • 持久性:事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中。

12.2 Spring 事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

为什么需要事务?

如果不配置事务,可能存在数据提交不一致的情况。

如果我们不在 Spring 中去配置声明式事务,我们就需要在代码中手动配置事务!

事务在项目的开发中十分重要,设计到数据的一致性和完整性问题,不容马虎!

12.3 Spring 声明式事务