Spring之旅

三世轮回 提交于 2020-03-10 23:14:41

Spring之旅

一、简化java开发
  1. 创建Spring的主要目的就是用来代替更加重量级的企业级的java技术,尤其是EJB。
  2. 为了降低java开发的复杂性,Spring采用了以下四种关键策略:
    • 基于POJO的轻量级和最小侵入性编程。
    • 通过依耐注入和面向接口实现松耦合。
    • 基于切面和惯例进行声明式编程。
    • 通过切面和模版减少板式代码。
激发POJO的潜能
与其他框架不一样的是,Spring不会像其他框架一样,让你继承某个类或者实现某个接口从而导致应用与框架绑死。
依赖注入
DI帮助应用对象彼此之间保持松耦合。
  1. 在构造的时候把接口对象作为构造器参数传入,即构造器注入。可以利用mock来测试
  2. 示例:(使用xml来装载)
    • 创建两个接口,因为现在都是面向接口开发,所以我们顶层一般都是用接口来声明功能。
    package spring.com.one;
    
    /**
     * 将要注入的接口,即调用的方法
     */
    public interface Quest {
    		void embark();
    }
    
    package spring.com.one;
    
     /**
      * 被实现的接口
      */
     public interface Knight {
     		//开始行动
     		void embarkOnQuest();
     }
    
    
    • 创建一个实现类BravenKnight用来实现Knight接口,这个类将会被注入Quest对象,
    package spring.com.one;
    
    /**
     * 将Quest接口对象注入到BravenKnight类中。
     * 1.声明一个注入接口类型的字段
     * 2. 在构造方法中设置入参为注入接口类型的参数
     * 3.将该参数赋值给声明字段
     */
    public class BravenKnight implements  Knight {
    	private Quest quest;
    	public BravenKnight(Quest quest){
    			this.quest=quest;
    	}
    
    	public void embarkOnQuest(){
    			quest.embark();
    	}
    }
    
    • 创建一个实现类SlayDragonQuest用来实现Quest接口,该对象将被注入PrintStream对象。
    package spring.com.one;
    
     import java.io.PrintStream;
    
     public class SlayDragonQuest implements Quest {
    
    		private PrintStream stream;
    		public  SlayDragonQuest(PrintStream stream){
    				this.stream=stream;
    		}
    
    		public void embark() {
    				stream.println("安怡宁爱学习");
    		}
     }
    
    
    • 创建一个Spring的配置文件,将PrintStream对象注入到SlayDragonQuest类中装配成名字为quest的bean,然后再将quest注入到对象为BravenKnight类中,bean的名字为knight。
    <?xml version="1.0" encoding="UTF-8"?>
    <!--xmlns:表示命名空间,允许你通过一个网址指向来识别你的标识.
    xmlns:xsi:遵守xml规范
    xsi:是指具体用到的schema资源,schema:文档的限制。
    -->
    <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为quest注入到BravenKnight中-->
        <bean id="knight" class="spring.com.one.BravenKnight">
            <constructor-arg ref="quest"/>
        </bean>
    <!--    将PrintStream对象注入到SlayDragonQuest类中-->
        <bean id="quest" class="spring.com.one.SlayDragonQuest"><!--这里的class会被标红,但是不影响程序运行,应该是版本的问题-->
            <constructor-arg value="#{T(System).out}"/>
        </bean>
    </beans>
    

3.下面我们用java的方式装配bean(@Bean注解会自动把方法的名字设置为bean的名字)

package spring.com.one;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class KnightConfig {

		@Bean
		public Knight knight() {
				return new BravenKnight(quest());
		}

		@Bean
		public Quest quest() {
				return new SlayDragonQuest(System.out);
		}
}
  • 测试()
	@Test
		public void KnightMain() {
		//xml配置测试
				ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("one/oneConfig.xml");
				Knight knight = context.getBean(Knight.class);
				knight.embarkOnQuest();
				context.close();
		//java注解配置测试	
				AnnotationConfigApplicationContext context1=new AnnotationConfigApplicationContext(KnightConfig.class);
				Knight knight1=(Knight) context1.getBean("knight");
				knight1.embarkOnQuest();
				context1.close();
		}
  • 输出
    安怡宁爱学习
    安怡宁爱学习
    

