Spring学习笔记

概述

耦合:类间的依赖
方法间的依赖
解耦:降低程序间的依赖关系
实际开发中应该做到:编译器不依赖,运行时才依赖
解耦的思路:
第一步,使用反射来创建对象,而避免使用new关键字
在这里插入图片描述
但这样还存在问题,驱动的描述字符串是写死的,如果需要切换其他数据库,则需要修改源码,不符合设计原则,解决办法:
第二步:通过读取配置文件来获取要创建的对象全限定类名

bean与spring容器的关系

在这里插入图片描述
bean配置信息定义了Bean的实现及依赖关系,spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载、实例化Bean,并建立Bean和Bean的依赖关系,最后将这些准备就绪的Bean放到Bean缓存池中,以供外层应用程序进行调用。

容器

Spring自带了多个容器实现,可以归为两种不同的类型。bean工 厂(由org.springframework. beans. factory.beanFactory接口定义)是最简单 的容器,提供基本的DI支持。应用上下文 (由org.springframework.context.ApplicationContext接口定义)基于 BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用 事件给感兴趣的事件监听者。
虽然我们可以在bean工厂和应用上下文之间任选一种,但bean工厂对大多数应用来说往往太 低级了,因此,应用上下文要比bean工厂更受欢迎。

应用上下文

Spring通过应用上下文(Application Context)装载bean的定义并把它们组装起来。Spring应用 上下文全权负责对象的创建和组装。Spring自带了多种应用上下文的实现,它们之间主要的 区别仅仅在于如何加载配置。
因为knights.xml中的bean是使用XML文件进行配置的,所以选择 ClassPathXmlApplicationContext[1]作为应用上下文相对是比较合适的。该类加载位 于应用程序类路径下的一个或多个XML配置文件。程序清单1.8中的main()方法调 用ClassPathXmlApplicationContext加载knights.xml,并获得Knight对象的引用。
程序清单1.8 KnightMain.java加载包含Knight的Spring上下文
在这里插入图片描述
这里的main()方法基于knights.xml文件创建了Spring应用上下文。随后它调用该应用上下文 获取一个ID为knight的bean。得到Knight对象的引用后,只需简单调用embarkOnQuest() 方法就可以执行所赋予的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险 任务,而且完全没有意识到这是由BraveKnight来执行的。只有knights.xml文件知道哪个骑 士执行哪种探险任务。

beanFactory和ApplicationContext区别

ApplicationContext:在构建核心容器时,创建对象采取的策略是立即加载的方式,也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象(单例对象下适用)
beanFactory:在构建核心容器时,创建对象采取的策略是延迟加载的方式,只有使用id获取bean对象的时候,才真正的创建对象(多例对象适用)

应用上下文类型

Spring自带了多种类型的应用上下文。常见的如下:
AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。
在这里插入图片描述
AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。
**ClassPathXmlApplicationContext:**从所有类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。 要求配置文件必须在类路径下,否则加载不了
在这里插入图片描述
FileSystemXmlapplicationcontext:从指定的文件系统路径下 的一个或多个XML配置文件 中加载上下文定义。 (必须有访问权限)在这里插入图片描述
XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上 下文定义。
应用上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取 bean。
Spring容器中的bean的生命周期
在这里插入图片描述
详细描 述如下:
1.Spring对bean进行实例化;
2.Spring将值和bean的引用注入到bean对应的属性中;
3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方 法;
4.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将 BeanFactory容器实例传入;
5.如果bean实现了ApplicationContextAware接口,Spring将调 用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来; 6.如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()方法;
7.如果bean实现了InitializingBean接口,Spring将调用它们的after
PropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方 法也会被调用;
8.如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法;
9.此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中, 直到该应用上下文被销毁;
10.如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同 样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

容器配置元数据(bean配置)

装配: 创建应用组件之间协作的行为。
1.基于xml文件配置bean:
Spring有多种装配bean的方式,采用 XML是很常见的一种装配方式。对于基于XML的配置,Spring 2.0以后使用Schema的格式,使得不同类型的配置拥有了自己的命名空间,是配置文件更具扩展性。
xml配置参数详解
①默认命名空间:它没有空间名,用于Spring Bean的定义;
②xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间;
③aop命名空间:这个命名空间是Spring配置AOP的命名空间,是用户自定义的命名空间。
命名空间的定义分为两个步骤:第一步指定命名空间的名称;第二步指定命名空间的Schema文档样式文件的位置,用空格或回车换行进行分分隔。
一般情况下,Spring IOC容器中的一个Bean即对应配置文件中的一个<bean>,这种镜像对应关系应该容易理解。其中id为这个Bean的名称,通过容器的getBean(“foo”)即可获取对应的Bean,在容器中起到定位查找的作用,是外部程序和Spring IOC容器进行交互的桥梁。class属性指定了Bean对应的实现类。

