Spring课程学习

Spring学习的资源[http://spring.io/;http://projects.spring.io/spring-framework/]

一.Spring简介

1.Spring是什么
  • Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用。
  • 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
    • 从大小与开销两方面而言Spring都是轻量的
    • 通过控制反转IoC的技术达到松耦合的目的
    • 提供了面向切面变成的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发
    • 包含并管理应用对象的配置和生命周期,这个意义上是一种容器
    • 将简单的组件配置、组合成为复杂的应用,这个意义上是框架
2.为什么是Spring
  • 在Spring上开发应用简单
  • 在Spring上开发应用方便
  • 在Spring上开发应用快捷
  • 带来了复杂JavaEE开发的春天
3.Spring作用

在这里插入图片描述

  • 容器
  • 提供了多种技术的支持
    • JMS
    • MQ支持
    • UnitTest
  • AOP(事务管理、日志等)
  • 提供了众多方便应用的辅助类(JDBC Template等)
  • 对主流应用框架(Hibernate等)提供了良好的支持
4.使用范围
  • 构建企业应用(SpringMVC+Spring+Hibernate/ibatis)
  • 单独使用Bean容器(Bean管理)
  • 单独使用AOP进行切面处理
  • 其他的Spring功能,如:对消息的支持等
5.什么是框架
  • 框架的特点
    • 半成品
    • 封装了特定的处理流程和控制逻辑
    • 成熟的、不断升级改进的软件
  • 框架与类库的区别
    • 框架一般是封装了逻辑、高内聚的,类库则是松散的工具组合
    • 框架专注于某一领域,类库则是更通用的
6.为什么使用框架
  • 软件系统日趋复杂
  • 重用度高,开发效率和质量提高
  • 软件设计人员要专注于对领域的了解,使需求分析更充分
  • 易于上手,快速解决问题

二.IOC(配置、注解)

1.接口
  • 用于沟通的中介物的抽象化
  • 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互方式
  • 对应Java接口即声明,声明了哪些方法是对外公开提供的
  • 在Java8中,接口可以拥有方法体
2.面向接口编程
  • 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
  • 接口实现的变动不影响各层间的调用,这点在公共服务中尤为重要
  • “面向接口编程”中的“接口”是用于隐藏具体实现和实现多态性的组件
3.什么是IOC
  • IOC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护

  • DI(依赖注入)是其中一种实现方式,所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象中

  • 目的:创建对象并且组装对象之间的关系

在这里插入图片描述

三.Bean(配置、注解)

1.Spring的Bean配置
eg.
<?xml version="1.0" encoding="UTF-8"?><!--版本和编码的说明-->
<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-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<bean id="oneInterface" class="com.interfaces.OneInterfaceImpl"/></bean>
</beans>
2.单元测试
  • 下载junit-*.jar并引入工程

  • 创建UnitTestBase类,完成对Spring配置文件的加载、销毁

    import org.junit.After;
    import org.junit.Before;
    import org.springframework.beans.BeansException;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.util.StringUtils;
     
    public class UnitTestBase {
    	private ClassPathXmlApplicationContext context;
    	private String springXmlpath;
    	public UnitTestBase() {}
    	public UnitTestBase(String springXmlpath) {
    		this.springXmlpath = springXmlpath;
    	}
    	
    	@Before
    	public void before() {
    		if (StringUtils.isEmpty(springXmlpath)) {
    			springXmlpath = "classpath*:spring-*.xml";
    		}
    		try {
    			context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
    			context.start();
    		} catch (BeansException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	@After
    	public void after() {
    		context.destroy();
    	}
    	
    	@SuppressWarnings("unchecked")
    	protected <T extends Object> T getBean(String beanId) {
    		try {
    			return (T)context.getBean(beanId);
    		} catch (BeansException e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    	
    	protected <T extends Object> T getBean(Class<T> clazz) {
    		try {
    			return context.getBean(clazz);
    		} catch (BeansException e) {
    			e.printStackTrace();
    			return null;
    		}
    	} 
    }
    
  • 所有的单元测试类都继承自UnitTestBase,并通过它的getBean方法获取想要得到的对象

  • 子类(具有执行单元测试的类)加注释:@RunWith(BlockJUnit4ClassRunner.class)

  • 单元测试方法加注释:@Test

  • 右键选择要执行的单元测试方法执行或执行一个类的全部单元测试方法

3.Bean容器初始化
  • 基础:两个包

    • org.springframework.beans
    • org.springframework.context
    • BeanFactory提供配置结构和基本功能,加载并初始化Bean
    • ApplicationContext保存了Bean对象并在Spring中被广泛使用
  • 初始化ApplicationContext方式:

    • 本地文件
    • Classpath【相对工程的路径】
    • Web应用中依赖servlet或Listener
    //Bean容器初始化举例
    //文件
    FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/User/wangzhe/workspace/appcontext.xml");
    //Classpath
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
    //web应用初始化bean容器
    <listener>
    	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
    	<servlet-name>context</servlet-name>
    	<servlet-class>org.springframe.web.context.ContextLoaderServlet</servlet-class>
    	<load-on-startup>1</load-on-startup>
    </servlet>
    
4.Spring注入
  • Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为

  • 常用的两种注入方式

    • 设值注入(利用调用setXXX()方法的方式注入)

      <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
      <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-3.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      	<!-- 加载xml文件的时候创建名为beanA和beanB的对象 -->
          <bean id="beanA" class="com.serviceImple">
              <!-- 在beanA中有一个名为B的对象,引用id是beanB -->
      		<property name="B" ref="beanB"/>
      	</bean>
      	<bean id="beanB" class="com.service"/>
      </beans>
      
    • 构造注入

      <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
      <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-3.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      	<!-- 加载xml文件的时候创建名为beanA和beanB的对象 -->
          <bean id="beanA" class="com.daley.serviceImple">
              <!-- 将beanB注入给beanA构造方法参数为B的变量 -->
      		<constructor-arg name="B" ref="beanB"/>
      	</bean>
      	<bean id="beanB" class="com.daley.service"/>
      
      </beans>
      
5.Bean配置项
  • Id【Bean的唯一标识】
  • Class【具体实例化的类】
  • Scope【作用域】
  • Constructor arguments【构造器的参数】
  • Properties【属性】
  • Autowiring mode【自动装配模式】
  • lazy-initialization mode【懒加载模式】
  • Initialization/destruction method【初始化和销毁方法】
6.Bean的作用域(scope)
//eg.实例代码
//main
package main;
public class BeanScope {
	public void say() {
		System.out.println("BeanScope say:"+this.hashCode());
	}
}
//resources
<?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
<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-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<bean id="beanScope" class="main.BeanScope" scope="singleton"/>
</beans>
//UnitTestBean
package test;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;
public class UnitTestBean {
	private ClassPathXmlApplicationContext context;
	private String springXmlpath;
	public UnitTestBean() {}
	public UnitTestBean(String springXmlpath) {
		this.springXmlpath = springXmlpath;
	}
	@Before
	public void before() {
		if (StringUtils.isEmpty(springXmlpath)) {
			springXmlpath = "classpath*:spring-*.xml";
		}
		try {
			context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
			context.start();
		} catch (BeansException e) {
			e.printStackTrace();
		}
    }
	@After
	public void after() {
		context.destroy();
	}
	@SuppressWarnings("unchecked")
	protected <T extends Object> T getBean(String beanId) {
		try {
			return (T)context.getBean(beanId);
		} catch (BeansException e) {
			e.printStackTrace();
			return null;
		}
	}
	protected <T extends Object> T getBean(Class<T> clazz) {
		try {
			return context.getBean(clazz);
		} catch (BeansException e) {
			e.printStackTrace();
			return null;
		}
	} 
}
//TestBeanScope
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import main.BeanScope;
@RunWith(SpringJUnit4ClassRunner.class)
public class TestBeanScope extends UnitTestBean{
	public TestBeanScope() {
		super("classpath*:spring-beanscope.xml");
	}	
	@Test
	public void testSay() {
		BeanScope beanScope1 = super.getBean("beanScope");
		beanScope.say();
        BeanScope beanScope2 = super.getBean("beanScope");
		beanScope.say();
        //当为singleton模式时,say()结果的输出值一样
        //当为prototype模式时,say()结果的输出值不一样
	}
}
  • singleton:单例,指一个Bean容器中只存在一份【默认模式 】
  • prototype:每次请求(每次使用)创建新的实例,destory方式不生效
  • request:每次http请求创建一个实例且仅在当前request内有效
  • session:同上,每次http请求创建,当前session内有效
  • global session:基于portlet的web中有效(protlet定义了global session),如果是在web中,同session
7.Bean的生命周期
  • 生命周期

    • 定义:在Spring的xml配置文件中定义的bean
    • 初始化:当IOC容器在start启动时,加载并初始化生成bean的实例
    • 使用:在单元测试或实际开发中,从bean容器中取出bean的实例并调用方法
    • 销毁:在bean容器停止的时候,销毁bean容器创建的所有实例
  • 初始化(针对具体某一个bean)

    • 方法一:实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法

      public class ExampleInitializingBean implements InitializingBean{
         @Override
          public void afterPropertiesSet() throws Exception{
              //do something
          } 
      }
      <bean id="exampleInitializingBean" class="examples.ExampleInitializingBean">
      
    • 方法二:配置init-method

      <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init">
      public class ExampleBean{
          public void init(){
              //do some initialization work
          }
      }
      
  • 销毁(针对具体某一个bean)

    • 方法一:实现org.springframework.beans.factory.DisposableBean接口,覆盖destory方法

      public class ExampleBean implements DisposableBean{
         @Override
          public void destory() throws Exception{
              //do something
          } 
      }
      <bean id="exampleBean" class="examples.ExampleBean"/>
      
    • 方法二:配置destory-methos

      <bean id="exampleBean" class="examples.ExampleBean" destory-method="cleanup"/>
      public class ExampleBean{
          public void cleanup(){
              //...
          }
      }
      
  • 配置全局默认初始化、销毁方法

    <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
    <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-3.0.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context-3.0.xsd"
        default-init-method="init" default-destory-method="destory">
    </beans>
    
  • 当同时存在三种初始化方法时,先执行实现接口的对应方法,之后执行init-method和destory-method的方法,默认初始化和销毁的方法不执行。

  • 当定义全局默认的方法在bean中没有时,不会抛出异常,IOC容器正常启动;当定义xxxx-method的方法在bean中没有时,会抛出异常BeanCreationException找不到对应初始化方法,IOC容器启动失败。

8.Aware
  • Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化之后可以获得对应资源

  • 通过Aware接口可以对Spring响应资源进行操作(一定要慎重)

  • 为对Spring进行简单的扩展提供方便的入口

  • ApplicationContextAware:向实现接口的bean提供IOC容器的上下文信息,当然此bean要配置到IOC容器中进行加载

    //eg.举例
    public class TestApplicationContext implements ApplicationContextAware{
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{//重写方法,参数为IOC容器的上下文对象
    		System.out.println("TestApplicationContext:"+applicationContext.getBean("testApplicationContext"));//此处testApplicationContext为配置文件中配置bean的id内容
        }
    }
    
  • BeanNameAware:提供关于BeanName定义的内容

    //eg.举例
    public class TestBeanName implements BeanNameAware{
        @Override
        public void setBeanName(String name){
            //得到xml文件中配置的bean的id值
            System.out.println("TestBeanName:"+name);
        }
    }
    
9.Bean的自动装配(Autowiring)
  • no:不做任何操作【默认】

  • byName:根据属性名自动装配(即bean的id)。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。

  • byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生。

  • constructor:与byType方式类似,不同之处在于它应用于构造器参数(byName和byType应用于set方法的参数)。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常。

  • 实例:

    <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
    <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-3.0.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context-3.0.xsd"
        default-autowire="byName">
        <bean id="autoWiringService" class="test.autowiring.AutoWiringService"></bean>
        <!-- 如果为byName则id的名与AutoWiringService的变量名需要一致才可以自动装配 -->
        <!-- 如果为byType则class的类与AutoWiringService定义变量的类型一致才可以自动装配 -->
        <bean id="autoWiringDao" class="test.autowiring.AutoWiringDao"></bean>
    </beans>
    
    • 当我们将自动装配的模式设置为byName时,不需要在配置文件的bean中添加property属性。只需要在引用其他bean的类中添加对应set方法,在被引用的类中不需要写任何内容,即可以将被引用的类的实例对象装配到引用其的类中。
10.Resources
  • 针对于资源文件的统一接口

  • Resources

    • UrlResource:URL对应的资源,根据一个URL地址即可构建
    • ClassPathResource:获取类路径下的资源文件
    • FileSystemResource:获取文件系统里面的资源
    • ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
    • InputStreamResource:针对于输入流封装的资源
    • ByteArrayResource:针对于字节数组封装的资源
  • ResourceLoader

    • 对Resources进行加载的类

    • 在IOC容器中,所有ApplicationContext都实现了此接口,都可以获得ResourceLoader实例

    • 注入参数的前缀

      • classpath:[classpath路径]
      • file:[在硬盘上的路径]
      • http:[URL地址]
      • (none)[依赖于applicationContext创建方式的路径]
    • 实例

      public class TestResource implements ApplicationContextAware{
          private ApplicationContext applicationContext;
          @Override
          public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
              this.applicationContext = applicationContext;
          }
          public void resource() throw IOException{
              Resource resource = applicationContext.getResource("classpath:config.txt");
              //Resource resource = applicationContext.getResource("file:/Users/wangzhe/config.txt");在硬盘上的路径
              System.out.println(resource.getFilename());
              System.out.println(resource.contentLength());
          }
      }
      
11.Bean管理的注解实现及例子——Classpath扫描与组件管理
  • 从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是XML定义bean,比如@Configuration,@Bean,@Import,@DependsOn

  • @Component是一个通用注解,可用于任何bean

  • @Repository,@Service,@Controller是更有针对性的注解

    • @Repository通常用于注解DAO类,即持久层
    • @Service通常用于注解Service类,即服务层
    • @Controller通常用于Controller类,即控制层(MVC)
  • 元注解(Meta-annotations)[即注解的注解]

    • 许多Spring提供的注解可以作为自己的代码,即“元数据注解”,元注解是一个简单的注解,可以应用到另一个注解(如@Component)
    • 除了value(),元注解还可以有其他的属性,允许定制
  • 类的自动检测及Bean的注册

    • Spring可以自动检测类并注册Bean到ApplicationContext中
  • <context:annotation-config/>

    • 通过在基于XML的Spring配置如下标签(请注意包含上下文命名空间)

    • <context:annotation-config/>仅会查找在同一个applicationContext中的bean注解

      <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
      <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-3.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      	<context:annotation-config/>
      </beans>
      
  • 类的自动检测及Bean的注册

    • 为了能够检测这些类并注册对应的Bean,需要下面内容

      <?xml version="1.0" encoding="UTF-8"?><!--版本和编码的声明-->
      <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-3.0.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context-3.0.xsd">
          <!-- base-package表示扫描对应包下的所有类,找到那些类上有对应Spring相关注解,将这些类注册到IOC容器中 -->
      	<context:component-scan base-package="org.example"/>
      </beans>
      
    • <context:component-scan>包含<context:annotation-config/>,通常在使用前者后不再使用后者(前者包含后者的所有功能),区别如下:

      • <context:component-scan>可以扫描基于类的注解
      • <context:annotation-config/>只能在完成Bean的注册之后,处理Bean中方法或者成员变量的注解
    • AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来

12.使用过滤器进行自定义扫描
  • 默认情况下,类被自动发现并注册bean的条件是:使用@Component、@Repository、@Service、@Controller注解或者使用@Component的自定义注解

  • 可以通过过滤器修改上面的行为,如:下面例子的XML配置忽略所有的@Repository注解并用“Stub”代替

    <beans>
    	<context:component-scan base-package="org.example">
            <!-- 排除指定格式的类 -->
        	<context:include-filter type="regex" expression=".*Stub.*Repository"/>
            <!-- 排除Repository注解的类 -->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        </context:component-scan>
    </beans>
    
  • 还可以使用use-default-filters="false"禁用自动发现与注册

13.定义Bean
  • 扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component,@Repository,@Service,@Controller都会有个name属性用于显示设置BeanName)

    //使用注解时直接定义BeanName
    @Service("myMovieListener")
    public class SimpleMovieListener{
        //...
    }
    //使用默认的定义方式定义BeanName[为类名的第一字母小写]
    @Repository
    public class MovieFinderImpl implements MovieFinder{
        //...
    }
    
  • 可以自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参数构造器

    <beans>
        <!-- 使用自己定义的命名策略 -->
    	<context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator"/>
    </beans>
    
14.作用于Scope
  • 通常情况下自动查找Spring组件,其scope实singleton,Spring2.5提供了一个标识scope作用域的注解@Scope

    //作用域为prototype
    @Scope("prototype")
    @Repository
    public class MovieFinderImpl implements MovieFinder{
        //...
    }
    
  • 也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器

    <beans>
        <!-- 使用自定义scope策略 -->
    	<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
    </beans>
    
15.代理方式
  • 可以使用scoped-proxy属性指定代理,有三个值可选:no,interfaces,targetClass

    <beans>
    	<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
    </beans>
    
16.@Required
  • @Required注解适用于bean属性的setter方法[只能用在set方法上]

  • 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值

    public class SimpleMovieListener{
        private MovieFinder movieFinder;
        @Required
        //表示受影响的bean属性(即movieFinder)必须在配置时被填充
        public void setMovieFinder(MovieFinder movieFinder){
            this.movieFinder = movieFinder;
        }
        //...
    }
    
17.@Autowired
  • 可以将@Autowired注解为传统的setter方法

    private MovieFinder movieFinder;
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder){
        this.movieFinder = movieFinder;
    }
    
  • 可用于构造器或成员变量

    @Autowired
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao){
        this.customerPreferenceDao=customerPreferenceDao;
    }
    
  • 即可以定义在set方法上,还可以定义在构造器或成员变量上

  • 默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下面的方式避免

    public class SimpleMovieLister{
    	private MovieFinder movieFinder;
    	@Autowired(required=false)
        public void setMovieFinder(MovieFinder movieFinder){
            this.movieFinder = movieFinder;
        }
        //...
    }
    
  • 每个类只能有一个构造器被标记为required=true

  • 给set方法添加@Autowired注解时,建议使用@Required注解替换[spring官网推荐]

  • 可以使用@Autowired注解那些众所周知的解析依赖性接口,比如:BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,and MessageSource

    public class MovieRecommender{
        @Autowired
        private ApplicationContext context;
        public MovieRecommender(){}
        //...
    }
    
  • 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中的所有特定类型的bean

    private Set<MovieCatalog>movieCatalogs;
    @Autowired
    //将IOC容器中所有类型为MovieCatalog的bean注入至参数中
    public void setMovieCatalogs(Set<MovieCatalog>movieCatalogs){
    	this.movieCatalogs = movieCatalogs;
    }
    
  • 可以用于装配key为String的Map

    private Map<String,MovieCatalog>movieCatalogs;
    @Autowired
    //将IOC容器中所有类型为MovieCatalog或其子类对象的id存至Map键中,对象存至值中
    public void setMovieCatalogs(Map<String,MovieCatalog>movieCatalogs){
        this.movieCatalogs = movieCatalogs;
    }
    
  • 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用@Order注解(@Order注解只针对集合有效,对于Map无效;且@Order注解参数值为优先级,越小越提前输出)

  • @Autowired是由Spring BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解(否则就会导致循环调用而一直递归),这些类型必须通过XML或者Spring的@Bean注解加载。

18.@Qualifier
  • 按类型自动装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数

  • 可用于注解集合类型变量

  • 实例代码

    public class MovieRecommender{
    	@Autowired
        @Qualifier("main")//如果有多个类型为MovieCatalog或子类的bean,则使用该标签装配qualifier为main的bean
        private MovieCatalog movieCatalog;
    }
    public class MovieRecommender{
        private MovieCatalog movieCatalog;
        private CustomerPreferenceDao customerPreferenceDao;
        @Autowired
        public void prepare(@Qualifier("main")MovieCatalog movieCatalog,CustomerPreferenceDao customerPreferenceDao){
            this.movieCatalog = movieCatalog;
            this.customerPreferenceDao = customerPreferenceDao;
        }
        //...
    }
    
    • 配置文件中的使用qualifier
    <bean class="example.SimpleMovieCatalog">
    	<qualifier value="main"/>
    </bean>
     
    <bean class="example.SimpleMovieCatalog">
    	<qualifier value="action"/>
    </bean>
    
  • @Autowired和@Resource的区别

    • @Autowired适用于fields,constructor,multi-argument methods这些允许在参数级别使用@Qualifier注解缩小范围的情况
    • @Resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用qualifier
19.基于Java容器的注解——@Bean
  • @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的<bean/>

  • 可以在Spring的@Component注解的类中使用@Bean注解任何方法

  • 通常跟@Bean搭配使用的是@Configuration

  • 实例代码

    • 在java中配置
    @Configuration
    public class AppConfig{
        @Bean
        public MyService myService(){
            return new MyServiceImpl();
        }
    }
    
    • 等同于xml中配置
    <beans>
    	<bean id="myService" class="com.example.services.MyServiceImpl"/>
    </beans>
    
  • @Bean的相关操作

    • 自定义Bean name

      @Configuration
      public class AppConfig{
          @Bean(name="myFoo")//相当于指定bean的id(默认是方法名)
          public Foo foo(){
              return new Foo();
          }
      }
      
    • init-method

    • destory-method

      public class Foo{
          public void init(){
              //初始化逻辑...
          }
      }
      public class Bar{
          public void cleanup(){
              //销毁逻辑...
          }
      }
      @Configuration
      public class AppConfig{
          @Bean(initMethod="init")
          public Foo foo(){
      		return new Foo();
          }
          @Bean(destoryMethod="cleanup")
          public Bar bar(){
              return new Bar();
          }
      }
      
20.基于Java容器的注解——@ImportResource和@Value
  • 使用xml的方式加载配置文件<context:property-placeholder location="classpath:..."/>

    <beans>
    	<context:annotation-config/>
        <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
        <bean class="org.acme.AppConfig"/>
        <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        	<property name="url" value="${jdbc.url}"/><!-- 通过${}的方式取值,使用对应的properties中的键取值即可 -->
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    </beans>
    
  • 使用java方式取值

    @Configuration
    @ImportResource("classpath:/com/acme/properties-config.xml")
    public class AppConfig{
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
        @Bean
        public DataSource dataSource(){
            return new DriverManagerDataSource(url,username,password);
        }
    }
    
21.基于Java容器的注解——@Bean和@Scope
  • 默认@Bean是单例的

  • Bean的作用域包括singleton、prototype、request、session、global session

  • proxyMode为代理方式

  • 实例代码

    @Configuration
    public class MyConfiguration{
        @Bean
        @Scope("prototype")
        public Encryptor encryptor(){
            //...
        }
    }
    
    @Bean
    @Scope(value="session",proxyMode=ScopedProxyMode.TARGET_CLASS)
    public UserPreferences userPreferences(){
        return new UserPreferences();
    }
    @Bean
    public Service userService(){
        UserService service = new SimpleUserService();
        service.setUserPreferences(userPreferences());
        return service;
    }
    
22.基于泛型的自动装配
  • 实例代码

    @Configutration
    public class MyConfiguration{
        @Bean
        public StringStore stringStore){
            return new StringStore();
        }
        @Bean
        public IntegerStore integerStore(){
            return new IntegerStore();
        }
    }
    
    @Autowired
    private Store<String>s1;
    @Autowired
    private Store<Integer>s2;
    
