Spring学习笔记

@wanqiuz 2018-05-19 02:02:03发表于 wanqiuz/blog-articles

1. Spring概述

1.1. Spring是什么

  • Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用。
  • 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架

从大小与开销两方面Spring都是轻量级的
通过控制反转(IoC)的技术达到松耦合的目的
提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
包含并管理应用对象的配置和生命周期,这个意义上是一种容器
将其他的组件配置、组合成为复杂的应用,这个意义上是框架

1.2. 为什么用Spring

  • 在Spring上开发应用简单
  • 在Spring上开发应用方便
  • 在Spring上开发应用快捷

Spring带来了复杂JavaEE开发的春天

1.3. Spring的作用

  • 容器
  • 提供了对多种技术的支持:JMS、MQ、UnitTest
  • AOP(事务管理、日志等)
  • 提供了众多方便应用的辅助类(JDBC Template等)
  • 对主流应用框架(Hibernate等)提供了良好的支持

1.4. Spring使用范围

  • 构建企业应用(SpringMVC+Spring+Hibernate/ibatis)
  • 单独使用Bean容器(Bean管理)
  • 单独使用AOP进行切面处理
  • 其他的Spring功能,如:对消息的支持等
  • 在互联网中的应用

框架:框架就是制定一套规范或者规则(思想),程序员只能在该规范或者规则下工作
框架的特点:1. 半成品 2. 封装了特定的处理流程和控制逻辑 3. 成熟的、不断升级的软件
框架与类库的区别: 1. 框架一般是封装了逻辑、高内聚的、类库则是松散的工具组合 2. 框架专注于某一领域,类库则是更通用的
为什么使用框架: 1. 软件系统日趋复杂 2. 重用度高,开发效率和质量提高 3. 软件设计人员要专注于对领域的了解,使需求分析更充分 4. 易于上手、快速解决问题。

2. Spring IoC容器

2.1. 接口简介

  • 用于沟通的中介物的抽象化
  • 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离除外沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式
  • 对应Java接口即声明,声明了哪些方法是对外公开提供的
  • 在Java8中,接口可以拥有方法体

2.2.面向接口编程

  • 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
  • 接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要
  • ”面向接口编程“中的”接口“是用于影藏具体实现和实现多态性的组件

2.3. 什么是IoC

IoC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护

哪些方面的控制被反转了:获得依赖对象的过程被反转了

DI:依赖注入,是IoC的一种实现方式,目的是为了创建对象并且组装对象之间的关系,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。

2.4. Spring的Bean配置

2.5. 单元测试

  • 下载Junit-*.jar并引入工程
  • 创建UnitTestbase类,完成对Spring配置文件的加载、销毁
  • 所有的单元测试类都继承自UnitTestBae,通过它的getBean方法获取想要得到的对象
  • 子类(具体执行单元测试的类)加注解:@RunWith(BlockJUnit4ClassRunner.class)
  • 单元测试方法加注解:@test
  • 右键选择要执行的单元测试方法执行或者执行一个类的全部单元测试方法

2.6. Bean容器初始化

基础:两个包

  • org.springframework.beans
    BeanFactory提供配置结构和基本功能,加载并初始化Bean
  • org.springframework.context
    ApplicationContext保存了Bean对象并在Spring中被广泛使用,ApplicationContext载入Bean对象的路径:1. 本地文件(本机的文件路径) 2. Classpath(相对工程的路径) 3. Web应用中依赖servlet或listener

2.7. Spring注入

  • Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
  • 常用的两种注入方式: 1. 设值注入 2. 构造注入

设值注入:

...
<bean id="injectionService" class="...">
    <property name="injectionDAO" ref="injectionDAO" />
</bean>

<bean id="injectionDAO" class="...">
</bean>
...

构造注入:

...
<bean id="injectionService" class="...">
    <constructor-arg name="injectionDAO" ref="injectionDAO" />
</bean>

<bean id="injectionDAO" class="...">
</bean>
...

3. Spring Bean

3.1 Bean配置项

  • Id
  • Class
  • Scope
  • Constructor arguments
  • Properties
  • AutoWiring mode
  • Lazy-initialization mode
  • Initialization/destruction method

3.2. Bean作用域Scope

  • singleton: (default)每个Spring IoC容器只会存在一个Bean实例
  • prototype:每次通过Spring IoC容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,destroy方式不生效
  • request:在一次Http请求中,Spring IoC容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前Http Request内有效
  • session:在一次Http Session中,Spring IoC容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效
  • global session:在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用portlet context时有效。

3.3. Bean生命周期

  • 定义
  • 初始化
  • 使用
  • 销毁

3.3.1. 初始化

  • 实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
public class ExampleBean implements InitializingBean {
    