下面是基于XML的配置文件定义了两个简单的Bean:在这里插入图片描述
2.基于注解定义bean:
Spring容器成功启动的三大要件分别是:Bean定义信息、Bean实现类以及Spring本身。

如果采用基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现。

下面是使用注解定义一个DAO的Bean:

package com.guazi.anno;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//①通过Repository定义一个DAO的Bean
@Component("userDao")
public class UserDao {

}

在①处,我们使用 @Component 注解在UserDao类声明处对类进行标注,它可以被Spring容器识别,Spring容器自动将POJO转换为容器管理的Bean。

它和以下的XML配置是等效的:

<bean id="userDao" class="com.guazi.anno.UserDao"/>

除了@Component以外,Spring提供了3个功能基本和@Component等效的注解,它们分别用于对DAO、Service及Web层的Controller进行注解,所以也称这些注解为Bean的衍型注解:(类似于xml文件中定义Bean<bean id=" " class=" "/>
@Repository:用于对DAO实现类进行标注;
@Service:用于对Service实现类进行标注;
@Controller:用于对Controller实现类进行标注;
之所以要在@Component之外提供这三个特殊的注解,是为了让注解类本身的用途清晰化,此外Spring将赋予它们一些特殊的功能。
关于三个注解的详细使用规则见:
https://www.cnblogs.com/haoyul/p/9875392.html

1.2.1 使用注解配置信息启动spring容器
spring容器默认情况下没有开启基于注解的装配。如果要使用基于java注解的元数据,我们需要首先在xml文件中引入context命名空间.然后使用<context:annotation-config/>元素开启spring的注解配置元
数据模板如下:

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

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

   <context:annotation-config/>
   <!-- bean definitions go here -->

</beans>

关于注解配置元<context:annotation-config/>的讲解:
https://www.cnblogs.com/leduo-zuul/p/10685904.html
下例中使用了 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了

此例中,Spring提供了一个context的命名空间,然后提供了通过扫描类包以应用注解定义Bean的方式:

<?xml version="1.0" encoding="UTF-8" ?>
<!--①声明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 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
         >
    <!--②扫描类包以应用注解定义的Bean-->
   <context:component-scan base-package="com.guazi.anno"/>
   <bean class="com.guazi.anno.LogonService"></bean>
   <!-- context:component-scan base-package="com.guazi" resource-pattern="anno/*.class"/ -->
   <!-- context:component-scan base-package="com.guazi">
       <context:include-filter type="regex" expression="com\.guazi\.anno.*Dao"/>
       <context:include-filter type="regex" expression="com\.guazi\.anno.*Service"/>
       <context:exclude-filter type="aspectj" expression="com.guazi..*Controller+"/>
   </context:component-scan -->
</beans>

在①处声明context命名空间,在②处即可通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。

如果仅希望扫描特定的类而非基包下的所有类,你们可以使用resource-pattern属性过滤特定的类,如下所示:

< context:component-scan base-package="com.guazi" resource-pattern="anno/*.class"/ >

这里我们将基类包设置为com.guazi,默认情况下resource-pattern属性的值为"**/*.class",即基类包里的所有类。这里我们设置为"anno/*.class",则Spring仅会扫描基包里anno子包中的类。

常用的注解:
Required注解:该注解标注到bean的setter上,意味着bean的该属性必须由spring进行注入。如果没有注入的话,spring容器在启动的时候就会报错.
Autowired注解:Autowired可以标注在字段、setter、构造函数上。默认是按类型进行装配。具体的工作流程如下:
1.如何一个字段、属性、构造方法被Autowired标注,spring去容器中找相应的类型的bean
2.如果找不到对应类型的bean,直接报错
3.如果找到了多个该类型的bean,会按照名字进行装,如果无法按照名字装配则报错
Qualifier注解: 与Autowired注解配合使用,可以使Autowired注解一开始就按照名字进行装配.
JSR-250注解
jsr-250提供了三个注解:PostConstruct、PreDestroy、Resource
PostConstruct相当于之前介绍的init-method属性,指定bean的初始化方法
PreDestroy相当于之前介绍的destroy-method属性,指定bean的销毁方法
Resource类似于Autowired注解,不过Resource是由jdk提供了,Autowired是spring提供的。Resource直接默认情况下先按照名字进行注入,如果在容器中找不到相应的名字,就会按照类型装配
JSR-330对标准注解的变体:
在这里插入图片描述
在这里插入图片描述

3.基于Java提供bean定义:
在普通的POJO类中只要标注@Configuration注解,就可以为spring容器提供Bean定义的信息了,每个标注了@Bean的类方法都相当于提供了一个Bean的定义信息。

package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//①将一个POJO标注为定义Bean的配置类
@Configuration
public class AppConf {
        //②以下两个方法定义了两个Bean,以提供了Bean的实例化逻辑
    @Bean
    public UserDao userDao(){
       return new UserDao();    
    }
    
    @Bean
    public LogDao logDao(){
        return new LogDao();
    }
    //③定义了logonService的Bean
    @Bean
    public LogonService logonService(){
        LogonService logonService = new LogonService();
                //④将②和③处定义的Bean注入到LogonService Bean中
        logonService.setLogDao(logDao());
        logonService.setUserDao(userDao());
        return logonService;
    }
}

①处在APPConf类的定义处标注了 @Configuration注解,说明这个类可用于为Spring提供Bean的定义信息 。类的 方法处可以标注@Bean注解,Bean的类型由方法返回值类型决定,名称默认和方法名相同,也可以通过入参显示指定Bean名称,如@Bean(name=“userDao”) 直接在@Bean所标注的方法中提供Bean的实例化逻辑。

在②处userDao()和logDao()方法定义了一个UserDao和一个LogDao的Bean,它们的Bean名称分别是userDao和logDao。在③处,又定义了一个logonService Bean,并且在④处注入②处所定义的两个Bean。

因此,以上的配置和以下XML配置时等效的:

<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="logService" class="com.baobaotao.conf.LogonService"
    p:logDao-ref="logDao" p:userDao-ref="userDao"/>

基于java类的配置方式和基于XML或基于注解的配置方式相比,前者通过代码的方式更加灵活地实现了Bean的实例化及Bean之间的装配,但后面两者都是通过配置声明的方式,在灵活性上要稍逊一些,但是配置上要更简单一些。

tips:从Spring3.0开始,由Spring JavaConfig提供的许多功能已经成为Spring框架中的核心部分。这样你可以使用Java程序而不是XML文件定义外部应用程序中的bean类。使用这些新功能,可以查看@Configuration,@Bean,@Import和@DependsOn这些注解.
@Bean注解加在方法上:用来说明这是一个通过Spring IoC容器来管理时一个新对象的实例化,配置和初始化的方法;可以在任何使用@Component的地方使用@Bean,但是更常用的是在配置@Configuration的类中使用。
@Configuration是一个类级别的注解,指明此对象是bean定义的资源文件。进一步的讲,被@Configuration注解的类通过简单地在调用同一个类中其他的@Bean方法来定义bean之间的依赖关系.
当@Bean方法在没有使用@Configuration注解的类中声明时,它们被称为以“lite”进行处理。例如,用@Component修饰的类或者简单的类中都被认为是“lite”模式。
不同于full @Configuration,lite @Bean 方法不能简单的在类内部定义依赖关系。通常,在“lite”模式下一个@Bean方法不应该调用其他的@Bean方法。
只有在@Configuration注解的类中使用@Bean方法是确保使用“full”模式的推荐方法。这也可以防止同样的@Bean方法被意外的调用很多次,并有助于减少在’lite’模式下难以被追踪的细小bug。

创建bean(bean实例化)的三种方式:
第一种方式:使用默认构造函数创建
在Spring的配置文件xml文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采取的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,对象无法创建。
第二种方式:使用普通工厂中的方法(使用某个类中的方法创建对象,并存入Spring容器)创建对象

package com.guazi.ditype;

public class CarFactory {
   public Car createHongQiCar(){
       Car car = new Car();
       car.setBrand("红旗CA72");
       return car;
   }
   
   public static Car createCar(){
       Car car = new Car();
       return car;
   }
}

工厂类负责创建一个或多个目标类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类实例,工厂类对外屏蔽了目标类的实例化步骤,调用者甚至不用知道具体的目标类是什么。这里想得到的是carFactory类中的createHongQiCar方法的返回值对象

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

    <!-- 工厂方法-->
    <bean id="carFactory" class="com.guazi.ditype.CarFactory" />
    <bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
    </bean>


</beans>

第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入Spring容器)
很多工厂类都是静态的,这意味着用户在无须创建工厂类实例的情况下就可以调用工厂类方法,因此,静态工厂方法比非静态工厂方法的调用更加方便。

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="car6" class="com.guazi.ditype.CarFactory"
        factory-method="createCar"></bean>