23.Spring对JSR标准的支持
  • @Resource

    • Spring还支持使用JSR-250@Resource注解的变量或setter方法,这是一种在JavaEE5和6的通用模式,Spring管理的对象也支持这种模式

    • @Resource有一个name属性,并且默认Spring解释该值作为被注入bean的名称

      public class SimpleMovieListener{
          private MovieFinder movieFinder;
          @Resource(name="myMovieFinder")
          public void setMovieFinder(MovieFinder movieFinder){
              this.movieFinder = movieFinder;
          }
      }
      
    • 如果没有显式地指定@Resource的name,默认的名称是从属性名或者setter方法得出

      public class simpleMovieListener{
          private MovieFinder movieFinder;
          @Resource
          public void setMovieFinder(MovieFinder movieFinder){
              this.movieFinder = movieFinder;
          }
      }
      
    • 注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的

  • @PostConstruct和@PreDestory

    • CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的

      public class CachingMovieListener{
          @PostConstruct
          public void populateMovieCache(){
              //...
          }
          @PreDestory
          public void clearMovieCache(){
              //...
          }
      }
      
  • 使用JSR330标准注解

    • 从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致

    • 使用JSR330需要依赖javax.inject包

    • 使用Maven引入方式

      <dependency>
      	<groupId>javax.inject</groupId>
          <artifactId>javax.inject</artifactId>
          <version>1</version>
      </dependency>
      
  • @Inject

    • @Inject等效于@Autowired,可以使用于类、属性、方法、构造器

      import javax.inject.Inject;
      public class SimpleMovieListener{
          private MovieFinder movieFinder;
          @Inject
          public void setMovieFinder(MovieFinder movieFinder){
              this.movieFinder = movieFinder;
          }
      	//...
      }
      
  • @Named

    • 如果想使用特定名称进行依赖注入,使用@Named

    • @Named与@Component是等效的

      import javax.inject.Inject;
      import javax.inject.Named;
      
      public class SimpleMovieListener{
          private MovieFinder movieFinder;
          @Inject
          public void setMovieFinder(@Named("main")MovieFinder movieFinder){
              this.movieFinder = movieFinder;
          }
          //...
      }
      
      //指定某个名称的bean
      @Named("movieListener")
      public class SimpleMovieListener{
          private MovieFinder movieFinder;
          @Inject
          public void setMovieFinder(MovieFinder movieFinder){
              this.movieFinder = movieFinder;
          }
          //...
      }
      

