Spring总结


 1.什么是spring?

Spring是一个分层的一站式轻量级开源框架。

2.spring入门

项目地址:https://github.com/zhongyushi-git/spring-collection.git。下载代码后,示例代码在spring5-demo文件夹下。本项目是以原生的jar方式介绍的,同步的maven版本的代码见https://www.cnblogs.com/zys2019/p/14538440.html。

2.1Spring的jar下载与导入

如果要使用spring中的东西,就必须被spring管理。

官网:spring.io,下载地址:https://repo.spring.io/release/org/springframework/spring/,下载对应版本,在下载时只需要下载以.dist.zip结尾的压缩包即可,这里以5.3.4为例:

下载好之后,打开libs文件夹,里面是所有的jar包

其他下载jar地址:

2.2使用spring调用对象的方法案例

1.使用idea新建一个普通的java项目

2.在项目下新建lib文件夹,把以下jar包复制到lib中

3.选择File->Project Structure..,按下图进行操作

 4.选择所有的jar,点击确定

 5.导入后如下图

6.编写配置文件。在src下新建一个applicationContext.xml的文件:

<?xml version="1.0" encoding="UTF-8"?>
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">


7.创建一个hello的类,在里面写一个方法:

package com.test;

public class Hello {
    public void say(){
        System.out.println("hello,goodmonring");
    }
}

8.修改配置文件applicationContext.xml,让其交给spring管理:


9.新建一个测试了哦进行测试测试:

package com.test;

import com.entity.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

@Test
public void helloTest() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象,其中hello就在xml中指定的id值
Hello hello = context.getBean("hello", Hello.class);//第一种方式,指定class
// Hello hello = (Hello)context.getBean("hello");//第二种方式,转换为指定类,两种方式都可以
//调用方法
hello.say();
}

}

10.运行测试方法,会发现出现了错误,原因是没有导入日志的jar。

11.只需要把commons-logging-1.1.1.jar导入进来即可,jar在项目lib文件夹中。导入后再执行就会在控制台打印相应的结果。本案例并没有使用new方式创建对象,而是使用spring方式。

3. IOC快速入门

3.1.原理

IOC:inversion of Controller(控制反转),把对象的控制权交给spring。原来由我们自己实例化的对象交给spring容器来实例化,这时对象的实例化的权利就会反转,降低耦合度。上面的案例就是使用IOC控制反转方式来操作的。

3.2.实现方式(接口)

1)BeanFactory

IOC容器最基本实现,是Spring内部的使用接口,不提供开发使用。在加载配置文件时,不会创建对象,只有在获取(使用)对象时才会创建。

2)ApplicationContext

是BeanFactory的子接口,功能更强大,供开发使用。在加载配置文件时,就会创建对象。

3.3.ApplicationContext的实现类

实现类有两个,如下图:(使用Ctrl+H查看实现类)

1)FileSystemXmlApplicationContext:使用这种方式xml可以放在磁盘的任何位置,但需要指定xml的全路径。

2)ClassPathXmlApplicationContext:使用这种方式必须让xml文件在src资源目录下,否则找不到。

4.DI快速入门

DI:dependency injection 依赖注入。在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件中,简单的说就是给对象的属性注入值。下面是对User的属性赋值案例:

4.1 通过属性注入值

注意:通过属性注入值时,此对象必须有set方法,否则注入不进去。

第一步:创建user对象,添加get和set方法

package com.entity;

public class User {
    private String name;
    private int 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 "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

第二步:把user交给spring管理并通过属性注入值

"user" class="com.entity.User">
    
    "name" value="元慧">
    "age" value="23">
 

第三步:测试方法

@Test
public void test1(){
    //加载配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取bean对象
    User user = (User) context.getBean("user");
    //调用方法
    System.out.println(user.toString());
}

执行测试方法后,打印的就是注入的值,如下图:

第四步:分析。

在注入时使用的property标签,name指定的是要注入的属性的名称,value是要注入的值。


