Spring框架基础入门

一、Spring概述与环境搭建

Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。

Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。

Spring环境搭建

Spring5至少要JDK8,Servlet3.1,Tomcat8.5+。请在实际开发时针对自己的 开发环境,选择Spring版本,Spring4支持JDK6/7/8。

创建工程
配置pom.xml引入Spring依赖包

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>
    </dependencies>

测试是否正常运行
创建一个类

package pojo;

public class Master { 
public void sayHello(){ 
System.out.println("hello world"); } 
}

配置spring.xml配置文件

<bean id="master" class="pojo.Master"></bean> 

创建一个测试类

@Test 
public void testSpring() { 
ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml"); 
Master master = (Master)context.getBean("master"); master.sayHello(); 
}
//运行结果
//hello world

运行结果成功表示环境配置成功

集成Junit

	<!-- JUnit单元测试框架     --> 
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!--集成Junit-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.3.RELEASE</version>
    </dependency>

测试类代码

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunne r;
import pojo.TestBean;
import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring.xml"})
public class TestUnit {
    @Resource(name = "testBean")
    private TestBean p;

    @Test
    public void testCreatePerson() {
        System.out.println(p);
    }
}

二、控制反转IoC

在传统的方式中,如果我们要用到一个类的对象,需要自己new一个出 来,如:

    @Test
    public void testMaster() {
        Master master = new Master();
        master.sayHello();
    }

Master的对象master在testMaster方法中创建,这时master对象的控制权是属于testMaster的。而在Spring中,我们看不到new Master()的操作。而是通过Spring的ApplicationContext获得:

ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml"); 
//不是自己new的对象,而是从ApplicationContext中获得 Master master = (Master) context.getBean("master");

其中:

context.getBean("master") 

getBean方法的参数值master,对应的是spring.xml中的bean的id:

<bean id="master" class="pojo.Master"></bean> 

可见master对象是由Spring创建的,其控制权属于Sping而不属于使用者。 这种设计叫做“控制反转(Inversion of Control,英文缩写为IoC)”。

为什么要控制反转
IoC把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了。
IoC的缺点是,生成一个对象的步骤变复杂了,对于不习惯这种方式的人, 会觉得有些别扭和不直观。对象生成因为是使用反射编程,在效率上有些 损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的, 除非某对象的生成对效率要求特别高。

三、多种方式实现依赖注入

1、属性注入

属性注入是通过属性的setter方法进行注入,需要注入的属性必须有setter 方法且命名必须符合规范。如属性名是abc,那么setter方法应为setAbc()

基本数据类型
<property name="属性名" value="基本类型的属性值"/> 

示例:

<bean id="master" class="pojo.Master">
	<property name="name" value="张三"/>
</bean>
注入对象类型

需要先创建对象bean

<property name="属性名" ref="对象的bean的id"/>

示例:

<bean id="master" class="pojo.Master">
	<property name="pet" ref="pet"/> 
</bean>
注入集合和数组类型

list和数组注入方式相同

    <!--给数组或者List赋值-->
    <property name="stringList">
        <list>
            <!--普通类型-->
            <value>abc</value>
            <value>efg</value>
            <!--注入对象类型-->
            <!--<ref bean="其他bean的id"/>-->
        </list>
    </property>
    <!--注入Set类型-->
    <property name="stringSet">
        <set>
            <!--普通类型-->
            <value>abc</value>
            <value>abc2</value>
            <!--注入对象类型-->
            <!--<ref bean="其他bean的id"/>-->
        </set>
    </property>
    <!--注入Map-->
    <property name="objectMap">
        <map>
            <!--注入普通类型-->
            <entry key="a" value="aa"/>
            <!--注入对象类型-->
            <!--<entry key="b" value-ref="其它bean的id"/>-->
        </map>
    </property>
2、构造方法注入

Spring创建对象默认使用的是类的无参构造方法。所以在某个类添加带参构造函数的时候,记得将无参构造写出来,否则会导致某些情况下Spring无法创建对象。

按类型注入

constructor-arg标签用于给构造方法注入参数,type是参数类型,value是参数值。
对于有特殊字符的属性,在value子节点中只用CDATA。如示例中,注入的 String类型参数值为“”,其中<>是xml中的节点符号,属于特殊字符。
注入对象类型,使用ref引用其他bean的id。

