Spring4 (1) 基础入门

216 阅读6分钟

1. 概念入门

概念: Spring是2003年兴起的一个轻量级的JAVA开源框架,核心特性是控制反转和面向切面编程:

  • 历史发展:
    • Spring1.x时代只能使用xml配置bean,随着项目扩大,bean和xml配置的数量不断增多,二者之间切换频繁。
    • Spring2.x时代支持使用JDK5带来的注解配置bean,减少了xml配置代码,简化项目。
    • Spring3.x时代开始支持使用纯java代码配置bean,SpringBoot推荐此种方案。
    • 总结:基本配置如数据源建议使用可读性更高的XML配置,开发相关如Service注入Dao等建议使用更简单的注解配置。
  • 控制反转IOC:Inversion of Control,就是将实例化过程和实例生命周期管理都交给spring容器,以解决手动new实例时,侵入性和耦合度双高的问题。
  • 依赖注入DI:depend injection,在IOC产生实例的同时注入属性。
  • 面向切面AOP:Aspect Oriented Programming,就是通过预编译的方式在运行期使用动态代理实现的一种技术,主要作用于控制事务的传播,如一套下单事务中:
    • orderDao.insert(order);:添加订单后,事务应该向下传播。
    • detailDao.insert(detail);:添加明细后,事务不该向下传播。
    • logDao.insert(log);:无论添加成功该是失败,都应该记录日志。
  • 官方下载:Spring官方下载地址,建议下载 4.3.14.RELEASE-dist 版本:
    • Framework5.x 支持 JDK8+
    • Framework4.x 支持 JDK6+
    • Framework3.x 支持 JDK5+

spring产品图解

2. 基础配通

流程:

  • 引入最简依赖:
    • spring-core:核心包,被很多其他spring模块引用。
    • spring-beans:管理java类的包。
    • spring-context:Spring上下文包。
    • spring-context-support:Spring上下文包拓展包。
    • spring-expression:SpEL表达式语言包。
  • 在classpath下开发主配文件:app-start.xml
    • 实例化spring容器(鱼缸)的时候需要加载该主配文件。
    • 头信息在 7.2.1 Configuration metadata
  • 开发实体类:Student.java
    • 埋一个 name 属性并配置set()/get()。
  • 在主配文件中管理实体类:使用 <bean> 标签来关联实体类(鱼):
    • <bean> 中使用 id 属性设置唯一标识,可以随意命名。
    • <bean> 中使用 class 属性关联Student实体类的类全名。
  • 测试:
    • new ClassPathXmlApplicationContext():指定主配文件位置以造鱼缸。
    • getBean():通过id的方式捞出对应Student实例类的那条鱼。
    • setName()/getName():使用这条鱼。
    • close():砸鱼缸以释放资源。

若主配文件不在 classpath 下,则替换使用 FileSystemXmlApplicationContext 容器,加载路径从项目出发。

  • res: pom.xml
    <properties>
        <spring>4.3.14.RELEASE</spring>
    </properties>
    
       <dependencies>
        <!--spring-core-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring}</version>
        </dependency>

        <!--spring-beans-->
        <dependency>
            <groupId>org.springframework
            </groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring}</version>
        </dependency>

        <!--spring-context-->
        <dependency>
            <groupId>org.springframework
            </groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring}</version>
        </dependency>
        
        <!--spring-context-support-->
        <dependency>
            <groupId>org.springframework
            </groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring}</version>
        </dependency>

        <!--spring-expression-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring}</version>
        </dependency>
        
         <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <optional>true</optional>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
 </dependencies>
  • res: classpath:spring/start/app-classpath.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.yap.start.pojo.Student"/>
</beans>
  • res: app-file-system.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentE" class="com.yap.start.pojo.Student"/>
</beans>
  • src: c.y.start.pojo.Student
/**
 * @author yap
 */
@Data
public class Student implements Serializable {
    private String name;
}
  • tst: c.y.start.StudentTest.classPathApp()
/**
 * @author yap
 */
public class StudentTest {

    private ClassPathXmlApplicationContext app;

    @Test
    public void classPathApp() {
        // "classpath:" Can be omitted
        app = new ClassPathXmlApplicationContext("classpath:spring/start/app-classpath.xml");
        Student student = (Student) app.getBean("student");
        student.setName("赵四");
        System.out.println(student.getName());
    }

    @After
    public void after(){
        app.close();
    }
}
  • tst: c.y.start.StudentTest.fileSystemApp()
    @Test
    public void fileSystemApp() {
        FileSystemXmlApplicationContext app;
        app = new FileSystemXmlApplicationContext("app-file-system.xml");
        Student studentE = (Student) app.getBean("studentE");
        studentE.setName("studentE");
        System.out.println(studentE.getName());
    }