四.AOP(配置、注解、AspectJ、API)

1.什么是AOP
  • AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术
  • 主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等
2.AOP实现方式
  • 预编译
    • AspectJ
  • 运行期的动态代理(JDK动态代理、CGLib动态代理)
    • SpringAOP、JbossAOP
3.AOP几个相关概念
  • 切面:一个关注点的模块化,这个关注点可能会横切多个对象
  • 连接点:程序执行过程中的某个特定的点
  • 通知(Advice):在切面的某个特定的连接点上执行的动作
  • 切入点:匹配连接点的断言,在AOP中通知和一个切入点表达式关联
  • 引入:在不修改类代码的前提下,为类添加新的方法和属性
  • 目标对象:被一个或者多个切面所通知的对象
  • AOP代理:AOP框架创建的对象,用来实现切面契约(包括通知方法执行等功能)
  • 织入:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为编译时织入、类加载时织入和执行时织入
4.Advice的类型
  • 前置通知(Before advice):在某连接点之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
  • 返回后通知(After returning advice):在某连接点正常完成后执行的通知
  • 抛出异常后通知(After throwing Advice):在方法抛出异常退出时执行的通知
  • 后通知(After (finally)advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
  • 环绕通知(Around Advice):包围一个连接点的通知
5.Spring框架中AOP的用途
  • 提供了声明式的企业服务,特别是EJB的替代服务的声明
  • 允许用户定制自己的方面,以完成OOP(Object Oriented Programming面向对象编程)与AOP的互补使用
    • OOP:一切皆对象,模拟真实世界中的行为和方式
    • AOP:关注于横切
6.Spring的AOP实现
  • 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
  • 目前只支持方法执行连接点(通知Spring Bean的方法执行)
  • 不是为了提供最完善的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和SpringIoC容器之间的整合,用于帮助解决企业应用中的常见问题
  • SpringAOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
7.有接口和无接口的SpringAOP实现区别
  • SpringAOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
  • SpringAOP中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)