</beans>

后两种方式尤其适用于只有jar包但还需要从中获取bean时
参考文档:
https://blog.csdn.net/qq_41109806/article/details/96445610

bean的作用域可以指定:使用@Scope注解:默认的作用域是单例,但是你可以用@Scope注解重写作用域。
取值:(常用的就是单例和多例)
singleton:单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,他就是session

自定义Bean命名:默认情况下,配置类使用@Bean方法的名称作为生成的bean的名称。但是,可以使用name属性来重写此功能。
bean描述:可以使用@Description注解对Bean添加描述
bean的生命周期
单例对象:
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象:
出生:当使用对象时Spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用且没有别的对象引用时,由Java的垃圾回收期回收

@Import注解允许从另一个配置类中加载@Bean定义

依赖注入(dependency injection,DI)

概况

通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设 定。对象无需自行创建或管理它们的依赖关系,如图1.1所示,依赖关系将被自动注入到需 要它们的对象当中去。
在这里插入图片描述
图1.1 依赖注入会将所依赖的关系自动交给目标对象,而不是让对象自己去获取依赖
依赖关系的维护就称之为依赖注入
关于注入和创建bean对象的区别:https://www.cnblogs.com/nijunyang/p/7688292.html
依赖注入能注入的数据有三类:(注入的通常是不怎么变化的值)
1.基本类型和string
2.其他bean类型(在配置文件中或者注解配置过的bean)
3.复杂类型(例:String[],List<String>,Map<String,String>, Properties, Set<String>)