 此行代码就是把23注入到对象User的age属性上,相当于user.setAge(20)。  

4.2 通过构造方法注入值

注意:通过构造方法注入时,此类必须有有参构造。

第一步:给User对象添加有参构造(以User2说明)

package com.entity;

public class User2 {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

第二步:把user交给spring管理并通过构造方法注入值

 "user2" class="com.entity.User2">
        
        "name" value="123">
        "age" value="20">
 

第三步:测试方法

 @Test
    public void test2() {
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean对象
        User2 user = (User2) context.getBean("user2");
        //调用方法
        System.out.println(user.toString());
    }

以上对象的属性是简单数据类型String和Integer类型的,也就是介绍了简单对象的属性注入,而其他对象的属性注入见后续章节。

第四步:分析。

在注入时使用的constructor-arg标签,name指定的是要注入的属性的名称,value是要注入的值。

4.3 注入特殊符号

当在xml中注入的值包含特殊符号时,可以使用CDATA。用法如下:

 "user" class="com.entity.User">
        
        "name" >
            ]]>
        
        "age" value="23">
    

把value单独拿出来作为标签,包含特殊符号的值放在CDATA的第二个中括号中。执行结果是User{name='<你好啊>', age=23}。

4.4 面试题IOC和DI区别?

IOC 控制反转,是指对象实例化权利由spring容器来管理

DI 依赖注入,在spring创建对象的过程中,对象所依赖的属性通过配置注入对象中。

5.Bean管理

Spring中有两种bean,一种是普通bean,即在配置文件中定义的bean和返回的类型一致;另一种是工厂bean(FactoryBean),它在配置文件中定义的bean可以和返回的类型不一养。默认创建的是单实例对象。

Bean管理就是Spring自身创建对象,再使用Spring来注入属性。

5.1使用spring创建对象

1)基于xml方式创建对象

入门案例中的xml配置就是基于这种方式

其中id是唯一标识,用于获取对象,class是指定类的路径。它在创建对象时会默认执行无参构造方法。

2)基于注解方式创建对象

详见第7章节。

5.2使用spring注入属性

详见第6章节。

5.3xml自动装配

定义:根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。

自动装配使用autowire属性,值有两种,分别是byName和byType。

byName:根据属性名称来装配,也就是说对象的属性名要和bean的id值一样,才能注入成功

byType:根据属性类型来装配,也就是说对象的属性类型要和bean的class的类型一致。如果在配置文件中有多个类型都是一样的,那么使用此方式就会报错。

在上述的案例中对象Car有属性user是User类型的,以此为例说明,下面两行代码分别是两种装配方式:

"car2" class="com.entity.Car" autowire="byName">

"car2" class="com.entity.Car" autowire="byType">

上述装配一般不会使用,只做介绍。

6.Spring属性注入

6.1基于xml方式注入

简单属性的注入在第4章已经介绍,这里介绍其他的类型属性注入。

为了方便,后面的实体类均省略getter、setter和toString方法。

1.ref的使用

在一个实体类中持有另一个实体类的引用,就可以用到ref属性。ref的值是要引用的对象的bean的id。

1)新建car类

package com.entity;

public class Car {
private String brand;
private double price;
private User user;
}

2)在xml中注入值

"user" class="com.entity.User">
     
    "name" value="元慧">
    "age" value="23">
 
"car" class="com.entity.Car">
    "brand" value="迈巴赫">
    "price" value="8000000">
    "user" ref="user">
  

3)测试方法

@Test
public void test3(){
    //加载配置文件
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取bean对象
    Car car = (Car) context.getBean("car");
    //调用方法
    System.out.println(car);
}

执行结果:

Car{brand='迈巴赫', price=8000000.0, user=User{name='元慧', age=23}}

4)使用内部bean注入

通过ref的方式是使用的外部bean方式,也就是说引用的bean都是定义好并指定了id,而内部bean是写在内部的,无需id值。把上述注入改成:

    "car" class="com.entity.Car">
        "brand" value="迈巴赫">
        "price" value="8000000">
        "user">
            class="com.entity.User">
                "name" value="郭慧">
                "age" value="20">
            
        
    