<constructor-arg type="属性类型" value="基本类型属性值"/>
<constructor-arg type="属性类型" ref="对象的bean的id"/>

示例:

    <bean id="petBean" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <!--注入基本数据类型-->
        <constructor-arg type="java.lang.Integer" value="20"/> <!-- 对于包含特殊字符的属性值,可以在value子节点使用CDATA -->
        <constructor-arg type="java.lang.String">
            <value><![CDATA[<sansan>]]></value>
        </constructor-arg>
        <!--ref用于注入对象类型-->
        <constructor-arg type="pojo.Pet" ref="petBean"/>
    </bean>
按位置注入

如果构造方法里有多个参数的类型是相同的,可以使用按位置注入的方式
constructor-arg 的index属性用于指定构造方法中参数的索引,从0开始。
示例:

    <bean id="petBean" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <constructor-arg value="20" index="1"/>
        <constructor-arg value="张三" index="0"/>
        <constructor-arg ref="pet" index="2"/>
    </bean>
按名称注入(最常用)
    <bean id="pet" class="pojo.Cat"></bean>
    <bean id="master" class="pojo.Master">
        <!--注入基本数据类型-->
        <constructor-arg name="age" value="20"/>
        <!-- 对于包含特殊字符的属性值,可以在value子节点使用CDATA -->
        <constructor-arg name="name">
            <value><![CDATA[<sansan>]]></value>
        </constructor-arg>
        <!--ref用于注入对象类型-->
        <constructor-arg name="pet" ref="pet"/>
    </bean>
P命名空间注入

P命名空间注入需要先引入头文件:

xmlns:p="http://www.springframework.org/schema/p"

在标签上使用p:属性名=“属性值”进行值注入。使用p:属性名-ref=”bean的id” 进行对象注入。
示例:

<bean class="pojo.Master" id="master5" p:name="筑梦者" p:age="18" p:cat-ref="pet"/>
工厂方法注入

静态工厂注入
工厂bean的class都是工厂类型,但实际的bean的对象是通过factory- method获得的。getCat()和getDog()返回的都是Pet类型的对象。所以 catBean和dogBean都是Pet类型的。

PetShop类:

package pojo;

public class PetShop {
    public static Pet getDog() {
        return new Dog();
    }

    public static Pet getCat() {
        return new Cat();
    }

    public static Pet getPet(String name) {
        if (name == "cat") {
            return new Cat();
        } else {
            return new Dog();
        }
    }

配置文件写法

    <!--创建工厂bean,调用不带参数的方法-->
    <bean id="catBean" class="pojo.PetShop" factory- method="getCat"/>
    <!--创建工厂bean,调用带参数的方法-->
    <bean id="dogBean" class="pojo.PetShop" factory- method="getPet">
        <constructor-arg value="dog"/>
    </bean>
    <!--可以给master分别注入catBean和dogBean,看运行结果-->
    <bean id="master" class="pojo.Master">
        <property name="pet" ref="dogBean"/>
    </bean>

测试

@Test 
public void testFactory() { 
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); 
Master master = (Master) context.getBean("master"); master.getPet().shout(); 
}

实例工厂注入

实例工厂和静态工厂的区别在于实例工厂必须先初始化工厂对象,工厂中的方法没有static
示例:

package pojo;

public class PetShop {
    public Pet getDog() {
        return new Dog();
    }

    public Pet getCat() {
        return new Cat();
    }

    public Pet getPet(String name) {
        if (name == "cat") {
            return new Cat();
        } else {
            return new Dog();
        }
    }
} 

配置文件

    <bean id="shopBean" class="pojo.PetShop"></bean>
    <bean id="petBean" factory-bean="shopBean" factory- method="getCat"></bean>
    <bean id="master" class="pojo.Master">
        <property name="pet" ref="petBean"/>
    </bean>
使用注解

使用注解可以批量生成bean,扫描注解修饰的类。需要引入头文件。

xmlns:context="http://www.springframework.org/schema/context"
//不指定id,默认id是类名首字母小写 
@Component("person")
public class Person { 
@Qualifier("cat")//注入其它bean 
private Pet pet; 
//getter/setter方法略 
}

@Component注解修饰一个类,这个类会创建成bean。@Component(“bean 的id”),如果不指定id,默认的id是类名首字母小写。

@Qualifier注解用于注入其它bean(按bean的id注入)。