注入方式

Bean注入的方式有三种:在XML中分别有属性注入、构造函数注入;另一种则是使用注解的方式注入@Autowired,@Resource,@Required。
第一种:使用构造函数提供
构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index :用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置行0开始
name :用于指定给构造函数中指定名称的参数赋值(最常用)

=以上三个用于指定给构造函数中的哪个参数赋值=====
value: 用于给基本类型和string类型的参数以具体值
ref:用于赋值 指定为其他的bean类型数据,其他类型指的是在Spring的ioc核心容器中出现过的bean对象。
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据也必须提供。

第二种:使用set方法提供(属性注入)
涉及的标签:property
出现的位置:bean标签的内部
标签属性:
name:用于指定注入时所用的set方法名称,其名称为类中定义的方法名称去掉set并把剩余部分的首字母变为小写。
value: 用于给基本类型和string类型的参数以具体值
ref:用于赋值 指定为其他的bean类型数据,其他类型指的是在Spring的ioc核心**容器中出现过的bean对象。
优势:创建对象时没有明确的制,可以直接使用默认构造函数
劣势:如果有某个成员必须有值,则获取对象有可能set方法没有执行

给复杂类型的注入/集合类型的注入
用于给list结构集合注入的标签:list array set
用于给map结构注入的标签:map props
结构相同标签可以互换

第三种:使用注解提供
@Autowired

1.在xml文件中配置依赖注入
1.1.1 属性注入

属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。

属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值

package com.guazi.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{

    private LogDao logDao;

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}

bean.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
       default-autowire="byName"
         >
    <bean id="logDao" class="com.guazi.anno.LogDao"/>
    <bean id="userDao" class="com.guazi.anno.UserDao"/>
   <bean class="com.guazi.anno.LogonService">
       <property name="logDao" ref="logDao"></property>
       <property name="userDao" ref="userDao"></property>
   </bean>
</beans>

1.1.2 构造函数注入
使用构造函数注入的前提是Bean必须提供带参数的构造函数 。例如

package com.guazi.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{

    public LogonService(){}

    public LogonService(LogDao logDao, UserDao userDao) {
        this.logDao = logDao;
        this.userDao = userDao;
    }

    private LogDao logDao;

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}

bean.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
       default-autowire="byName">

    <bean id="logDao" class="com.guazi.anno.LogDao"/>
    <bean id="userDao" class="com.guazi.anno.UserDao"/>
   <bean class="com.guazi.anno.LogonService">
      <constructor-arg  ref="logDao"></constructor-arg>
       <constructor-arg ref="userDao"></constructor-arg>
   </bean>