在注入user属性时,在内部写了一个bean标签来输注入User对象的属性。内部属性注入的方式并没有外部的清晰,用的较少。

2.集合属性的注入

集合类型的属性只介绍List、Set和Map类型。

1)创建实体类

实体类包含两种,一种是String类型,代表基本类型;另一种是User类型,代表对象类型。测试方法在代码中,在此略。

基本类型实体:

package com.entity;

import java.util.List;
import java.util.Map;
import java.util.Set;
public class Coll {
private List listStr;
private Set setStr;
private Map mapStr;
private Map> mapList;
}

对象类型实体:

package com.entity;

import java.util.List;
import java.util.Map;
import java.util.Set;
public class Coll2 {
private List userList;
private Set userSet;
private Map userMap;
private Map> mapList;
}

2)属性注入

A:List类型

数组类型的注入和List类似,只需要把list标签改为array标签即可,使用list标签也是可以的。

(1)基本类型注入:

"coll" class="com.entity.Coll">
    "listStr">
        
            张三
            李四
            王五
            张三
        
    

(2)实体类型注入:

注入实体类型时,使用ref标签,bean的值就是对象的bean的id值。

"user" class="com.entity.User">
    
   "name" value="元慧">
   "age" value="23">

"user3" class="com.entity.User">
    "name" value="郭慧">
    "age" value="20">

"coll2" class="com.entity.Coll2"> "userList"> <ref bean="user">ref> <ref bean="user3">ref>

B:Set类型

1)基本类型注入:




张三
李四
王五
张三


(2)实体类型注入:


    
   
   


    
    







C:Map类型

1)基本类型注入:









(2)实体类型注入:


    
   
   


    
    







D:组合类型

1)基本类型注入:


    
        
            张三
            李四
            王五
            张三
        
    

(2)实体类型注入:


    
   
   


    
    


    
       
           
           
       
    

3.外部属性文件注入

有些属性的值是需要配置的,也就是说属性的值会放到一个properties配置文件中,在xml中读取这个配置文件内容,然后把属性注入即可。下面以配置数据库连接池为例说明。

1)导入druid的jar(在lib中)

2)直接方式配置

 
    "dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        "driverClassName" value="com.mysql.jdbc.Driver" />
        "url" value="jdbc:mysql://localhost:3306/community-manage" />
        "username" value="root" />
        "password" value="root" />
    

这种方式虽然简单,但是值发生变化是还要来改xml文件,可以把配置提取出来。

3)外部属性文件配置

第一步:在src下新建db.properties,内容如下:

第二步:在beans中引入context的名称空间

 截图如上,做法是把beans的xmlns和http都复制一份,改成context即可。

第三步:把db.properties引入到xml配置文件中


"classpath:db.properties"/>

第四步:属性的注入。使用${}来注入属性,括号里面是外部文件中key值。


"dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        "driverClassName" value="${jdbc.driver}" />
        "url" value="${jdbc.url}" />
        "username" value="${jdbc.username}" />
        "password" value="${jdbc.password}" />

6.2基于注解方式注入

基于注解方式的注入内容较多,见第7章节。

7. spring注解开发

7.1什么是注解

注解是代码的特殊标记,可简化xml配置。格式:@注解名称(属性名称=属性值,属性名称=属性值...)。

7.2使用注解方式创建对象

在spring中针对bean创建对象的注解有:@Component、@Repository、@Service、@Controller。

@Component:在类的前面加上这个注解,就可以把它交给spring管理,不需要在xml中配置,相当于xml中配置一个标签。

@Repository:对Component的延伸,用于DAO层,便于区分

@Service:对Component的延伸,用于Service层,便于区分

@Controller:对Component的延伸,用于Controller层,便于区分

1)导入aop的jar。此jar包在下载的spring压缩包中有。在spring中使用注解必须进行包扫描,还要开启注解。为了代码清晰度,注解方式的配置文件以新建的applicationContext2.xml为例说明