8.Schema-based AOP
  • schema-defined aspects只支持singleton model(即配置文件的aspect只支持单例模式)

  • Spring所有的切面和通知器都必须放在一个<aop:config>内(可以配置包含多个<aop:config>元素),每一个<aop:config>可以包含pointcut,advisor和aspect元素【它们必须按照这个顺序进行声明

  • <aop:config>风格的配置大量使用了Spring的自动代理机制

  • <aop:config><aop:aspect>

    <aop:config>
        <!-- 将aBean配置为切面-->
    	<aop:aspect id="myAspect" ref="aBean">
        	...
        </aop:aspect>
    </aop:config>
    <!-- 切面类 -->
    <bean id="aBean" class="...">
        ...
    </bean>
    
9.pointcut切入点
  • SpringAOP和AspectJ都支持的

    • execution(public * *(..))切入点为执行所有public方法时
    • execution(* set*(..))切入点为执行所有set开始的方法时
    • execution(* com.xyz.service.AccountService.*(..))切入点为执行AccountService类中的所有方法时
    • execution(* com.xyz.service..(..))切入点为执行com.xyz.service包下的所有方法时
    • execution(* com.xyz.service...(..))切入点为执行com.xyz.service包及其子包下的所有方法时
  • 只有SpringAOP支持的【请查找Spring的官方文档:建议不要死记】

  • 实例代码

    <aop:config>
        <aop:aspect id="myAspect" ref="aBean">
            <!-- 匹配类中所有方法 -->
            <aop:pointcut id="businessService" expression="execution(* com.xmy.myapp.BusinessService.*(..))"/>
        </aop:aspect>
    </aop:config>
    