</beans>

2 使用注解的方式注入
2.1 使用@Autowired进行自动注入
Spring通过@Autowired注解实现Bean的依赖注入,下面是一个例子:

package com.guazi.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
//① 定义一个Service的Bean(不需要在XML中定义Bean)
@Service
public class LogonService implements BeanNameAware{
        //② 分别注入LogDao及UserDao的Bean(不需要在XML中定义property属性注入)
    @Autowired(required=false)
    private LogDao logDao;
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}

在①处,我们使用@Service将LogonService标注为一个Bean,在②处,通过@Autowired注入LogDao及UserDao的Bean。@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入到@Autowired标注的变量中。
自动注入怎么实现的?:
_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg3NzY1Mw==,size_16,color_FFFFFF,t_70)

2.2使用@Autowired的required属性
如果容器中没有一个和标注变量类型匹配的Bean,Spring容器启动时将报NoSuchBeanDefinitionException的异常。如果希望Spring即使找不到匹配的Bean完成注入也不用抛出异常,那么可以使用@Autowired(required=false)进行标注:

@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
        ...
}

默认情况下,@Autowired的required属性的值为true,即要求一定要找到匹配的Bean,否则将报异常。

2.3 使用@Qualifier指定注入Bean的名称
如果容器中有一个以上匹配的Bean时,则可以通过@Qualifier注解限定Bean的名称,如下所示:

@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
    //①注入名为UserDao,类型为UserDao的Bean
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
}

这里假设容器有两个类型为UserDao的Bean,一个名为userDao,另一个名为otherUserDao,则①处会注入名为userDao的Bean。

2.4 对类方法进行标注
@Autowired可以对类成员变量及方法的入参进行标注,下面我们在类的方法上使用@Autowired注解:

package com.guazi.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class LogonService implements BeanNameAware{
    
    private LogDao logDao;
    private UserDao userDao;
    
    
    @Autowired
    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    @Autowired
    @Qualifier("userDao")
    public void setUserDao(UserDao userDao) {
        System.out.println("auto inject");
        this.userDao = userDao;
    }
    
}

如果一个方法拥有多个入参,在默认情况下,Spring自动选择匹配入参类型的Bean进行注入。Spring允许对方法入参标注@Qualifier以指定注入Bean的名称,如下所示:

  @Autowired
    public void init(@Qualifier("userDao")UserDao userDao,LogDao logDao){
        System.out.println("multi param inject");
        this.userDao = userDao;
        this.logDao =logDao;
    }

在以上例子中,UserDao的入参注入名为userDao的Bean,而LogDao的入参注入LogDao类型的Bean。

一般情况下,在Spring容器中大部分的Bean都是单实例的,所以我们一般都无须通过@Repository、@Service等注解的value属性为Bean指定名称,也无须使用@Qualifier按名称进行注入。

2.5 对标准注解的支持
此外,Spring还支持@Resource和@Inject注解,这两个标准注解和@Autowired注解的功能类似,都是对类变量及方法入参提供自动注入的功能。@Resource要求提供一个Bean名称的属性,如果属性为空,则自动采用标注处的变量名或方法名作为Bean的名称。

package com.guazi.anno;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import org.springframework.stereotype.Component;

@Component
public class Boss {
    
    private Car car;
    
    public Boss(){
        System.out.println("construct...");
    }

//    @Autowired
//    private void setCar(Car car){
//        System.out.println("execute in setCar");
//        this.car = car;
//    }
    
    @Resource("car")
    private void setCar(Car car){
        System.out.println("execute in setCar");
        this.car = car;
    }
    
    @PostConstruct
    private void init1(){
        System.out.println("execute in init1");
    }
    
    @PostConstruct
    private void init2(){
        System.out.println("execute in init1");
    }
    
    @PreDestroy
    private void destory1(){
        System.out.println("execute in destory1");
    }
    
    @PreDestroy
    private void destory2(){
        System.out.println("execute in destory2");
    }

}

这时,如果@Resource未指定"car"属性,则也可以根据属性方法得到需要注入的Bean名称。可见**@Autowired默认按类型匹配注入Bean,@Resource则按名称匹配注入Bean。**而@Inject和@Autowired一样也是按类型匹配注入的Bean的,只不过它没有required属性。可见不管是@Resource还是@Inject注解,其功能都没有@Autowired丰富,因此除非必须,大可不必在乎这两个注解。(类似于Xml中使用<constructor-arg ref="logDao"></constructor-arg>或者<property name="logDao" ref="logDao"></property>进行注入,如果使用了@Autowired或者Resource等,这不需要在定义Bean时使用属性注入和构造方法注入了)