3. 容器的多配置

概念: 同时读取多个配置来实例化spring容器有两种方式:

  • 使用 ClassPathXmlApplicationContext(String... configLocations) 构造。
  • 在一个主配文件中使用 <import resource=""> 引入另一个主配文件。

源码: /spring4/

  • res: classpath:spring/start/multiple-a.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentA" class="com.yap.start.pojo.Student"/>
</beans>
  • res: classpath:spring/start/multiple-b.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentB" class="com.yap.start.pojo.Student"/>
</beans>
  • res: classpath:spring/start/multiple-c.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="classpath:spring/start/multiple-d.xml"/>
    <bean id="studentC" class="com.yap.start.pojo.Student"/>
</beans>
  • res: classpath:spring/start/multiple-d.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentD" class="com.yap.start.pojo.Student"/>
</beans>
  • src: c.y.start.pojo.Student
/**
 * @author yap
 */
@Data
public class Student implements Serializable {
    private String name;
}
  • tst: c.y.start.StudentTest.loadMultipleXmlByArray()
    @Test
    public void loadMultipleXmlByArray() {
        app = new ClassPathXmlApplicationContext("spring/start/multiple-a.xml", "spring/start/multiple-b.xml");
        Student studentA = (Student) app.getBean("studentA");
        Student studentB = (Student) app.getBean("studentB");
        studentA.setName("studentA");
        System.out.println(studentA.getName());
        studentB.setName("studentB");
        System.out.println(studentB.getName());
    }
  • tst: c.y.start.StudentTest.loadMultipleXmlByImport()
    @Test
    public void loadMultipleXmlByImport() {
        app = new ClassPathXmlApplicationContext("spring/start/multiple-c.xml");
        Student studentC = (Student) app.getBean("studentC");
        Student studentD = (Student) app.getBean("studentD");
        studentC.setName("studentC");
        System.out.println(studentC.getName());
        studentD.setName("studentD");
        System.out.println(studentD.getName());
    }

4. bean的获取方式

概念: 通过 idname类.class 都可以获取到bean:

  • id 不允许重复,使用 id 来获取bean是相对比较规范的一种方式。
  • name 可以重复但会发生覆盖,若和 id 同时存在,则 name 退化成别名。
  • 类.class 可以省略掉强转这一步骤。

源码: /spring4/

  • res: classpath:spring/start/get-by-id.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentF" class="com.yap.start.pojo.Student"/>
</beans>
  • res: classpath:spring/start/get-by-name.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="admin" class="com.yap.start.pojo.Student"/>
</beans>
  • res: classpath:spring/start/get-by-class.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yap.start.pojo.Student"/>
</beans>
  • src: c.y.start.pojo.Student
/**
 * @author yap
 */
@Data
public class Student implements Serializable {
    private String name;
}
  • tst: c.y.start.StudentTest.getBeanById()
    @Test
    public void getBeanById() {
        app = new ClassPathXmlApplicationContext("spring/start/get-by-id.xml");
        Student student = (Student) app.getBean("studentF");
        student.setName("studentF");
        System.out.println(student.getName());
        app.close();
    }
  • tst: c.y.start.StudentTest.getBeanByName()
    @Test
    public void getBeanByName() {
        app = new ClassPathXmlApplicationContext("spring/start/get-by-name.xml");
        Student student = (Student) app.getBean("admin");
        student.setName("admin");
        System.out.println(student.getName());
        app.close();
    }
  • tst: c.y.start.StudentTest.getBeanByClass()
    @Test
    public void getBeanByClass() {
        app = new ClassPathXmlApplicationContext("spring/start/get-by-class.xml");
        Student student = app.getBean(Student.class);
        student.setName("student");
        System.out.println(student.getName());
        app.close();
    }

5. bean的创建方式

概念: spring容器支持通过无参构造,静态工厂或实例工厂的方式来创建bean:

  • 无参构造:spring容器默认调用类的无参构造器进行实例化过程,工作中常用:
    • 只能调用无参构造,无法调用有参构造。
  • 静态工厂:在静态工厂方法中手动实例化指定类并返回,然后由spring容器来调用该方法即可,静态方法速度快但会有线程安全问题:
    • <bean class="静态工厂类全名" factory-method="静态工厂方法名"/>
  • 实例工厂:在实例工厂方法中手动实例化指定类并返回,然后由spring容器来调用该方法即可,实例方法速度慢但没有线程安全问题:
    • <bean id="实例工厂ID" class="实例工厂类全名"/>
    • <bean factory-bean="实例工厂ID" factory-method="实例工厂方法名"/>

源码: /spring4/

  • res: classpath:spring/ioc/build-by-constructor.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yap.start.pojo.User"/>