    @override
    public void afterPropertiesSet() throws Exception {
        // no some init work
    }
}
  • 配置init-method
<bean id="exampleInitBean" class="examples.ExampleBean" init-methods="init" />
public class ExampleBean {
    
    public void init() {
        // no some init work
    }
}

3.3.2. 销毁

  • 实现org.springframework.beans.factory.DisposableBean接口,覆盖destroy方法
public class ExampleBean implements DisposableBean {
    
    @override
    public void destroy() throws Exception {
        // no some destruction work (like releasing pooled connections)
    }
}
  • 配置destroy-method
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup" />
public class ExampleBean {
    
    public void cleanup() {
        // no some destruction work (like releasing pooled connections)
    }
}

3.3.3. 配置全局默认初始化、销毁方法

<beans ...
    default-init-method="defaultInit" default-destroy-method="defaultDestroy">

3.3.4. 三种方式同时使用时

全局默认初始化、销毁方法不生效,其他执行顺序为:

  • Bean afterPropertiesSet
  • Bean start
  • Bean destroy
  • Bean stop

3.4. Aware

  • Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化之后,可以获取相应资源
  • 通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
  • 为对Spring进行简单的扩展提供了方便的入口
接口名 作用
BeanNameAware 获得到容器中Bean的名称
BeanFactoryAware 获得当前bean Factory,从而调用容器的服务
ApplicationContextAware 当前的application context从而调用容器的服务
MessageSourceAware 得到message source从而得到文本信息
ApplicationEventPublisherAware 应用时间发布器,用于发布事件
ResourceLoaderAware 获取资源加载器,可以获得外部资源文件
... ...

3.5. Bean的自动装配(Autowiring)

  • No:(default)不做任何操作
  • byName:根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配
  • byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生
  • constructor:与byType方式类似,不同指出在于它应用于构造器参数。如果容器中没有找到与构造器类型一致的bean,则抛出异常
<beans ...
    default-autowire="byName">

3.6. Resources

  • 针对于资源文件的统一接口
  • Resources:
    -- UrlResource: URL对应的资源,根据一个URL地址即可构建
    -- ClassPathResource:获取类路径下的资源文件
    -- FileSystemResource:获取文件系统里面的资源
    -- ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
    -- InputStreamResource:针对于输入流封装的资源
    -- ByteArrayResource:针对于字节数组封装的资源
  • ResourceLoader
    -- 所有的applicatin contexts都实现了ResourceLoader接口,所以,所有的applicatin contexts都可以用于获取资源实例
public interface ResourceLoader {
    
    Resource getResource(String location);
}
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:/some/resource/path/myTemplate.txt");

3.7. Bean管理的注解实现及例子

3.7.1. Classpath扫描机组件管理

3.7.2. 元注解(Meta-annotations)

  • 许多Spring提供的注解可以作为自己的代码,即”元数据注解,元注解是一个简单的注解,可以应用到另一个注解
  • 除了value(),元注解还可以有其他的属性,允许定制

3.7.3. 类的自动检测与注册Bean

  • Spring可以自动检测类并注册Bean到ApplicaitionContext中

3.7.4. context:annotation-config/

  • 通过在基于XML的Spring配置如下标签(请注意包含上下文命名空间)
  • context:annotation-config/仅会查找在同一个applicationContext中的bean注解
<beans ...>
    <context:annotation-config/>
</beans>
  • 只会处理类中的方法或成员变量的注解

3.7.5. 类的自动检测及Bean的注册

  • 为了能够检测这些类注册相应的Bean,需要下面内容
<beans ...>
    <context:componnet-scan base-package="org.example" />
</beans>
  • context:componnet-scan包含context:annotation-config,通常在使用前者后,不用再使用后者,前者包含后者的功能
  • AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来

3.7.6. 使用过滤器进行自定义扫描

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex" expression=".*Stub.*Repository" />
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" />
</beans>
  • 还可使用use-default-filters="false"禁用自动发现与注册
    qq20180519-161946

3.7.7. 定义Bean

  • 扫描过程中组件被自动检测,那么Bean名称是由BeanNameGeenerator生成的(@component, @repository, @service, @service, @controller都会有个name属性用于显示设置Bean Name)
  • 可自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参数构造函数
<beans>
    <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator" />
</beans>

3.7.8. 作用域(Scope)

  • 通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了了一个标识scope的注解@scope
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    
    // ...
}
  • 也可以自定义scope策略,实现ScopeMetadataResolve接口并提供一个无参构造器
<beans>
    <context:component-scan base-package="org.example" scope-resolver="org.example.myScopeResolver" />
</beans>

3.7.9. 代理方式

  • 可以使用scoped-proxy属性指定代理,有三个值可选:no,interfaces,targetClass
<beans>
    <context:component-scan base-package="org.example" scope-proxy="interfaces" />