2.6 关于Autowired和@Resource
1.@Autowired注入是按照类型注入的,只要配置文件中的bean类型和需要的bean类型是一致的,这时候注入就没问题。但是如果相同类型的bean不止一个,此时注入就会出现问题,Spring容器无法启动。
2.@Resourced标签是按照bean的名字来进行注入的,如果我们没有在使用@Resource时指定bean的名字,同时Spring容器中又没有该名字的bean,这时候@Resource就会退化为@Autowired即按照类型注入,这样就有可能违背了使用@Resource的初衷。所以*建议在使用@Resource时都显示指定一下bean的名字@Resource(*name=“xxx”)

2.7 让@Resource和@Autowired生效的几种方式
1.在xml配置文件中显式指定

<!-- 为了使用Autowired标签,我们必须在这里配置一个bean的后置处理器 -->  
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />   
      
    <!-- 为了使用@Resource标签,这里必须配置一个后置处理器 -->  
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />   

2.在xml配置文件中使用context:annotation-config

<context:annotation-config />

3.在xml配置文件中使用context:component-scan

<context:component-scan base-package="com.baobaotao.anno"/>

4.重写Spring容器的Context,在自定义BeanFactory时调用AnnotationConfigUtils.registerAnnotationConfigProcessors()把这两个注解处理器增加到容器中。

面向切面编程(aspect-oriented programming,AOP)

概况

在这里插入图片描述
现存的代码存在的问题:虽然事务正常,但是一个线程获取了多次连接,所以创建了多个控制事务的对象。
所以引申出了动态代理
动态代理分类:
基于接口的动态代理:要求被代理类至少实现一个接口,没有则不能创建代理对象
基于子类动态代理:要求被代理类不能是最终类
AOP实现方式:使用动态代理技术
Spring中的aop就是通过配置方式实现代码的动态代理
aop相关术语:
Joinpoint(连接点): 就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法有关的前前后后都是连接点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。
Pointcut(切入点): 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
Aspect(切面): 是切入点(哪些方法被增强过就是我们的切入点)和通知(引介)的结合
。现在发现了吧,没连接点什么事,链接点就是为了让你好理解切点搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的befor,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。
Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
在这里插入图片描述
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。
Target(目标对象): 代理的目标对象,也就是被代理对象 。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。

横切关注点: 系统由许多不同的组 件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常 承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业 务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组 件。
如果将这些关注点分散到多个组件中去,代码将会带来双重的复杂性。
实现系统关注点功能的代码将会重复出现在多个组件中。这意味着如果你要改变这些关 注点的逻辑,必须修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的 模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的 方法应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。
AOP 能够使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造 成的结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉 及系统服务所带来复杂性。

spring中基于xml的aop配置

步骤
1.把通知Bean也交给spring来管理
2.使用aop:config标签表明开始aop的配置
3.使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一的标识
ref属性:是指定通知类bean的id
4.在aop:aspect标签的内部使用对应标签来配置通知的类型
前置\后置\异常\最终\环绕
aop:before:表示配置前置通知,在切入点方法执行之前执行
aop:after-returning:表示配置后置通知,在切入点方法正常执行之后执行
aop:after-throwing:表示配置异常通知,在切入点方法执行产生异常之后执行,异常通知和后置通知永远只能执行一个
aop:after:表示最终通知,无论切入点方法是否正常执行他都会在其后面执行

method属性:用于指定logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
标准表达式写法:(这里是对saveaccount方法进行增强)

 public void com.guazi.service.impl.AccountServiceImpl.saveAccount()

访问修饰符可以省略

   void com.itheima.service.impl.AccountServiceImpl.saveAccount()

返回值可以使用通配符,表示任意返回值

 * com.itheima.service.impl.AccountServiceImpl.saveAccount()

包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.

  * *.*.*.*.AccountServiceImpl.saveAccount())

包名可以使用..表示当前包及其子包

* *..AccountServiceImpl.saveAccount()

类名和方法名都可以使用*来实现通配

 * *..*.*()

参数列表:
可以直接写数据类型:
基本类型直接写名称 int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
例:有三个方法需要增强,两个无参数一个有参数,则此时被增强的只有有参数的方法
可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:

 * *..*.*(..)

实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法

* com.guazi.service.impl.*.*(..)