</beans>
  • res: classpath:spring/ioc/build-by-static-factory.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yap.ioc.factory.UserFactory" factory-method="getUserByStaticMethod"/>
</beans>
  • res: classpath:spring/ioc/build-by-dynamic-factory.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userFactory" class="com.yap.ioc.factory.UserFactory"/>
    <bean factory-bean="userFactory" factory-method="getUserByDynamicMethod"/>
</beans>
  • src: c.y.ioc.pojo.User
/**
 * @author yap
 */
public class User {

    public User() {
        System.out.println("User()...");
    }

    public User(String name) {
        System.out.println("User(String name)... " + name);
    }

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }
}
  • src: c.y.ioc.factory.UserFactory
/**
* @author yap
*/
public class UserFactory {

    public UserFactory() {
        System.out.println("UserFactory()...");
    }

    public static User getUserByStaticMethod() {
        return new User("static");
    }

    public User getUserByDynamicMethod() {
        return new User("dynamic");
    }
}
  • tst: c.y.ioc.UserTest.buildByConstructor()
/**
 * @author yap
 */
public class UserTest {

    private ClassPathXmlApplicationContext app;

    @Test
    public void buildByConstructor() {
        app = new ClassPathXmlApplicationContext("spring/ioc/build-by-constructor.xml");
        app.getBean(User.class);
    }

    @After
    public void after() {
        app.close();
    }
}
  • tst: c.y.ioc.UserTest.buildByStaticFactory()
    @Test
    public void buildByStaticFactory() {
        app = new ClassPathXmlApplicationContext("spring/ioc/build-by-static-factory.xml");
        app.getBean(User.class);
    }
  • tst: c.y.ioc.UserTest.buildByDynamicFactory()
    @Test
    public void buildByDynamicFactory() {
        app = new ClassPathXmlApplicationContext("spring/ioc/build-by-dynamic-factory.xml");
        app.getBean(User.class);
    }

6. bean的加载机制

概念: <bean>lazy-init 属性可以调整该bean的加载机制:

  • false:非延迟加载,默认值,表示该bean在spring容器初始化时被加载和实例化。
  • true:延迟加载,也叫懒加载,表示该bean在spring容器调用 getBean() 时被加载和实例化。

源码: /spring4/

  • res: classpath:spring/ioc/lazy.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean lazy-init="false" class="com.yap.start.pojo.User"/>
</beans>
  • src: c.y.ioc.pojo.User
/**
 * @author yap
 */
public class User {

    public User() {
        System.out.println("User()...");
    }

    public User(String name) {
        System.out.println("User(String name)... " + name);
    }

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }
}
  • tst: c.y.ioc.UserTest.lazy()
    @Test
    public void lazy() {
        app = new ClassPathXmlApplicationContext("spring/ioc/lazy.xml");
        app.getBean(User.class);
    }

7. bean的作用范围

概念: <bean>scope 属性可以调整该bean的作用范围:

  • singleton:表示该bean使用单例模式,即每次从容器中获取到的都是同一个bean,默认值。
  • prototype:表示该bean使用原型模式,即每次从容器中获取到的都是新new出来的一个bean,该模式必须搭配延迟加载。

源码: /spring4/

  • res: classpath:spring/ioc/scope.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yap.start.pojo.User" scope="prototype" lazy-init="false"/>
</beans>
  • src: c.y.ioc.pojo.User
/**
 * @author yap
 */
public class User {

    public User() {
        System.out.println("User()...");
    }

    public User(String name) {
        System.out.println("User(String name)... " + name);
    }

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }
}
  • tst: c.y.ioc.UserTest.scope()
    @Test
    public void scope() {
        app = new ClassPathXmlApplicationContext("spring/ioc/scope.xml");
        User userA = app.getBean(User.class);
        User userB = app.getBean(User.class);
        System.out.println(userA.hashCode());
        System.out.println(userB.hashCode());
    }

8. bean的生命周期

概念: <bean>init-method 属性和 destroy-method 属性可以分别指定该bean的初始化方法和销毁方法。

源码: /spring4/

  • res: classpath:spring/ioc/life-cycle.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.yap.start.pojo.User" init-method="init" destroy-method="destroy"/>
</beans>
  • src: c.y.ioc.pojo.User
/**
 * @author yap
 */
public class User {

    public User() {
        System.out.println("User()...");
    }

    public User(String name) {
        System.out.println("User(String name)... " + name);
    }

    public void init(){
        System.out.println("init()...");
    }

    public void destroy(){
        System.out.println("destroy()...");
    }
}
  • tst: c.y.ioc.UserTest.lifeCycle()
    @Test
    public void lifeCycle() {
        app = new ClassPathXmlApplicationContext("spring/ioc/life-cycle.xml");
        app.getBean(User.class);
    }