Spring通过应用上下文,装载bean的定义并把它们组装起来,Spring应用上下文全权负责对象的创建和组装,Spring自带了多种应用上下文的实现,他们之间的区别仅仅在于如何加载配置

应用切面
DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aop)允许你把遍布应用各处的功能分离出来形成可重用的组件。
  • 书写Minstrel类,在这里定义切点前后要使用的方法。只需要将PrintStream注入到Minstrel类中。
package spring.com.one.aop;

import java.io.PrintStream;
/*
将这个类的方法加入到别的代码中并运行起来
 */
public class Minstrel {
		private PrintStream stream;

		public Minstrel(PrintStream stream) {
				this.stream = stream;
		}

		public void singBeforeQuest() {
				stream.println("安公主睡前喜欢听音乐!");
		}

		public void singAfterAuest() {
				stream.println("安公主醒来后就要看书!");
		}
}

  • 书写BravenKnight类实现knight接口
package spring.com.one.aop;

import spring.com.one.di.Quest;

public class BravenKnight implements Knight {

		private Quest quest;
		private Minstrel minstrel;

		public BravenKnight(Quest quest, Minstrel minstrel) {
				this.quest = quest;
				this.minstrel = minstrel;
		}

		//将Minstrel类中的方法分别在Quest的方法的前后执行
		@Override
		public void embarkOnQuest() {
//				minstrel.singBeforeQuest();
				quest.embark();
//				minstrel.singAfterAuest();
		}
}

  • xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!--xmlns:表示命名空间,允许你通过一个网址指向来识别你的标识.
xmlns:xsi:遵守xml规范
xsi:是指具体用到的schema资源,schema:文档的限制。
-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
   
    <!--    将PrintStream对象注入到SlayDragonQuest类中-->
    <bean id="quest" class="spring.com.one.di.SlayDragonQuest">
        <constructor-arg value="#{T(System).out}"/>
    </bean>
    <!--    首先将Minstrel声明为一个bean,注入PrintStream-->
    <bean id="minstrel" class="spring.com.one.aop.Minstrel">
        <constructor-arg value="#{T(System).out}"/>
    </bean>
<!--    将minstrel和quest的bean注入到名字为knight1的BravenKnight类中-->
    <bean id="knight1" class="spring.com.one.aop.BravenKnight">
        <constructor-arg ref="quest"/>
        <constructor-arg ref="minstrel"/>
    </bean>

    <!--    execution():表达式的主体;-->
    <!--    第一个”*“符号 :表示返回值的类型任意;-->
    <!--    com.sample.service.impl	:AOP所切的服务的包名,即,我们的业务部分-->
    <!--    包名后面的”..“	:表示当前包及子包-->
    <!--    第二个”*“	:表示类名,*即所有类。此处可以自定义,下文有举例-->
    <!--    .*(..)	:表示任何方法名,括号表示参数,两个点表示任何参数类型-->
    <aop:config>
        <!--        将Minstrel  bean声明为一个切面,即bean名为minstrel,引用前面的bean-->
        <aop:aspect ref="minstrel">
            <!--            声明一个切点,当调用embarkOnQuest方法的时候触发该切面,在该方法执行前执行before,之后执行after-->
            <aop:pointcut id="embark" expression="execution(*  spring.com.one.aop.BravenKnight.embarkOnQuest(..))"/>
            <aop:before pointcut-ref="embark" method="singBeforeQuest"/>
            <aop:after pointcut-ref="embark" method="singAfterAuest"/>
        </aop:aspect>
    </aop:config>
</beans>

  • 输出
安公主睡前喜欢听音乐!
安怡宁爱学习
安公主醒来后就要看书!

使用aop技术可以将我们常用的组件加入到某一个点,这样可以实现组件的重用。