在这里插入图片描述在这里插入图片描述
如果多个通知pointcut相同,一堆通知会很繁琐,aspect标签中有<aop:pointcut id="pt " expression="excution(* com.guazi.service.impl.*.*(..))"></aop:pointcut>
配置切入点表达式。 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用。
它还可以写在aop:aspect外面,此时就变成了所有切面可用

此时在各个通知配置中,pointcut就改变为了如下形式:
在这里插入图片描述
环绕通知

  • 问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
  • 分析:通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
  • 解决: Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
    该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
  • spring中的环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。看在proceed方法的前后,为前置后置。
    在这里插入图片描述

基于注解的aop配置

xml文件中,使用<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 配置spring开启注解AOP的支持
或者不使用xml配置文件的方式:
在这里插入图片描述

在类文件中:
@Aspect:加在类上,表示当前类是一个切面类
@Pointcut:加在方法上,表示当前方法为切入点表达式在这里插入图片描述
@Before(“pt1()”):加在方法上,表示配置为前置通知
@AfterReturning(“pt1()”):后置通知
@AfterThrowing(“pt1()”):异常通知
@After(“pt1()”):最终通知
上述在执行调用顺序上会有问题
@Around(“pt1()”):环绕通知,建议使用,因为没有顺序问题

Spring模块

在这里插入图片描述

Spring核心容器

容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块 中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多 种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。 除了bean工厂和应用上下文,该模块也提供了许多企业服务,例如E-mail、JNDI访问、EJB 集成和调度。
所有的Spring模块都构建于核心容器之上。当你配置应用时,其实你隐式地使用了这些类。

Spring的AOP模块

在AOP模块中,Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。与DI一样,AOP可以帮助应用对象解耦。借助于AOP,可以将遍布系统的关注 点(例如事务和安全)从它们所应用的对象中解耦出来。

数据访问与集成

使用JDBC编写代码通常会导致大量的样板式代码,例如获得数据库连接、创建语句、处理结果集到最后关闭数据库连接。Spring的JDBC和DAO(Data Access Object)模块抽象了这些样板式代码,使我们的数据库代码变得简单明了,还可以避免因为关闭数据库资源失败而引发的问题。该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层,以后我们再也不需要解释那些隐晦专有的SQL错误信息了!
Spring还提供了ORM模块。Spring的ORM模块建立在对DAO的支持之上,并为多个ORM框架提 供了一种构建DAO的简便方式。Spring没有尝试去创建自己的ORM解决方案,而是对许多流 行的ORM框架进行了集成,包括Hibernate、Java Persisternce API、Java Data Object和iBATIS SQL Maps。Spring的事务管理支持所有的ORM框架以及JDBC。

Web与远程调用

虽然Spring能够与多种流行的MVC框架进行集成,但它的Web和远程调用模块自带了一个强 大的MVC框架,有助于在Web层提升应用的松耦合水平。
除了面向用户的Web应用,该模块还提供了多种构建与其他应用交互的远程调用方案。 Spring远程调用功能集成了RMI(Remote Method Invocation)、Hessian、Burlap、JAX-WS, 同时Spring还自带了一个远程调用框架:HTTP invoker。Spring还提供了暴露和使用REST API 的良好支持。

Instrumentation

Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能。具体来讲,它为Tomcat 提供了一个植入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一样。

测试

鉴于开发者自测的重要性,Spring提供了测试模块以致力于Spring应用的测试。 通过该模块,你会发现Spring为使用JNDI、Servlet和Portlet编写单元测试提供了一系列的 mock对象实现。对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spring上 下文中的bean进行交互提供了支持。

spring artifactid

在这里插入图片描述
在这里插入图片描述

Spring Portfolio

整个Spring Portfolio包括多个构建于核心Spring框架之上的框架和类库。概括地讲,整个 Spring Portfolio几乎为每一个领域的Java开发都提供了Spring编程模型。

Spring Web Flow

Spring Web Flow建立于Spring MVC框架之上,它为基于流程的会话式Web应用(可以想一下 购物车或者向导功能)提供了支持。

Spring Web Service

虽然核心的Spring框架提供了将Spring bean以声明的方式发布为Web Service的功能,但是这 些服务是基于一个具有争议性的架构(拙劣的契约后置模型)之上而构建的。这些服务的契 约由bean的接口来决定。 Spring Web Service提供了契约优先的Web Service模型,服务的实现 都是为了满足服务的契约而编写的。

Spring Security