10.advice通知
  • Before advice

    <!-- 举例一 -->
    <aop:aspect id="beforeExample" ref="aBean">
        <!-- 表示前置通知针对dataAccessOperation切入点,调用doAccessCheck方法去执行 -->
    	<aop:before pointcut-ref="dataAccessOperation" method="doAccessCheck"/>
    </aop:aspect>
    <!-- 举例二 -->
    <aop:aspect id="beforeExample" ref="aBean">
        <!-- 直接指定pointcut -->
    	<aop:before pointcut="execution(* com.xyz.myapp.dao..(..))" method="doAccessCheck"/>
    </aop:aspect>
    
  • After returning advice(在方法返回之后执行)

    <!-- 举例一 -->
    <aop:aspect id="afterReturnExample" ref="aBean">
    	<aop:after-returning
             pointcut-ref="dataAccessOperation"
             method="doAccessCheck"/>
    </aop:aspect>
    <!-- 举例二 -->
    <aop:aspect id="afterReturnExample" ref="aBean">
    	<aop:after-return
             pointcut-ref="dataAccessOperation"
             returning="retVal"
             method="doAccessCheck"/>
    </aop:aspect>
    
  • After throwing advice(在方法抛出异常时执行)

    <aop:aspect id="afterThrowingExample" ref="aBean">
    	<aop:after-throwing
             pointcut-ref="dataAccessOperation"
             method="doRecoveryActions"/>
    </aop:aspect>
    <!-- 使用throwing属性来指定可被传递的异常的参数名称 -->
    <aop:aspect id="afterThrowingExample" ref="aBean">
    	<aop:after-throwing
             pointcut-ref="dataAccessOperation"
             throwing="dataAccessEx"
             method="doRecoveryActions"/>
    </aop:aspect>
    
  • After (finally) advice(在方法结束前的最后一行代码)

    <aop:aspect id="afterFinallyExample" ref="aBean">
    	<aop:after
             pointcut-ref="dataAccessOperation"
             method="doReleaseLock"/>
        ...
    </aop:aspect>
    
  • Around advice【通知方法的第一个参数必须是ProceedingJoinPoint类型】

    //通知方法的第一个参数必须是ProceedingJoinPoint类型
    public Object doBaseProfiling(ProceedingJoinPoint pip) throws Throwable{
        Object retVal = pip.proceed();
        return retVal;
    }
    
    <aop:aspect id="aroundExample" ref="aBean">
    	<aop:around
             pointcut-ref="businessService"
             method="doBasicProfiling"/>
        ...
    </aop:aspect>
    
  • Advice parameters[在advice中传递参数]

    public interface FooService{
        Foo getFoo(String fooName,int age);
    }
    public class DefaultFooService implements FooService{
        public Foo getFoo(String name,int age){
            return new Foo(name,age);
        }
    }
    
    public class SimpleProfiler{
        public object profile(ProceedingJoinPoint call,String name,int age) throws Throwable{
    		StopWatch clock = new StopWatch("Profiling for" + name+" and "+age);
            try{
                clock.start(call.toShortString());
            }finally{
                clock.stop();
                System.out.println(clock.prettyPrint());
            }
        }
    }
    
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <bean id="profiler" class="x.y.SimpleProfiler"/>
    <aop:config>
    	<aop:aspect ref="profiler">
        	<aop:pointcut id="theExecutionOfSimeFooServiceMethod"
                 expression="execution(* x.y.service.FooService.getFoo(String,int)) and args(name,age)"/>
            <aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
                 method="profile"/>
        </aop:aspect>
    </aop:config>
    
