Spring学习笔记 一

概述

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

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

特点

控制反转 IOC

学习Spring时,IOC和DI的特性大家铭记于心。
那么到底什么是控制反转呢?控制反转,反转的是什么?

其实说白了,就是将对象的维护权交给Spring容器去管理,反转的是对象的生命周期。

开发者无需手动创建对象,而是交给Spring管理,需要对象时,从Spring的容器中拿就行了。

之前一直讲针对接口编程,而非针对实现。

让Spring管理对象可以在程序上很好的解耦。

例如:接口和实现类,当我们手动new实现类时,程序就依赖于实现类了。
我们应该在容器中配置具体的实现类,让Spring去帮我们维护对象,而不是在程序中硬编码。

依赖注入 DI

依赖注入就是控制反转的一种技术实现,只是Spring觉得这个名字描述的更恰当而已。

什么是依赖?

Web项目中采用分层的思想:控制层、业务层、持久层。

Controller需要依赖于Service,Service依赖于Dao。

我们声明的永远是接口,并没有实例化实现类。
就是通过@Autowired让Spring去帮我们注入依赖对象的。

大型项目中,类大多数是一起协同工作,类之间会相互依赖,Spring会帮我们自动注入依赖的对象,而不用开发者手动实例化对象,这就是依赖注入。

面向切面 AOP

面向切面弥补了面向对象的不足。

当我们要在不修改原代码情况下,对原有的系统做增强时,就可以利用AOP来实现。

分离业务代码和非业务代码。

例如:我们需要打印方法运行的开始和结束日志时。

容器

Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。

BeanFactory

这是一个最简单的容器,主要的功能是为依赖注入 (DI) 提供支持。

常用的就是XmlBeanFactory,新版本的Spring已经将其废弃。

BeanFactory 创建时,不会立刻创建Bean实例,只有在getBean()时才会创建,延迟了对象的实例化。
节省了系统的资源,系统资源不紧张的情况下,建议使用ApplicationContext。

XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/beanFactory.xml"));
Person person = (Person) beanFactory.getBean("PersonBean");

ApplicationContext

ApplicationContext是Spring中较高级的容器。

ApplicationContext 包含 BeanFactory 所有的功能,并在其基础上针对企业做了一些加强。

常用的ApplicationContext实现:

  • ClassPathXmlApplicationContext

    常用。从 CLASSPATH 中加载 bean 配置文件。

  • FileSystemXmlApplicationContext

    不常用。给出绝对路径,从磁盘加载配置文件。

如果配置的bean的scope是单例的,那么ApplicationContext容器创建时,会直接实例化所有对象。

如果配置是多例的,则会每次getBean()时才实例化对象,且返回的都是新对象。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/beanFactory.xml");
Person person = (Person) applicationContext.getBean("PersonBean");

Bean的定义

属性 描述
class Bean的全限定类名
name/id Bean的唯一标识符,通过getBean(name)获取
scope 对象作用域,单例多例等
constructor-arg 通过构造函数注入
autowire 自动装配
properties 通过setter方法注入
lazy-init 是否延迟实例化
init-method 实例化后调用的方法
destroy-method 销毁后调用的方法

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true" init-method="created" destroy-method="destroy">
        <property name="name">
            <value>小狗钱钱value>
        property>
    bean>

beans>

scope作用域

Spring对于bean默认的scope是单例(singleton)的。

header 1 header 2
singleton 单例的
prototype 多例的,每次获取都会创建新对象
request 作用域限于每次Http请求
session 同一个Session共享一个bean
global-session 仅适用于WebApplicationContext环境

注:这里单例和设计模式中的单例模式是两回事。单例指的是对象在Spring容器中,只会存在一个对象,但是客户端仍然可以手动的创建对象实例。

生命周期

init-method和destroy-method分别会在对象创建时和销毁时触发。

AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/beanFactory.xml");
Person person = (Person) applicationContext.getBean("PersonBean");
System.out.println(person);
//关闭IOC容器
applicationContext.registerShutdownHook();

输出如下:
Person初始化
Person创建完毕
Person{
     name='小狗钱钱'}
Person销毁

Bean后置处理器

Spring允许在实例化对象前后对bean进行额外的处理。

BeanPostProcessor 接口定义回调方法,通过实现它,并注入到Spring容器,Spring会在实例化对象前后调用增强方法。

public class PersonPostProcessor implements BeanPostProcessor {
     

	@Override
	public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
     
		System.out.println("before");
		return o;
	}

	@Override
	public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
     
		System.out.println("after");
		return o;
	}
}

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true" init-method="created" destroy-method="destroy">
        <property name="name">
            <value>小狗钱钱value>
        property>
    bean>
     
    <bean class="com.ch.entity.PersonPostProcessor" />