</beans>

#### 3.7.10. @Required(不常用)
* @Required注解适用于bean属性的setter方法
* 这个注解仅仅标识,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
```java
public class SimpleMovieLister implements MovieFinder {
    
    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder (MovieFinder movieFiner) {
        this.movieFinder = movieFiner;
    }
}

3.7.11. @Autowired(常用)

  • 可以将@Autowired注解为“传统”的setter方法
public class SimpleMovieLister implements MovieFinder {
    
    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder (MovieFinder movieFiner) {
        this.movieFinder = movieFiner;
    }
}
  • 可用于构造器或成员变量
public class SimpleMovieRecommender implements MovieFinder {
    
    @Autowired
    private MovieFinder movieFinder;

    CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void SimpleMovieRecommender (CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}
  • 默认情况下,如果因找不到合适的bean将会导致autowired失败抛出异常,可以通过下面的方式避免
public class SimpleMovieLister implements MovieFinder {
    
    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder (MovieFinder movieFiner) {
        this.movieFinder = movieFiner;
    }
}
  • 每个类只能有一个构造器被标记为required=true

  • @Autowired的必要属性,建议使用@required注解

  • 可以用@Aurowired注解哪些众所周知的解析依赖性接口,比如BeanFactory,ApplicationContext,Envirenment, ResourceLoader, ApplicationEventPublisher, and MessageSource

public class MovieRecommender {
    
    @Autowired
    private ApplicationContext context;

    public MovieRecommender () {
    }
}
  • 可以通过注解给需要改类型的数组的字段或方法,以提供ApplicationContext中的所有特定类型的bean
public class MovieRecommender {
    
    private Set<MovieCatelog> movieCatelogs;

    @Autowired
    public setMovieCatalogs (Set<MovieCatelog> movieCatelogs) {
        this.movieCatelogs = movieCatelogs;
    }
}
  • 可以用于装配key为String的Map
public class MovieRecommender {
    
    private Map<String, MovieCatelog> movieCatelogs;

    @Autowired
    public setMovieCatalogs (Set<String, MovieCatelog> movieCatelogs) {
        this.movieCatelogs = movieCatelogs;
    }
}
  • 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用的@order注解,@order对List有效,对Map无效
  • @Autowired是由Spring BeanPostProcessor处理的,所以不能再自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@bean注解加载

3.7.12. @qualifier

  • 按类型自动装配可能多个bean实例的情况,可以使用Spring的@qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数
  • 可用于注解集合类型变量
  • 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@qualifier指定bean的名字),替代方式是使用JSR-250@Resource注解,它是通过其独特的名称来定义来识别特定的目标(这是一个与所声明的类型是无关的匹配过程)
  • 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的bean,为这些bean使用@resource注解,通过唯一名称引用集合或Map的bean
  • @Autowired适用于fields, constructors, multi-argument, methods这些允许在参数级别使用@qualifier注解缩小范围的情况
  • @resource适用于成员变量、只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方法是使用qualifiers

3.8. Spring Bean装配之基于Java的容器注解说明---@bean

  • @bean标识一个用于配置和初始化一个由Spring IoC容器管理的新对象的方法,类似于XML配置文件的
  • 可以在Spring的@component注解的类中使用@bean注解任何方法(仅仅是可以),创建对象并返回
  • 上一点中,通常使用的是@configuration
@Configuration
public class AppConfig {
    
    @Bean // 默认bean的id为方法的名称,或者@Bean(name="   ")
    // 还可以定义初始化方法和销毁方法如:@Bean(name="   ", initMethod="", destroyMethod="") 在MyServiceImpl类中定义了实现
    public MyService myService() {
        return new MyServiceImpl();
    }
}

相当于

<beans>
    <bean id="myService" class="com.aa.services.MyServiceImpl" />
</beans>

此外还有,引入资源文件初始化值@ImportResource:

@Configuration
@ImportResource("classpath:/com/aa/properties-config.xml")
public class AppConfig {
    
    @Value("$(jdbc.url)")
    private String url;

    @Value("$(jdbc.username)") // 注意xml中username不可以是“username”,否则会直接访问当前主机的系统名称,建议改成jdbc.username
    private String username;

    @Value("$(jdbc.password)")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}
  • 默认@bean是单例的
    可以使用scope改变
    @scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)

3.9. Spring Bean装配之Spring对JSR支持的说明

3.9.1. @resource

  • Spring还支撑使用JSR-250@Resource注解的变量或setter方法,这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持这种模式
  • Resource有一个name属性,并且默认Spring解释该值为被注入bean的名称
public class SimpleMovieLister {
    
    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder")
    public void  setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
  • 如果没有显式地指定@resource的name,默认的名称是从属性名或者setter方法得出
  • 注解提供的名字呗解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的

3.9.2. @PostConstruct and @PreDestroy

  • CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的

3.9.3. 使用JSR330标准注解

  • 从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致
  • 使用JSR330需要依赖javax.inject包
    使用Maven引入方式
3.9.3.1. @Inject
3.9.3.2. @nAmed

4. AOP

4.1. AOP概述

4.1.1. 什么是AOP

  • AOP: Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等

4.1.2. AOP实现方式

  • 预编译
    -- AspectJ
  • 运行期动态代理(JDK动态代理、CGLib动态代理)
    -- SpringAOP、JbossAOP
    qq20180519-193336
    qq20180519-193807
    qq20180519-193830
    qq20180519-194324

4.1.3. Spring框架中AOP的用途

  • 提供了声明式的企业服务,特别是EJB的替代服务的声明
  • 允许用户定制自己的方面,以完成OOP与AOP的互补使用

4.1.4. Spring的AOP实现

  • 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
  • 目前只支持方法执行连接点(通知Spring Bean的方法执行)
  • 不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题
  • Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案

4.1.5. 有接口和无接口的Spring AOP实现区别

  • Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
  • Spring AOP也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)

4.2. Schema--based AOP(基于配置的AOP实现)

Spring所有的切面和增强器都必须放在一个aop:config内(可以配置包含多个aop:config元素),每一个aop:config可以包含pointcut,advisor和aspect元素(它们必须按照这个顺序进行声明)
aop:config风格的配置大量使用了Spring的自动代理机制。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut expression="execution(* com.xyz.service.AccountService.*(..)) id="myPointcut" />
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

pointcut

  • execution(public * *(..)) // execution用于匹配方法执行的连接点 // 切入点为执行所有public方法时
  • execution(* set*(..)) // 切入点为执行所有set开始的方法时
  • execution(* com.xyz.service.AccountService.*(..)) // 切入点为执行AccountService类中的所有方法时
  • execution(* com.xyz.service..(..)) // 切入点为执行com.xyz.service包下所有方法时
  • execution(* com.xyz.service...(..))// 切入点为执行com.xyz.service包及其子包下的所有方法时
  • within(com.xyz.service.*) ( only in Spring AOP ) // within用于匹配指定类型内的方法
  • within(com.xyz.service..*) ( only in Spring AOP ) //
  • this(com.xyz.service.AccountService) ( only in Spring AOP ) // this用于匹配当前AOP代理对象类型的执行方法
  • target(com.xyz.service.AccountService) ( only in Spring AOP ) // target用于匹配当前目标对象类型的执行方法
  • args(java.io.Serializable) ( only in Spring AOP ) // args用于匹配当前执行的方法传入的参数为指定类型的执行方法
  • @target(org.springframework.transaction.annotation.Transactional) ( only in Spring AOP )
  • @Within(org.springframework.transaction.annotation.Transactional) ( only in Spring AOP )
  • @annotation(java.io.Serializable) ( only in Spring AOP )
  • @Args(com.xyz.security.Classified) ( only in Spring AOP )
  • bean(tradeService) ( only in Spring AOP )
  • bean(*Service) ( only in Spring AOP )

Before advice

<aop:aspect id="beforeExample" ref="aBean">
        <aop:before pointcut-ref="dataAccessOperation" method="doAccessCheck" />
</aop:aspect>
<aop:aspect id="beforeExample" ref="aBean">
        <aop:before pointcut="execution(* com.xyz.myapp.dao..(..))" method="doAccessCheck" />
</aop:aspect>
<aop:config id="AspectAOP" ref="Aspect">
    <aop:aspect id="AspectAOP" ref="Aspect">
        <aop:pointcut expression="execution(* com.ss.biz.*Biz *(..)) id="pointcut" />
        <aop:before method="before" pointcut-ref="pointcut" />
        <aop:after-returning method="after-returning" pointcut-ref="pointcut" /> // 正常返回时,和后一个只有一个会执行
        <aop:after-throwing method="after-throwing" pointcut-ref="pointcut" />  // 异常返回时,和前一个只有一个会执行
        <aop:after method="after" pointcut-ref="pointcut" /> // 返回时做的最后一件事,无论是否正常返回
        <aop:around method="around" pointcut-ref="pointcut" /> // 必须
    <aop:aspect>
</aop:aspect>

Introductions

  • 简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象
  • 由aop:aspect中的aop:declare-parents元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此得名)

schema-defined aspects 只支持singleton model

advisors

  • advisor就像一个小的自包含的方面,只有一个advice
  • 切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切点表达式
  • Spring通过配置文件中aop:advisor元素支持advisor实际使用中,大多数情况下它会和transactional advice配合使用
  • 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序

4.3. Spirng AOP API