2)在applicationContext2.xml文件中引入命名空间。

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

3)在applicationContext2.xml文件中开启注解,进行包扫描


base-package="com.test">

其中指定了包是com.test,只要把所有注解的类放到com.test包下都可以被扫描到。

4)在com.test包下新建一个UserService类,加上@Component注解。

package com.test;

import org.springframework.stereotype.Component;

@Component
//@Component(value = "userService")
public class UserService {

    public void say(){
        System.out.println("userService执行了。");
    }
}

此注解可以指定一个value值,可省略,默认值是此类名并把首字母小写。如同上述代码,使用两种方式的注解都可以,其中value的值就是在xml中指定的bean的id值。

5)新建测试方法

   @Test
    public void test7(){
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        //获取bean对象
        UserService userService=context.getBean("userService",UserService.class);
        //调用方法
        userService.say();
    }

执行测试方法,say方法正常执行。这就是使用注解注入对象。

6)开启包扫描细节问题

(1)当只指定了包名时,spring会扫描此包下面所有的类,也可以设置只扫描指定的类。


    base-package="com.test" use-default-filters="false">
        "annotation" expression="org.springframework.stereotype.Controller"/>
    

上述代码就指定了只扫描controller类,即注解是@Controller的类。

(2)除了指定要扫描的类之外,也可以设置哪些类不进行扫描


    base-package="com.test">
        "annotation" expression="org.springframework.stereotype.Controller" />
    

include-filter是包含的,exclude-filter是不包含的。

7.3使用注解方式注入属性

在spring中针对bean属性注入的注解有:@Autowired、@Qualifier、@Resource、@Value。

@Autowired:根据属性类型进行自动注入,注入复杂类型的值

@Qualifier:根据属性名称进行注入,可以解决一个类有多个子类而找不到具体哪一个类的问题

@Resource:根据属性类型换名称进行注入。是上面两个注解的合并

@Value:根据属性名称进行注入,注入简单类型的值

1)新建UserDao类,添加注解

package com.test;

import org.springframework.stereotype.Repository;

@Repository
public class UserDao {

    public void add(){
        System.out.println("我是userDao的add方法");
    }
}

2)在UserService中注入UserDao并调用add方法

package com.test;

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

@Component
//@Component(value = "userService")
public class UserService {

    public void say(){
        System.out.println("userService执行了。");
    }

    @Autowired
    private UserDao userDao;

    public void add(){
        userDao.add();
    }
}

要使用@Qualifier,那么必须和@Autowired一起使用,

@Autowired
@Qualifier("userDao")
private UserDao userDao;

它们也可以替换为@Resource

@Resource(name = "userDao")
private UserDao userDao;

3)新建测试方法,调用add方法

 @Test
    public void test8(){
        //加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        //获取bean对象
        UserService userService=context.getBean("userService",UserService.class);
        //调用方法
        userService.add();
    }

执行结果:我是userDao的add方法。

4)@Value的使用

把括号里面的值注入给变量

 @Value("123")
private String name;

这种方式就可以把123赋值给name,此时name就有值了。这个注解主要用在读取properties文件内容。

7.4全注解方式开发

全注解方式就是说不要xml配置文件,使用注解来替代xml配置文件。

1)新建config包,在包下新建SpringConfig类,添加两个注解

package com.test.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

//此注解表示此类是一个配置类,可代替xml配置文件
@Configuration
//开启注解进行包扫描
@ComponentScan(basePackages = {"com.test"})
public class SpringConfig {
}

2)新建测试方法

 @Test
    public void test9() {
        //加载配置类
        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
        //获取bean对象
        UserService userService = context.getBean("userService", UserService.class);
        //调用方法
        userService.add();
    }

这样方式并没有使用xml,仍然可以实现对象的创建和属性的注入。

8.SpringAOP

8.1aop概述

1)定义

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。主要用来记录日志。

2)AOP与OOP区别

OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。换而言之,OOD/OOP面向名词领域,AOP面向动词领域。

3)底层原理

底层使用的是动态代理,有两种方式,分别是JDK代理(用于有接口的情况)和CGLIB代理(用于没有接口的情况)。

JDK代理:需要创建当前接口实现类的代理对象

CGLIB代理:需要创建当前类的子类代理对象

8.2 aop入门

8.2.1准备工作

为了代码的清晰度,在src下新建一个配置文件applicationContext3.xml

1)导入相关的jar包(在项目lib中)

 2)在applicationContext3.xml中配置

 引入命名空间:

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

引入约束:

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

8.2.2.使用xml方式配置

1)新建包com.controller,然后新建目标类StudDao

package com.controller;

public class StuDao {
public void add(){
System.out.println("操作数据库啦");
}
}

2)编写增强类StuHelper,这个类用于在add方法调用之前执行:

package com.controller;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class StuHelper implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("我是增强类的before方法");
}
}

3)修改applicationContext3.xml:










4)新建测试类进行测试:

package com.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext3.xml")
public class StuTest {

@Autowired
private StuDao dao;

@Test
public void test(){
dao.add();
}
}

输出结果:

我是增强类的before方法
操作数据库啦

8.2.3整合aspect

1)自定义增强类:

package com.controller;

public class Helper {
    public void before(){
        System.out.println("before方法");
    }
    public void after(){
        System.out.println("after方法");
    }
}

2)修改applicationContext.xml:











3)测试:测试输出的结果是    before方法,操作数据库啦,after方法

8.2.4.使用注解方式配置

1)修改applicationContext3.xml:




2)编写目标类StuDao2:

package com.controller;

import org.springframework.stereotype.Repository;

@Repository
public class StuDao2 {

public void add(){
System.out.println("操作数据库啦");
}
}

3)编写增强类:

package com.controller;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Helper2 {

@Before(value = "execution(* com.controller.StuDao2.add(..))")
public void before(){
System.out.println("before方法");
}

@After(value = "execution(* com.controller.StuDao2.add(..))")
public void after(){
System.out.println("after方法");
}
}

4)新建测试方法,测试结果同上。

8.2.5其他通知类型

1)后置通知

//后置通知
@AfterReturning(value = "execution(* com.controller.StuDao2.add(..))", returning = "value")
public void afterReturn(JoinPoint jp, Object value) {
System.out.println("后置通知的返回值是" + value);
}

2)环绕通知

//环绕通知
@Around(value = "execution(* com.controller.StuDao2.add(..))")
public Object around(ProceedingJoinPoint jp) throws Throwable {
Object o = jp.proceed();
return o;
}

2)异常通知(常用):

//异常通知
@AfterThrowing(value = "execution(* com.controller.StuDao2.add(..))", throwing = "e")
public void afterThrow(Throwable e) {
System.out.println(e);
}

8.3.Scope注解

@Scope它以描述bean的作用域(单例或多例)。

Singleton:单例模式,内存中只会有一个该对象,是默认的模式。

Prototype:多例模式,每次使用都初始化一个新的对象。一般用在controller或service层,增加访问带宽

@Repository
@Scope("Prototype")
//指定为多例模式
public class StuDao {
   ...
}

此代码只做介绍。   

9.spring5新功能

9.1整合日志框架Log4j2

在spring5中自带了日志框架,可与log4j2结合使用,下面就介绍spring5整合日志框架。

1)导入jar(在项目的lib)

 2)在src中创建log4j.xml,此名称是固定的

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


"INFO">
    
    
        
        "Console" target="SYSTEM_OUT">
            
            "%d{yyyy-M-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        
    
    
    
    
        "info">
            ref ref="Console"/>
        
    

3)新建一个UserLog类用于测试

package com.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserLog {

    private static final Logger log= LoggerFactory.getLogger(UserLog.class);

    public static void main(String[] args) {
        log.info("你好啊");
    }
}

执行main方法可以看到在控制台的打印结果

相关