11.Introductions
  • 简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象

  • <aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent

  • <aop:aspect id="usageTrackerAspect" ref="usageTracking">
    	<aop:declare-parents
             types-matching="com.xyz.myapp.service.*+"
             implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
             default-impl="com.xyz.myapp.servicec.tracking.DefaultUsageTracked"/>
        <aop:before
             pointcut="com.xyz.myapp.SystemArchitecture.businessService() and this(usageTracked)"
             method="recordUsage"
    </aop:aspect>
    
12.Advisors
  • advisor就像一个小的自包含的方面,只有一个advice

  • 切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入表达式

  • spring通过配置文件中<aop:advisor>元素支持advisor实际使用中,大多数情况下它会和transavtional advice配合使用

  • 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性定义advisor的顺序

  • 相关配置

    <aop:config>
    	<aop:pointcut id="businessService"
             expression="execution(* com.xyz.myapp.service..(..))"/>
        <aop:advisor
             pointcut-ref="businessService"
             advice-ref="tx-advice"/>
    </aop:config>
    <tx:advice id="tx-advice">
    	<tx:attributes>
        	<tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
13.Spring AOP API
  • Pointcut

    • 实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
    • 成员变量:mappedNames,匹配的方法名集合
    <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
    	<property name="mappedNames">
        	<list>
            	<value>sa*</value>
            </list>
        </property>
    </bean>
    
  • Before advice

  • Throws advice

  • After Returning advice

  • Interception around advice

  • Introduction advice

  • Advisor API in Spring

  • ProxyFactoryBean

    • 创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
    • 这可以完全控制切入点和通知(Advice)以及他们的顺序
  • 使用ProxyFactory

    • 使用Spring AOP而不必依赖于Spring IoC

      ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
      factory.addAdvice(myMethodInterceptor);
      factory.addAdvisor(myAdvisor);
      MyBusinessInterface tb = (MyBusinessInterface)factory.getProxy();
      
    • 大多数情况下最佳实践是用IoC容器创建AOP代理

    • 虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现

14.AspectJ
  • @AspectJ的风格类似纯java注解的普通java类

  • Spring可以使用AspectJ来做切入点解析

  • AOP的运行时仍旧是存的SpringAOP,对AspectJ的编译器或者植入无依赖性

  • Spring中配置@AspectJ

    • 对@AspectJ支持可以使用XML或Java风格的配置
    • 确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8或更高版本)的classpath中
    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig{}
    
    //或使用xml配置方式
    <aop:aspectj-autoproxy/>
    
  • @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean都将被Spring自动识别并应用

  • 用@Aspect注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明

  • @Aspect注解时不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者在XML配置bean

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值