beans>

Bean继承

Bean的配置可以继承,子Bean可以继承父Bean的配置。

需要注意的是Spring中Bean的继承和Java的继承毫无关系,Bean继承只是为了简化同样的配置,减少重复配置。

@Data
public class Man {
     
	private String name;
	private int age;
}

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true">
        <property name="name">
            <value>小狗钱钱value>
        property>
    bean>

    
    <bean class="com.ch.entity.PersonPostProcessor" />

    
    <bean id="manBean" class="com.ch.entity.Man" parent="PersonBean">
        <property name="age">
            <value>18value>
        property>
    bean>
beans>

manBean会继承父类的name属性配置:“小狗钱钱”。

依赖注入

一个复杂的程序通常由很多类组成,类与类之间可能存在依赖关系。

Spring可以帮我们完成依赖对象的注入。

通过Spring来管理依赖对象,而不是硬编码写在Java代码中,可以很好的解耦。

注入的方式有两种:构造器和setter方法。

构造器注入

public class Inner {
	public void inner(){
		System.out.println("inner");
	}
}

@Data
public class Outer {
	private Inner inner;

	public Outer(Inner inner) {
		this.inner = inner;
	}
}

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="outer" class="com.ch.entity.Outer">
        <constructor-arg name="inner" ref="inner" />
    bean>
    <bean id="inner" class="com.ch.entity.Inner"/>
beans>
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/DI.xml");
Outer outer = (Outer) applicationContext.getBean("outer");
outer.getInner().inner();

输出如下:
inner

setter注入


<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="outer" class="com.ch.entity.Outer">
        
        
        
        <property name="inner" ref="inner"/>
    bean>
    <bean id="inner" class="com.ch.entity.Inner"/>
beans>

内部bean注入




    
        
        
        
        
        
            
        
    
    

注入集合

除了注入单个对象外,有时还需注入集合。

@Data
public class Outer {
	private List list;
	private Set set;
	private Map map;
	private Properties properties;
}

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="outer" class="com.ch.entity.Outer">
        <property name="list">
            <list>
                <value>adminvalue>
                <value>rootvalue>
            list>
        property>
        <property name="set">
            <set>
                <value>adminvalue>
                <value>rootvalue>
            set>
        property>
        <property name="map">
            <map>
                <entry key="name" value="admin"/>
                <entry key="age" value="18"/>
            map>
        property>
        <property name="properties">
            <props>
                <prop key="name">adminprop>
                <prop key="age">18prop>
            props>
        property>
    bean>
beans>
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/collection.xml");
Outer outer = (Outer) applicationContext.getBean("outer");
System.out.println(outer.getList());
System.out.println(outer.getSet());
System.out.println(outer.getMap());
System.out.println(outer.getProperties());

输出如下:
[admin, root]
[admin, root]
{name=admin, age=18}
{age=18, name=admin}

自动装配

除了通过构造器和setter手动的注入对象外,Spring还支持自动装配。

自动装配的方式有两种:byName和byType。

byName通过bean的name去自动装配,byType通过bean的类型去装配。

当接口有多个实现类时,通过byType装配会报错,因为Spring不知道该装配哪一个实现类。

@Data
public class AutoWire {
	private Inner inner;
}

<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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="autoWire" class="com.ch.entity.AutoWire" autowire="byType"/>

    <bean id="inner" class="com.ch.entity.Inner"/>
beans>

基于注解注入

Spring2.5开始支持使用注解来注入对象,简化了xml的配置。

@Autowired

通过对象类型自动注入–byType。

@Data
public class AnnotationWire {
     
	@Autowired
	private Inner inner;
}

<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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    
    <context:annotation-config/>
    <bean id="autoWire" class="com.ch.entity.AnnotationWire"/>

    <bean id="inner" class="com.ch.entity.Inner"/>
beans>

@Qualifier

同一个类型我们有时可能会在Spring中配置多个对象。

@Autowired是通过类型来进行装配的,当同一个类型扫描到多个对象时,这时Spring就不知道该注入哪一个了。

可以通过@Qualifier制定bean的name或id,来指定装配的具体bean。

@Qualifier("inner1")
@Qualifier("inner2")

@Resource

Spring还使用基于 JSR-250 注解。

如果觉得使用@Autowired+@Qualifier麻烦,那么可以使用@Resource。

@Data
public class AnnotationWire {
     
	@Resource(name = "inner")
	private Inner inner;
}

基于Java配置