安全对于许多应用都是一个非常关键的切面。利用Spring AOP,Spring Security为Spring应用 提供了声明式的安全机制。

Spring Integration

许多企业级应用都需要与其他应用进行交互。Spring Integration提供了多种通用应用集成模式 的Spring声明式风格实现。

Spring Batch

当需要对数据进行大量操作的时候,需要批处理,如果需要开发一个批处理应用,可以通过Spring Batch.

Spring Data

Spring Data使得spring中使用任何数据库都变得非常容易。不管你使用文档数据库,如MongoDB,图数据库,如Neo4j,还是传统的关系型数据来,Spring Data都为持久化提供了一种简单的编程模型。这包括为多种数据库类型提供了一种自动化的Respository机制,他负责为我们创建Repository的实现。

Spring Social

Spring的一个社交网络扩展模块,它更多的是关注连接,而不是社交,能帮助我们通过REST API连接Spring应用。

Spring Mobile

Spring Mobile是Spring MVC新的扩展模块,用于支持移动Web应用开发。

Spring for Android

与Spring Mobile相关的是Spring Android项目。这个新项目,旨在通过Spring框架为开发基于 Android设备的本地应用提供某些简单的支持。最初,这个项目提供了Spring RestTemplate的一个可以用于Android应用之中的版本。它还能与Spring Social协作,使得 原生应用可以通过REST API进行社交网络的连接。

JdbcTemplate

概述

它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多 的操作模板类。
操作关系型数据的: JdbcTemplate HibernateTemplate
操作 nosql 数据库的: RedisTemplate
操作消息队列的: JmsTemplate

在spring的ioc中使用

1.在spring文件中配置JdbcTemplate
在这里插入图片描述

CRUD操作

1.保存操作
在这里插入图片描述
2.更新操作
在这里插入图片描述
3.删除操作
在这里插入图片描述
4.查询所有
在这里插入图片描述
或者使用jdbctemplate自带的容器:在这里插入图片描述

5.查询一个操作
在这里插入图片描述
或者:

List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));

在这里插入图片描述
6.查询返回一行一列
在这里插入图片描述

在DAO中使用JdbcTemplate

1.在 dao 中定义 JdbcTemplate
在这里插入图片描述
这种方式下, dao 有很多时,每个 dao 都有一些重复性的代码。下面就是重复代码:

 private JdbcTemplate jdbcTemplate;   
 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {   this.jdbcTemplate = jdbcTemplate;  } 

2.让 dao 继承 JdbcDaoSupport
在这里插入图片描述
这种方式下,JdbcTemplate jdbcTemplate;set方法不用每次都重复写。
两版 Dao区别
第一种在 Dao类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml和注解都可以)。 第二种让 Dao继承 JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。

Spring的新功能

Spring 4.0新特性

1.Spring提供了对WebSocket编程的支持,包括支持JSR-356——Java API for WebSocket;
2.鉴于WebSocket仅仅提供了一种低层次的API,急需高层次的抽象,因此Spring 4.0在 WebSocket之上提供了一个高层次的面向消息的编程模型,该模型基于SockJS,并且包 含了对STOMP协议的支持;
3.新的消息(messaging)模块,很多的类型来源于Spring Integration项目。这个消息模块支 持Spring的SockJS/STOMP功能,同时提供了基于模板的方式发布消息;
4.Spring是第一批(如果不说是第一个的话)支持Java 8特性的Java框架,比如它所支持的 lambda表达式。别的暂且不说,这首先能够让使用特定的回调接口(如RowMapper和 JdbcTemplate)更加简洁,代码更加易读;
5.与Java 8同时得到支持的是JSR-310——Date与Time API,在处理日期和时间时,它为开 发者提供了比java.util.Date或java.util.Calendar更丰富的API;
6.为Groovy开发的应用程序提供了更加顺畅的编程体验,尤其是支持非常便利地完全采用 Groovy开发Spring应用程序。随这些一起提供的是来自于Grails的BeanBuilder,借助它能 够通过Groovy配置Spring应用;
7.添加了条件化创建bean的功能,在这里只有开发人员定义的条件满足时,才会创建所声 明的bean;
8.Spring 4.0包含了Spring RestTemplate的一个新的异步实现,它会立即返回并且允许在 操作完成后执行回调;
9.添加了对多项JEE规范的支持,包括JMS 2.0、JTA 1.2、JPA 2.1和Bean Validation 1.1。


版权声明:本文为weixin_43877653原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。