初识spring


一、spring是什么

Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。

1、spring的主要功能:

1) 方便解耦,简化开发
Spring就像一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理
这里提及了耦合和解耦合的概念:什么是耦合和解耦合呢?

  1. 在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应使类和构件之间的耦合最小。
  2. 降低耦合度即可以理解为解耦,模块间有依赖关系必然存在耦合,理论上的绝对零耦合是做不到的,但可以通过一些现有的方法将耦合度降至最低。

2)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts2、Hibernate、MyBatis)的直接支持。

3)降低Java EE API 的使用难度
Spring对Java EE 开发中非常难用的一些API(JDBC、JavaMail、远程调用等)都提供了封装,使这些API应用的难度大大降低。

4)方便程序的测试
Spring支持JUnit4,可以直接通过注解方便的测试Spring程序

5)AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截和运行监控等功能。

6)声明事务的支持
只需要通过配置就可以完成对事务的管理,而不用我们去手动编程。

二、spring的核心模块

1、核心模块(Core Container)

spring的核心模块实现了IoC的功能,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述。由Ioc容器负责类的创建,管理,获取等。BaenFactory接口是spring框架的核心接口,是西安了容器很多核心的功能。

2、AOP模块

spring AOP模块提供了满足AOP Alliance规范的实现,还整合了AspectJ这种语言级框架。通过AOP能降低耦合。

3、数据访问集成模块

该模块包括了JDBC、ORM、OXM、JMS和事务管理:

  • 事务模块:该模块用于Spring管理事务,只要是Spring管理对象都能得到Spring管理事务的好处,无需在代码中进行事务控制了,而且支持编程和声明性的事务管理。

  • JDBC模块:提供了一个JBDC的样例模板,使用这些模板能消除传统冗长的JDBC编码还有必须的事务控制,而且能享受到Spring管理事务的好处。

  • ORM模块:提供与流行的“对象-关系”映射框架的无缝集成,包括hibernate、JPA、MyBatis等。而且可以使用Spring事务管理,无需额外控制事务。

  • OXM模块:提供了一个对Object/XML映射实现,将Java对象映射成XML数据,或者将XML数据映射成java对象,Object/XML映射实现包括JAXB、Castor、XMLBeans和XStream。

  • JMS模块:用于JMS(Java Messaging Service),提供一套 “消息生产者、消息消费者”模板用于更加简单的使用JMS,JMS用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

4、Web模块

该模块建立在ApplicationContext模块之上,提供了Web应用的功能,如文件上传、FreeMarker等。

5、测试模块

spring可以用非容器依赖的编程方式进行几乎所有的测试工作,支持Junit和TestNG等测试框架。

三、IoC容器和DI

IoC(控制反转:Inverse of Control): 是Spring容器的核心,但是IoC这个概念却比较晦涩,让人不太容易望文生义。

DI(依赖注入:Dependency Injection): 让第三方来实现注入,以移除我们类与需要使用的类之间的依赖关系。

区别: IoC是目的,DI是手段,创建对象的过程往往意味着依赖的注入。我们为了实现IoC,让生成对象的方式由传统方式(new)反转过来,需要创建相关对象时由IoC容器帮我们注入(DI)。

1、常见的几种属性注入方式

这里我先准备一个Student类和School类:

public class Student {

    private String name;
    private int age;
    private School school;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(String name, int age, School school) {
        System.out.println("====有参构造====");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
    
	@Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }

}
public class School {
    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
  • 有参构造方法注入,在bean标签中使用constructor-arg标签来进行有参方法的注入:
	<!--声明Student对象-->
	<bean id="myStudent" class="com.wenhua.ba05.Student" >
        <constructor-arg name="name" value="张三丰"/>
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>
  • 属性注入(set注入),在bean标签中使用property标签来进行有参方法的注入:
 <!--声明School对象-->
    <bean id="mySchool" class="com.wenhua.ba05.School">
        <property name="name" value="人民大学"/>
        <property name="address" value="海淀区"/>
    </bean>

在Student类中由name,age,school三个属性,其中school是引用类型。普通类型和引用类型使用的属性标签是不一样的:

 		1)简单类型的有参注入
                <bean id="xx" class="yyy">
                    <property name="属性名字" value="此属性的值"/>
                    一个property只能给一个属性赋值
                    <property ...>
                </bean>

        2)引用类型的有参注入
                <bean id="xx" class="yyy">
                    <property name="属性名称" ref="bean的id(对象名称)"/>
                </bean>

当该类的某一属性是简单类型或者String类型时,属性赋值使用的value。而当属性是简单类型或者String类型时,属性赋值是用的ref。

2、集合属性注入方式

这里先做了一个Collections类

public class Collections {
    private List<String> list;
    private Set<String> set;
    private Map<Integer,String> map;

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<Integer, String> map) {
        this.map = map;
    }
}

集合注入:

<bean id="aCollections" class="com.wenhua.ba02.Collections">
        <!--List注入,可以存在相同对象-->
        <property name="list">
            <list>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </list>
        </property>![在这里插入图片描述](https://img-blog.csdnimg.cn/ef26d69a232e4e13be04a3091db82fff.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAd2VuaHVhYnV6aGlqaWFu,size_13,color_FFFFFF,t_70,g_se,x_16#pic_center)

        
        <!--Set注入,不可以存在相同对象-->
        <property name="set">
            <set>
                <value>list1</value>
                <value>list2</value>
                <value>list3</value>
            </set>
        </property>
        
        <!--Map注入,只要map中的key值不一样就可以装配value-->
        <property name="map">
            <map>
                <entry key="1" value="map元素1" />
                <entry key="2" value="map元素1" />
            </map>
        </property>
    </bean>

四、Bean的作用域

在这里插入图片描述
这里来演示一下第一种(singleton)和第二种():

1、scope=“singleton”(默认):

//XML文件配置
<bean id="myStudent" class="com.wenhua.ba02.Student" scope="singleton">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
        <property name="school" ref="mySchool" />
    </bean>

//测试
@Test
    public void test02(){
        String config = "ba02/applicationContext.xml";
        //2.创建spring容器的对象,ApplicationContext
        //ApplicationContext就是表示spring容器,通过这个容器获取对象
        //ClassPathXmlApplicationContext表示从类路径种加载spring的配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

        //从容器中获取student对象
        Student myStudent1 = (Student) ac.getBean("myStudent");
        Student myStudent2 = (Student) ac.getBean("myStudent");
        System.out.println(myStudent1);
        System.out.println(myStudent2);
    }

/**Output
com.wenhua.ba02.Student@757942a1
com.wenhua.ba02.Student@757942a1
**/

从输出可以看出是同一个对象

2、scope=“prototype”:

//XML文件配置
<bean id="myStudent" class="com.wenhua.ba02.Student" scope="prototype">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
        <property name="school" ref="mySchool" />
    </bean>

//测试
@Test
    public void test02(){
        String config = "ba02/applicationContext.xml";
        //2.创建spring容器的对象,ApplicationContext
        //ApplicationContext就是表示spring容器,通过这个容器获取对象
        //ClassPathXmlApplicationContext表示从类路径种加载spring的配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

        //从容器中获取student对象
        Student myStudent1 = (Student) ac.getBean("myStudent");
        Student myStudent2 = (Student) ac.getBean("myStudent");
        System.out.println(myStudent1);
        System.out.println(myStudent2);
    }

/**Output
com.wenhua.ba02.Student@757942a1
com.wenhua.ba02.Student@4a87761d
**/

从输出可以看出是两个不一样的对象

五、生命周期

  1. 实例化BeanFactoryPostProcessor,调用postProcessBeanFactory()方法对工厂定义信息进行后处理。

  2. 调用者通过getBean向容器请求bean时,如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,会调用接口的postProcessBeforeInstantiation()方法。

  3. 根据配置文件调用Bean的构造方法或者工厂方法实例化Bean。

  4. 实例化Bean后,调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation()方法,在这里可以对已经实例化的对象进行一些修饰。

  5. 如果Bean配置了属性信息,在设置每个属性之前将先调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法。

  6. 调用Bean的属性设置方法为Bean设置属性。

  7. 如果Bean实现了BeanNameAware接口,会调用该接口的setBeanName()方法,将配置文件中该Bean对应的名称设置到Bean中。

  8. 如果Bean实现了BeanFactoryAware接口,会调用该接口的setBeanFactory方法,将BeanFactory容器实例设置到Bean中。

  9. 如果BeanFactory装配了BeanPostProcessor后处理器,将调用Object postProcessBeforeInitialization(Object bean,String beanName)方法对Bean进行加工操作,bean是当前处理的Bean,beanName是当前bean的配置名,返回的对象是加工处理后的Bean。用户可以使用该方法对Bean进行特殊处理。BeanPostProcessor在Spring框架中占有重要地位,AOP等都通过该BeanPostProcessor实施。

  10. 如果Bean实现了InitializingBean接口,会调用该接口的afterPropertiesSet()方法。

  11. 如果<bean>中配置了init-method属性,执行指定的初始化方法。

  12. 调用BeanPostProcessor后处理器的Object postProcessAfterInitialization(Object bean,String beanName)方法再次获得对Bean的加工处理机会。

  13. 将Bean返回给调用者。

  14. 当容器关闭时, 如果Bean实现了DisposableBean接口,调用接口的destory方法。

  15. 如果<bean>设置了destory-method属性,执行指定的销毁方法。

六、Bean的两种方式

我们前面的<bean>都是普通bean,Spring利用反射机制通过bean的class属性来实例化Bean。如果有的Bean属性特别多,我们就需要编写大量的配置信息。
Spring提供了一个FactoryBean接口。我们可以通过实现该接口来返回特定的Bean,该接口定义了三个方法:

  • T getObject():返回由FactoryBean创建的Bean实例。
  • boolean isSingleton():确定创建的Bean的作用域是singleton还是prototype
  • Class< ? > getObjectType():返回FactoryBean创建Bean的类型
// 我们创建一个类实现FactoryBean接口 
public class UserFactory implements FactoryBean<User> {

    //返回对象
    public User getObject() throws Exception {
        User user=new User();
        user.setAge(14);
        user.setName("tom");
        return user;
    }

    //返回bean的类型
    public Class<User> getObjectType() {
        return User.class;
    }

    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return false;
    }
}

//XML配置
<bean id="userfactory" class="com.wenhua.ba02.UserFactory"></bean>

//测试
@Test
public class Test {
    public void test(){
        ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
        User user=(User) ac.getBean("userfactory"); 
        user.say();
    }
}

/*output:
	tom:14 
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值