目前为止,所有的bean配置都是基于xml文件的,大型项目往往会滥用xml文件,导致不好管理。

除了xml文件配置容器外,Spring还支持Java代码来配置。

@Configuration

带有 @Configuration 的注解类表示这个类可以使用 Spring IOC 容器作为 bean 定义的来源。

@Bean

@Bean 注解告诉 Spring 将返回对象注册到当前应用程序上下文中的bean。

@Configuration
public class MyConfig {
     

	@Bean
	public Inner inner(){
     
		return new Inner();
	}
}

@Test
public void annotationConfigApplicationContext(){
     
	ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
	Inner bean = applicationContext.getBean(Inner.class);
	bean.inner();
}

输出如下:
inner

@Import

当有多个@Configuration配置类时,我们可以在主配置类上 加@Import注解,将其他配置类引入进来。

@Configuration
@Import(ConfigA.class)
public class ConfigB {
     

}

@Scope

指定Bean的作用域。

@Bean
@Scope("prototype")
public Inner inner(){
     
	return new Inner();
}

事件监听

Spring 的核心是 ApplicationContext,它负责管理Beans。

当加载Bean时,ApplicationContext会发布某些类型事件,当Bean实现了ApplicationListener接口,那么当ApplicationEvent发布到ApplicationContext上时,该bean就会被通知。

我没有看源码,但是我猜测用到的应该是 “观察者模式”。

内置事件

事件 描述
ContextRefreshedEvent 容器初始化或刷新时,该事件被发布。
ContextStartedEvent 容器启动时,该事件被发布。
ContextStoppedEvent 容器停止时,该事件被发布。
ContextClosedEvent 容器关闭时,该事件被发布。
RequestHandledEvent web-specific 事件

编写监听的bean

public class RefreshedEvent implements ApplicationListener<ContextRefreshedEvent> {
     
	public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
     
		System.out.println("contextRefreshedEvent");
	}
}

public class StartedEvent implements ApplicationListener<ContextStartedEvent> {
     

	public void onApplicationEvent(ContextStartedEvent contextStartedEvent) {
     
		System.out.println("contextStartedEvent");
	}
}

public class StoppedEvent implements ApplicationListener<ContextStoppedEvent> {
     
	public void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {
     
		System.out.println("contextStoppedEvent");
	}
}

public class ClosedEvent implements ApplicationListener<ContextClosedEvent> {
     
	public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
     
		System.out.println("ContextClosedEvent");
	}
}

配置类

@Configuration
public class MyConfig {
     

	@Bean
	public Inner inner(){
     
		return new Inner();
	}

	@Bean
	public ApplicationListener start(){
     
		return new StartedEvent();
	}

	@Bean
	public ApplicationListener stop(){
     
		return new StoppedEvent();
	}

	@Bean
	public ApplicationListener refresh(){
     
		return new RefreshedEvent();
	}

	@Bean
	public ApplicationListener close(){
     
		return new ClosedEvent();
	}
}
@Test
public void annotationConfigApplicationContext(){
     
	ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
	applicationContext.start();
	Inner bean = applicationContext.getBean(Inner.class);
	bean.inner();
	applicationContext.stop();
	applicationContext.close();
}

输出如下:
contextRefreshedEvent
contextStartedEvent
contextStoppedEvent
inner
ContextClosedEvent

自定义事件

自定义的事件比较复杂。

需要自己实现事件处理器、事件发布。

public class CustomEvent extends ApplicationEvent {
     
	public CustomEvent(Object source) {
     
		super(source);
	}
}

public class CustomEventHandler implements ApplicationListener<CustomEvent> {
     

	public void onApplicationEvent(CustomEvent customEvent) {
     
		System.out.println("customEvent 自定义事件");
	}
}

public class CustomEventPublisher implements ApplicationEventPublisherAware {
     
	private ApplicationEventPublisher publisher;

	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
     
		this.publisher = applicationEventPublisher;
	}

	public void publish() {
     
		CustomEvent ce = new CustomEvent(this);
		publisher.publishEvent(ce);
	}
}

@Configuration
public class CustomEventConfig {
     

	@Bean
	public ApplicationListener eventHandler(){
     
		return new CustomEventHandler();
	}

	@Bean
	public ApplicationEventPublisherAware eventPublisher(){
     
		return new CustomEventPublisher();
	}
}
@Test
public void customEvent(){
     
	ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomEventConfig.class);
	CustomEventPublisher bean = applicationContext.getBean(CustomEventPublisher.class);
	bean.publish();
}

输出如下:
customEvent 自定义事件

你可能感兴趣的:(后端,spring)