@Autowired注解是按类型注入,默认情况下它要求依赖对象必须存在,如 果允许null值,可以设置它的required属性为false。当有多个类型一样的 bean存在时,会出现异常。

@Resource注解是java的注解,spring提供了支持。它可以实现按名称注入 和按类型注入。

//@Resource(name = "cat")//按名称注入 
@Resource(type = Cat.class)//按类型注入 
private Pet pet;

@Qualifier, @Autowired, @Resource可以用在属性上,也可以放在setter方法 上。

其它注解:
@Controller 分层开发中用于修饰web层的类。

@Service 分层开发时用于修饰service层的类。

@Repository 分层开发时用于修饰dao层的类。

这三个注解都是对于@Component更具体的实现。

@PostConstruct 依赖注入成功后被调用。构造方法->@Autowire- >@PostConstruct

@PreDestroy 销毁前执行。

Spring4之后新增一系列注解可以代替xml文件。

import org.springframework.context.annotation.*;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import pojo.Cat;
import pojo.Dog;
import pojo.Pet;

@Configuration//相当于代替一个xml文件 
@ComponentScan(basePackages = "service",
        excludeFilters =
        @ComponentScan.Filter(Repository.class),
        includeFilters = @ComponentScan.Filter(Service.class), useDefaultFilters = false)
public class AppConfig {
    @Bean//默认单例 
    public Pet cat() {
        return new Cat();
    }

    @Bean
    @Scope("prototype")//每次都获得一个新的实例 
    public Dog dog() {
        return new Dog();
    }
} 

四、Bean的生命周期和作用域

单例Bean的生命周期

在这里插入图片描述
1、实例化一个Bean--也就是我们常说的new;

2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的 setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的 id值

4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身
(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一 个普通的Bean就可以);

5、 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
(同样这个方式也可以实现步骤4的内容,但比4更好,因为 ApplicationContext是BeanFactory的子接口,有更多的实现方法);

6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法, BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在 Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

7、 如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

8、如果这个Bean关联了BeanPostProcessor接口,将会调用 postProcessA

9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean 这个接口,会调用那个其实现的destroy()方法;

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

 

Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的 Bean时,都将产生一个新的Bean实例。Bean完全交给客户端管理,容器不在跟踪生命周期。
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用 Spring时,该作用域才有效
session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
application:web环境中,一个ServletContext生命周期中的单例对象。
websocket:WebSocket环境中使用。

其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成 prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实 例,然后返回给程序。在这种情况下,Spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

五、面向切面AOP

核心概念
a)、Aspect 切面,一个模块化的关注点,它横跨多个类。事务管理是企业 Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,切面通过使用常规类(基于模式的方法)或通过@Aspect注解修饰的类来实现。

b)、Join point 连接点。程序执行过程中的一个点,如在方法上进行统一的异 常处理。SpringAOP中,某个方法执行前、执行后、抛出异常后等一些具有 边界性质的特定点,称作连接点。

c)、Advice 增强。在一个特定切入点上采取的动作。

d)、Pointcut 切点。某个特定的连接点就是切点。

e)、Introduction 引介。引介是一种特殊的增强,它为类添加一些属性和方法. 这样,即使一个业务类原本没有实现某一个接口,通过AOP的引介功能,也可以 动态地为该业务类添加接口的实现逻辑.让业务类成为这个接口的实现类。

f)、Target object 目标对象。增强逻辑的织入目标类。如果没有AOP,那么目标 业务类需要自己实现所有逻辑,如果使用AOP可以把一些非逻辑性代码通过 AOP织入到主程序代码上。由于通过使用运行时代理来实现Spring AOP,所 以该对象始终是代理对象。

g)、AOP proxy AOP框架为了实现切面增强而创建的对象。在Spring框架中, AOP代理是JDK动态代理或CGLIB代理。

h)、Weaving 织入。织入是将增强添加到目标类具体链接点上的过程

Spring中的增强有五种:

1、前置增强(Before): 在方法执行前调用。
2、后置增强(AfterReturning): 在方法执行后调用(方法正常结束,没有异
常退出)。
3、异常抛出增强(AfterThrowing): 在方法抛出异常时调用。。
4、环绕增强(Around): 在方法执行前后都调用。。
5、最终增强(After): 在方法执行后执行(不管是正常退出还是异常退出)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章