二、使用模版消除板式代码

  1. 在开发中我们常常会遇见一些板式代码,为了实现通用的和简单的任务,我们不得不一遍遍的重复编写这样的代码。
  2. 比如我们曾用到的jdbc,去操作数据库。
  3. Spring旨在通过模版封装来消除板式代码,比如使用Spring的JdbcTemplate重写的getEmployeeById方法仅仅关注于获取员工数据的核心逻辑,而不需要迎合JDBC API的需求。
  4. 示例:
    • 创建pojo类
package spring.com;

//pojo类
public class Employee {
		private String name;
		private int id ;
		private String sex;
		private  int age;

		public String getName() {
				return name;
		}

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

		public int getId() {
				return id;
		}

		public void setId(int id) {
				this.id = id;
		}

		public String getSex() {
				return sex;
		}

		public void setSex(String sex) {
				this.sex = sex;
		}

		public int getAge() {
				return age;
		}

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

  • 创建获取数据的方法类
package spring.com;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

//@Repository
public class GetEmployee {

		private JdbcTemplate template;

		//		@Autowired
		public GetEmployee(JdbcTemplate template) {
				this.template = template;
		}

		public Employee get(int id) {
				String sql = "Select ID,NAME,SEX,AGE from teacher where id=?";
				RowMapper<Employee> mapper = new BeanPropertyRowMapper<>(Employee.class);
				return template.queryForObject(sql, mapper, id);
		}


}

  • 引入依耐(连接池com.mchange.v2.c3p0.ComboPooledDataSource)
     <!--jdbc连接池-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
  • 书写配置xml(记着添加xmlns,xsi)
  <context:property-placeholder location="classpath:one/db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="getEmployee" class="spring.com.GetEmployee">
        <constructor-arg ref="jdbcTemplate"/>
    </bean>
  • 书写连接数据库的properties属性文件
jdbc.user=lining
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/student
  • 测试方法
	@Test
		public void getEmployee() {
				ApplicationContext context = new ClassPathXmlApplicationContext("one/oneConfig.xml");
				Employee employee=context.getBean(GetEmployee.class).get(8);
				System.out.println(employee.getAge());
				System.out.println(employee.getId());
				System.out.println(employee.getSex());
				System.out.println(employee.getName());
		}
  • 输出
19
8
女
泰妍
这里我们可以多去了解板式代码的好处和使用的思想,在Spring中还有很多这种方法的封装,让我们一起去体验吧。

三、容纳你的Bean

  1. 在基于Spring的应用中,你的应用对象生存与Spring容器中。Spring容器负责创建对象,装配他们,配置他们并管理他们的整个生命周期,从生存到死亡。
  2. Spring自带多个容器实现,可以归为两种不同的类型:
    • bean工厂是最简单的容器,提供基本的DI支持。
    • 应用上下文基于BeanFactory构建,并提供应用框架级别的服务。
使用应用上下文
  • AnnotationConfigApplicationContext:从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式;

  • ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式;

  • FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件;

  • AnnotationConfigWebApplicationContext:专门为web应用准备的,适用于注解方式;

  • XmlWebApplicationContext:从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。

ClassPathXmlApplicationContext是在类路径中寻找文件,而 FileSystemXmlApplicationContext是在系统路径下寻找文件。在前面我们也用到过前两种上下文,后面我们会详细使用他们。
通过配置类来加载bean
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(配置类的全限定名);
bean的生命周期

使用java关键字new进行bean实例化,然后该bean就可以使用,则由java自动进行垃圾回收。相比之下Spring容器中的bean的生命周期就显得相对复杂多了,

我们对上图进行详细描述
  1. Spring对bean进行实例化;

  2. Spring将值和bean的引用注入到bean对应的属性中;

  3. 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;

  4. 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

  5. 如果bean实现了ApplicationContextAware接口,Spring将掉用SetApplicationContext()方法,将bean所在的应用上下文引用传入进来;

  6. 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法;

  7. 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用

  8. 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。

  9. 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。

  10. 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。

下一节我们将仔细描述Bean的装配

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!