《SPRING实战第三版》1~4章笔记

第一章

SPRING 最根本的使命:简化Java开发

  • 基于POJO的的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码

框架的侵入性

  • 当一个框架强迫我们实现它的接口或者继承他的类时,那么我们的应用就与这个框架进行了绑定,我们的应用设计也不得不迁就与这个框架,我们需要写很多代码来维护框架与应用的关系,而不是我们的具体业务。

POJO / JaveBean

  • JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性由三部分组成属性、方法、事件。百科传送门 
  • POJO(Plain Ordinary Java Object)简单的Java对象,包含属性和set/get访问方法,不包含业务处理或数据处理逻辑。百科传送门

代码紧耦合

  • 下例中Classroom在构造函数中创建了MathTeacher这使得这两个类紧密的联系在了一起。实现限制了教室的能力,这个教室只能用来讲数学。PS:一定的耦合是必须的如果没有老师上课,那教室就只是个空荡荡的房子。

public class Classroom {
	private MathTeacher teacher;

	public Classroom(){
		teacher = new MathTeacher();
	}

	public void education(){
		teacher.speak();
	}
}

构造器注入

  • 优化一下刚才的示例,抽象出一个Teacher接口。教室不自行创建老师而是通过构造器注入进来。MathTeacher、PhysicalTeacher等实现Teacher接口类的对象都可以注入进来。

public class Classroom {
	private Teacher teacher;
	
	public Classroom(Teacher t){
		teacher = t;
	}
	
	public void education(){
		teacher.speak();
	}
}

装配

  • 创建应用组件之间协作的行为通常称为装配。

	@Test
	public void ClassroomTest(){
		MathTeacher mathTeacher = new MathTeacher();
		Classroom mathCr = new Classroom(mathTeacher);
		mathCr.education();
		PhysicalTeacher physicalTeacher = new PhysicalTeacher();
		Classroom pCr = new Classroom(physicalTeacher);
		pCr.education();
	}

AOP

  • 依赖注入让相互协作的软件组件松散耦合,而AOP允许你把遍布应用各处的功能分离出来形成可重用的组件。

样板式代码

  • 一个常见的样板式代码范例是在使用JDBC查询数据库,你一定记得sql拼接和取值的酸爽滋味。

        @Test
	public void findUser(Integer id) {
		Connection connection = null;
		PreparedStatement statement = null;
		ResultSet resultSet = null;
		try {
			/*噩梦一般的sql拼接*/
			String sql = "select * from user where id="+id;
			connection = getConn();
			statement = connection.prepareStatement(sql);
			resultSet = statement.executeQuery();
			User user = null;/*写到手残的列取值*/
			while (resultSet.next()) {
				user = new User();
				user.setId(resultSet.getInt("id"));
				user.setName(resultSet.getString("name"));
				user.setSex(resultSet.getString("sex"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{/*收尾工作*/
			if (null!=resultSet) {
				try {
					resultSet.close();
				} catch (SQLException e) {e.printStackTrace();}
			}
			if (null!=statement) {
				try {
					statement.close();
				} catch (SQLException e) {e.printStackTrace();}
			}
			if (null!=connection) {
				try {
					connection.close();
				} catch (SQLException e) {e.printStackTrace();}
			}
		}
	}

Spring Bean容器

  • 容器用来管理Bean,创建对象、装配对象、配置对象,管理对象的生命周期(创建到销毁)

     容器有两种

  • Bean工厂 org.springframework.beans.factory.BeanFactory接口定义。
  • 应用上下文org.springframework.context.ApplicationContext接口定义。

     Spring自带了多种应用上下文实现,下列是最常用到的。

  • ClassPathXmlApplicationContext 从类路径下的xml文件中加载上下文定义
  • FileSystemXmlapplicationcontext 读取文件系统下的xml配置文件加载上下定义
  • XmlWebApplicationContext 读取web应用下的xml配置并加载上下文定义

Bean的生命周期

  1. Spring对Bean进行实例化
  2. Spring将值和Bean的引入注入进Bean对应的属性中。
  3. Bean实现BeanNameAware接口,Spring将BeanID传递给setBeanName()接口方法。
  4. Bean实现BeanFactoryAware接口,Spring将调用setBeanFactory()接口方法,传入BeanFactory容器实例
  5. Bean实现ApplicationContextAware接口,Spring将调用setApplicationContext()接口方法,传入应用上下文的引用
  6. Bean实现BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()接口方法
  7. Bean实现InitializingBean接口,Spring将调用它们的afterPropertiesSet()接口方法。如果init-method声明了初始化方法,该方法也会调用
  8. Bean实现BeanPostProcessor接口,Spring将调用它们的postPoressAfterInitialization()方法。
  9. Bean准备就绪,可以被应用程序使用,直到应用上下文销毁
  10. Bean实现DisposableBean接口,Spring将调用它的destroy()接口方法。使用destroy-method声明了销毁方法,该方法也会被调用。

  • 唯一可能使用Spring的生命周期接口的场景是,开发一个明确在Spring容器内使用的框架Bean。PS:初始化和销毁方法或许是我们需要参与的阶段,所以Spring提供了另一种可配置的方式来指定。

第二章

Spring核心框架自带的命名空间

  • aop 声明切面以及@AspectJ注解的类代理为Spring切面提供了配置元素。
  • beans 支持声明Bean和装配Bean,是Spring最核心也是最原始的命名空间
  • context为配置应用上下文提供了配置元素,包括自动检测和自动装配Bean、注入非Spring直接管理的对象。
  • jee 提供了与java EE API的集成,例如JNDI和EJB
  • jms 为声明消息驱动的POJO提供配置元素
  • lang 支持配置由Groovy、JRuby或者BeanShell等脚本实现的Bean
  • mvc 启动Spring MVC的能力,例如面向注解的控制器、视图控制器和拦截器。
  • oxm 支持Spring的对象到XML映射配置。
  • tx 提供声明式事物配置
  • util 提供各种各样的工具类元素,包括把集合配置为Bean、支持属性占位符元素

Bean的作用域

  • singleton 在每个Spring容器中,一个Bean定义只有一个对象实例(默认,多次调用getBean返回统一个对象)
  • prototype 允许Bean的定义可以被实例化任意次(每次调用都创建一个实例)
  • request 在一次HTTP请求中,每个Bean定义对应一个实例。该作用域仅在基于Web的Spring上下文(例如Spring MVC)中才有效
  • session 在一个HTTP Session中,每个Bean定义对应一个实例。该作用域仅在基于Web的Spring上下文(例如SpringMVC)中才有效
  • global-session 在一个全局HTTP Session中,每个Bean定义对应一个实例。该作用域仅在Portlet上下文中才有效。

声明装配Bean

	<!-- 声明一个简单的Bean -->
	<bean id="mT" class="com.test.coupling.MathTeacher" />

	<!-- 通过构造器依赖注入① -->
	<bean id="cr_1" class="com.test.coupling.Classroom">
		<constructor-arg ref="mT" />
	</bean>

	<!-- 通过构造器依赖注入② -->
	<bean id="cr_2" class="com.test.coupling.Classroom">
		<constructor-arg value="30"/>
		<constructor-arg ref="mT" />
	</bean>
	
	<!-- 调用属性setter方法设置属性值 -->
	<bean id="cr_3" class="com.test.coupling.Classroom">
		<property name="number" value="30" />
		<property name="teacher" ref="mT"/>
	</bean>
	
	<!-- 内部Bean:缺点不能复用,影响XML可读性。 -->
	<bean id="cr_4" class="com.test.coupling.Classroom">
		<property name="number" value="30" />
		<property name="teacher">
			<bean class="com.test.coupling.MathTeacher" />
		</property>
	</bean>
	
	<!-- 设置空值 :潜在用法是覆盖自动装配的值-->
	<bean id="cr_5" class="com.test.coupling.Classroom">
		<property name="teacher" ><null /></property>
	</bean>
	
	<!-- 指定初始化/销毁方法 -->
	<bean id="mT" class="com.test.coupling.MathTeacher" init-method="initMt" destroy-method="destroyMt"/>
	
	<!-- 设置Bean的作用域 -->
	<bean id="cr_4" class="com.test.coupling.Classroom" scope="prototype"/>
	
	<!-- 通过自定义方法创建Bean:通过这种方式来实现一个Bean的单例(在整个应用中) -->
	<bean id="cr_3" class="com.test.coupling.Classroom" factory-method="getInstance"/>

通过Beans元素来设置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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd"

        default-init-method="init"
        default-destroy-method="destroy"
        >
</beans>

使用Spring命名空间p装配属性

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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="mT" class="com.test.coupling.MathTeacher" />
	<bean id="cr" class="com.test.coupling.Classroom" p:number="30" p:teacher-ref="mT" />
</beans>

装配集合

Spring提供的集合配置元素

  • <list> 装配java.util.Collection的任意实现或者数组,允许重复
  • <set> 装配java.util.Collection的任意实现或者数组,不允许重复
  • <map>装配java.util.Map/java.util.Properties类型的值,名称和值可以使任意类型
  • <props>装配java.util.Map/java.util.Properties类型的值,名称和值必须都是String类型

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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="mT" class="com.test.coupling.MathTeacher" />
	<bean id="eqC" class="com.test.coupling.Computer" />
	
	<bean id="cr" class="com.test.coupling.Classroom" >
		<property name="seatNames">
			<list>
				<value>座位_1</value>
				<value>座位_2</value>
				<value>座位_3</value>
			</list>
		</property>
		<property name="teachers">
			<list>
				<ref bean="mT"/>
				<bean class="com.test.coupling.PhysicalTeacher"/>
			</list>
		</property>
		<property name="students">
			<set>
				<bean class="com.test.coupling.Students"/>
				<bean class="com.test.coupling.Students"/>
			</set>
		</property>
		<property name="equipment">
			<map>
				<entry key="bb"  value="blackboard"/>
				<entry key="c"  value-ref="eqC"/>
			</map>
		</property>
		<property name="slogan">
			<props>
				<prop key="s1">好好学习天天向上</prop>
			</props>
		</property>
	</bean>
</beans>

SpEl表达式

  • 使用Bean的ID来引用Bean;
  • 调用方法和访问对象的属性;
  • 对值进行算术、关系和逻辑运算;
  • 正则表达式匹配;
  • 集合操作。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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  
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-2.0.xsd
       ">
       
       <bean id="sr" class="com.test.wwq.impl.RoleImpl" >
			<property name="name" value="学生"/>
       </bean>
       
       <util:list id="roles">
			<bean class="com.test.wwq.impl.RoleImpl" p:name="学生"/>
			<bean class="com.test.wwq.impl.RoleImpl" p:name="老师"/>
			<bean class="com.test.wwq.impl.RoleImpl" p:name="校长"/>
       </util:list>
       
       <util:map>
			<entry key="club_1" value="篮球社"/>  
			<entry key="club_2" value="足球社"/>  
       </util:map>
       <util:properties id="provinces" location="classpath:" />
       
       <bean id="" class="com.test.coupling.Students" >
	        <!-- 字面值:字符串,数字,浮点,科学计数法,布尔 -->
			<property name="name" value="#{'李连杰'}" />
	        <!-- 与非SpEl表达式混用 -->
			<property name="nativePlace" value="河北省_#{石家庄}" />
	        <!-- 引用Bean -->
			<property name="role" value="#{sr}" />
	        <!-- 引用Bean的属性/方法结果 -->
			<property name="roleName" value="#{sr.name}" />
			<property name="behavior" value="#{sr.behavior()}" />
			<!-- 操作Bean的属性/方法结果 -->
			<property name="capitalBehavior" value="#{sr.behavior().toUpperCase()}" />
			<!-- 使用null-safe存取器避免NullPointerException -->
			<property name="lowercaseBehavior" value="#{sr.behavior()?.toLowerCase()}" />
			<!-- 操作类:T()运算符调用类作用域的方法和常量 -->
			<property name="pi" value="#{T(java.lang.Math).PI}" />
			<property name="number" value="NUM_#{T(java.lang.Math).random()}" />
			<!-- 访问List集合成员 -->
			<property name="role" value="#{roles[0]}" />
			<!-- 访问Map集合成员 -->
			<property name="club" value="#{roles['club_2']}" />
			<!-- 访问Properties -->
			<property name="provinces" value="#{provinces['0100']}" />
			<!-- systemEnvironment:机器环境变量 -->
			<property name="homePath" value="#{systemEnvironment['HOME']}" />
			<!-- systemProperties:启动时设置的所有属性(-D参数)-->
			<property name="homePath" value="#{systemEnvironment['application.home']}" />
			<!-- 得到字符串摸个字符 -->
			<property name="group" value="#{'A,B,C,D'[0]}"/>
			<!-- 查询集合 -->
			<property name="role" value="#{roles.?[name eq '学生']}"/>
			<!-- 投影集合:从集合的每个成员选择特定的属性放入一个新的集合(可以多个属性进行计算) -->
			<property name="roleNames" value="#{roles.![name]}"/>
       </bean>
</beans>

第三章

自动装配(autowiring)

  • 让Spring 自动识别如何装配Bean的依赖关系,有助减少甚至消除配置<property>元素和<constructor-arg>元素。

自动装配的4中类型

  • byName:把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的名字相匹配的Bean,则该属性不进行装配。
  • byType:把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。如果没有跟属性的类型相匹配的Bean,则该属性不被装配。
  • constructor:把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean的构造器的对应入参中。
  • autodetect:首先尝试使用constructor进行装配。如果失败,在尝试使用byType进行自动装配。

4中类型的优缺点

  • byName:多个Action类配置了相同名称的Service,那么这几个Action类共用一个Service这种情况在大多数情况下是没有问题的(实际上我们也是这么用的),但是我们应该记得这个限制。
  • byType:如果有多个符合Bean,Spring会不知道怎么选择从而报错。我们通过primary属性取消其他Bean的资格来候选资格(primary默认是true所以我们需要把不想被匹配的Bean都显示的设置为false)
  • consturctor:与byType相同的局限性多个可匹配的Bean报错,此外如果一个类的多个构造器,他们都满足自动装配的条件时,Spring也不会尝试猜测那个构造器更适合使用。

自动装配示例:

<!-- 指定Bean的自动装配方式 -->
<bean id="user" class="com.wwq.test.User" autowire="byName"></bean>
<!-- 自动装配和显示装配混合使用 -->
<bean id="user" class="com.wwq.test.User" autowire="byName">
	<property name="" /><null/></property>
</bean>
<!-- 取消byType匹配资格 -->
<bean id="user" class="com.wwq.test.User" primary="false"></bean>

默认自动装配方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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  
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-2.0.xsd"
	default-autowire="byType">
</beans>

启用自动装配注解

<context:annotation-config>

Spring3自动装配注解

  • Spring自带的@Atutowired注解;
  • JSR-330的@Inject注解;
  • JSR-250的@Resource注解;

Atutowired注解

  • byType类型的自动装配,该注解可以标注属性,set方法或者其他任意需要自动装配Bean引用的方法。即使以上方法和属性的作用域修饰词是private。

Atutowired的required属性

  • 作用:@Autowired具有强契约特征,其所标注的属性或参数必须是可装配的。如果没有就会抛出NoSuchBeanDefinitionException。我们可以设置required为false来消除这种异常。
  • 限制:当@Autowired标注多个构造器时,只有一个构造器可以将@Autowired的requeired属性设置为true其他只能设置为false,并且Spring会从所有满足装配条件的构造器中选择入参最多的那个构造器

限制歧义性的依赖

  • 通过@Qualifier来缩小byType可选择的范围

示例1

设置Bean的@Qualifier

	@Qualifier("studentsUserDao")	
	public class StudentsUserDao implements UserDao{
	}

注入ID为userDao的Bean

	@Autowired
	@Qualifier("studentsUserDao")
	private UserDao userDao;

示例2

创建自定义的限定器

	@Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
	@Retention(RetentionPolicy.RUNTIME)
	@Qualifier
	public @interface StudentsUserDaoInStrument{
	}
使用自定义的限定器
	@StudentsUserDaoInStrument	
	public class StudentsUserDao implements UserDao{
	}
	public class StuService{
		@Autowired
		@StudentsUserDaoInStrument
		private UserDao userDao;
		......
	}

基于标准的自动装配

  • JSR-330的@Inject注解与Spring的@Autowired注解几乎完全一样,都可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性(所以他只能通过限定来避免多个可适配Bean的问题)。

Inject注入Provider

  • Provider可以实现Bean引用的延迟注入以及注入Bean的多个实例等功能。

	
public class StuService{
	private UserDao userDao;
		public StuService(Provider<UserDao> userDaoProvider){
		userDao = userDaoProvider.get();
	}
}

限定@Inject所标注的属性

  • @Inject对应的限定@Named注解。它非常类似与Spring的@qualifier

@Named与@Qualifier的区别

  • 区别在于语义层面(就是没有区别?)@Qualifier注解帮助我们缩小所匹配Bean的选择范围(默认使用Bean的ID)而@Named通过Bean的ID来标识可选择的Bean。

创建自定义的JSR-330 Qualifier

  • JSR-330在javax.inject包中有自己的@Qualifier注解。不像Spring的@Qualifier,JSR-330不建议使用该注解。相反,JSR-330鼓励我们使用该注解来创建自定义的限定注解。就像使用Spring的@Qualifier来创建自定义注解一样。

@Value

  • 注解装配String类型的值和基本类型的值。
  • 使用SpEl表达式。

自动检测(autodiscovery)

  • 让Spring能够自动识别那些类需要被配置成Spring Bean,从而减少<bean>元素的使用。

启动自动检测Bean

<context:component-scan base-package="com.test.wwq"></context:component-scan>
  • <context:component-scan>元素会扫描指定的包及其所有子包,并找出能够自动注册为Spring Bean的类。base-package属性标识了元素所扫描的包。

标注可被自动检测的Bean

  • @Component:通用的构造型注解,标识该类为Spring组件。
  • @Controller:标识将该类定义为Spring MVC controller。
  • @Repository:标识将该类定义为数据仓库。
  • @Service:标识将该类定义为服务。
  • 使用@Component标注的任意自定义注解。

示例

@Component(value="stu")
public class StudentsUserDao{
}
public class StuService{
		@Autowired
		@Qualifier("stu")
		private UserDao userDao;
		......
}
  • value属性指定注册Bean的ID名称,默认为无限定类名。

过滤组件扫描

  • 基于注解的扫描策略的痛点:如果我们让所有实现UserDao的类都自动注册,那么我们需要浏览每一个UserDao实现源码,并且使用构造型注解标注他们。如果想把第三方插件的类也注册进去,在没有源码访问权限来添加注解的情况下,基于注解的扫描策略就比较头疼了。
  • 过滤扫描策略和注解扫描策略是共存的,使用<context:include-filter>的同时,@Service、@Component等注解同样有效。

过滤行为

  • <context:include-filter>:告知那些类需要注册为Spring Bean。
  • <context:exclude-filter>:告知那些类不需要注册为Spring Bean。

过滤器类型

  • annotation:过滤器扫描使用指定注解所标注的那些类。通过expression属性指定要扫描的注解
  • assignable:过滤器扫描派生于expression属性所指定类型的那些类。
  • aspectj:过滤器扫描与expression属性所指定的AspectJ表达式所匹配的那些类。
  • custom:使用自定义的org.springframework.core.type.TypeFilter实现类,该类由expression属性指定。
  • regex:过滤器扫描类的名称与expression属性所指定的正则表达式所匹配的哪些类。

示例1:

  • 实现UserDao接口的类自动注册
  • 放弃基于注解的组件扫描策略,增加一个过滤器通过type和expression属性一起协作来定义组件扫描策略

	    <context:component-scan base-package="com.wwq.test">
	    	<context:include-filter type="assignable" expression="com.wwq.test.UserDao"/>
	    </context:component-scan>
示例2
  • 实现UserDao接口并且没有被@FailureRole注解标注的类自动注册

	    <context:component-scan base-package="com.wwq.test">
	    	<context:include-filter type="assignable" expression="com.wwq.test.UserDao"/>
	    	<context:exclude-filter type="annotation" expression="com.wwq.test.FailureRole"/>
	    </context:component-scan>

使用Spring基于Java的配置

  • @Configuration注解的Java类,等价于XML配置中的<beans>元素。这个注解会作为一个标识告知Spring:这个类将包含一个或多个Spring Bean的定义。这些Bean的定义是使用@Bean注解所标注的方法。

示例

@Configuration
public class SpringConfig {
	@Bean
	public UserDao userDao(){
		return new UserDao();
	}
	@Bean
	public UserDao stuService(){
		return StuService(userDao()); 
	}
}
  • @Bean告知Spring这个方法将返回一个对象,该对象应该被注册为Spring应用上下文中的一个Bean。方法名作为该Bean的ID。
  • Spring的Java配置相对于XML卑职的一个优点是它可以进行编译期检查。因为在Java的配置中Bean的ID和类型都被视为方法签名的一部分,而Bean的实际创建是在方法体中定义的。因为他们全部是Java代码所以我们可以进行编译期检查来确保Bean的类型是合法类型,并且Bean的ID是唯一。
  • Spring会拦截@Bean注解标注的方法调用,先尝试在应用上下文中查找该Bean如果没有,才会让方法创建一个新的实例。

第四章

AOP术语

  • 通知(advice)
    • 定义了切面是什么,什么时候使用。
    • Before 方法调用之前调用通知
    • After 方法完成之后调用通知,无论方法是否成功
    • After-returning 在方法成功执行之后调用通知
    • After-throwing 在方法抛出异常后调用通知。
    • Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
  • 链接点(join point)
    • 通知执行的时机,调用方法时、抛出异常时、甚至修改一个字段时。
  • 切点(pointcut)
    • 通知执行的位置,类和方法名称或者正则表达式匹配的方式指定切点。
  • 切面(Aspect)
    • 切面是通知和切点的结合。
  • 引入(introduction)
    • 无需修改现有的类的情况下添加新方法或属性。
  • 织入(weaving)
    • 织入是将切面应用到目标对象来创建新的代理对象的过程。
    • 在目标对象的生命周期里有多个点可以进行织入。
    • 编译器:在目标类编译时织入切面。需要特殊的编译器(AspectJ编译器)
    • 类加载器:在目标类加载到JVM时被织入,需要特殊的类加载器(ClassLoader)它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的LTW支持这种方式。
    • 运行期:在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态的创建一个代理对象。Spring AOP采用这种方式方式。

著名的AOP框架

  • Aspectj
  • JBosss AOP
  • Spring AOP

Spring提供了4种AOP支持

  • 基于代理的经典AOP
  • @AspectJ 注解驱动的切面;
  • 纯POJO切面;
  • 注入式AspectJ切面(适合Spring各个版本)

在ApplicationContext从BeanFactory中加载所有Bean时,Spring创建被代理的对象。

  • Spring 只支持方法连接点,AspectJ和Jboss除方法切点还提供字段和构造器接入点。

Spring 支持的AspectJ切点指示器

  • arg() 限制连接点匹配参数为指定类型的执行方法。
  • @args() 限制连接点匹配参数由指定注解标注的执行方法。
  • execution() 用于匹配是连接点的执行方法。
  • this() 限制连接点匹配AOP代理的Bean引用为指定类型的类。
  • target() 限制连接点匹配目标对象为指定类型的类。
  • @target() 限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解
  • within 限制连接点匹配指定的类型
  • @within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时,方法定义在由指定的注解所标注的类里)
  • @annotation 限制匹配带有指定注解连接点。

编写切点

  • 当User的registered()方法执行时会触发通知
	在方法执行时触发    犯法所属的类型	 使用任意参数
      	____|____       ________|________          __|__ 
     	|       |	|               |          |   | 
	execution(  *   com.test.wwq.User.registered(..))
	          |___| |__________________________|
		    |		    |	
    	       返回任意类型	 特定方法
	
  • 仅匹配com.test.wwq包
			执行user.registered()方法
	______________________|__________________________
	|                                              | 
	execution(  *   com.test.wwq.User.registered(..))
		&&   within(com.test.wwq.*)
	       |___||_____________________|
                 |            |  
	   与(and)操作符   当com.test.wwq包下的任意类的方法被调用时
	

bean()指示器

  • Spring 2.5引入了的bean()指示器,该指示器允许我们在切入点表达式中使用Bean的ID或名称作为参数来限制切入点只匹配特定的Bean。
  • execution( * com.test.wwq.User.registered(..)) and bean(user)
    • 执行user.registered()方法应用通知,但限定Bean的id为user。
  • execution( * com.test.wwq.User.registered(..))and !bean(user)
    • 执行user.registered()方法应用通知,但限定是所有id不是user的bean。

在XML中声明切面

  • 定义AOP通知器
  • 定义AOP后置通知(不管被通知的方法是否执行成功)
  • 》 定义AOP after-returning 通知
  • 定义after-throwing通知
  • 定义AOP环绕通知
  • 定义切面
  • 启动@AspectJ注解驱动切面
  • 定义AOP前置通知
  • 顶层的AOP配置元素。多数的元素必须包含在元素内
  • 为被通知的对象引入额外的接口,并透明地实现
  • 定义切点,避免重复的切点表达式。

    示例

    		<bean id="log" class="com.test.wwq.LogUtil"/>
    		<aop:config>
    		    <aop:aspect ref="log">
    			<aop:pointcut id="logPointcut" expression="切点表达式"/>
    			<aop:before pointcut-ref="logPointcut" method="通知方法" />
    			<aop:after-returning pointcut-ref="logPointcut" method="通知方法" />
    			<aop:after-throwing pointecut="切点表达式" method="通知方法" />
    		    </aop:aspect>
    		</aop:config>
    	

    声明环绕通知

    • 整个通知逻辑在一个方法内实现。

    实例

    	public void watchPerformance(ProceedingJoinPoint joinpoint){
    		try{
    		    /*执行被通知方法之前*/
    		    joinpoint.proceed();
    		    /*执行被通知方法之后*/	
    		}catch(){
    		    /*执行被通知方法异常*/
    		}
    	} 
    	
    	<aop:config>
    	    <aop:aspect ref="log">
    		<aop:pointcut id="logPointcut" expression="切点表达式"/>
    		<aop:around pointcut-ref="logPointcut" method="watchPerformance()" />
    	    </aop:aspect>
    	</aop:config>
    	

    为通知传递参数

    	<aop:config>
    	    <aop:aspect ref="log">
    		<aop:pointcut id="logPointcut" expression="切点表达式"/>
    		<aop:before pointcut-ref="logPointcut" method="通知方法" arg-names="形参名称"/>
    	    </aop:aspect>
    	</aop:config>
    	

    通过切面引入新功能

    	<aop:config>
    		<aop:aspect>
    			<aop:declare-parents types-matching="com.test.wwq.User+" 
    					implement-interface="com.test.wwq.Role"
    					default-impl="com.test.wwq.impl.RoleImpl"/>
    		</aop:aspect>
    	</aop:config>
    	
    • types-matching:指定那些Bean需要引入新功能,“+”标识匹配的接口,如果不写在转换会失败,具体原因未知。
    • implement-interface:引入的新功能接口。
    • default-impl:引入的新功能接口实现。
    • delegate-ref:与default-impl一样,不同的是它指定的是一个可以被注入,被通知的Spring Bean。

    切面注解

    • 注解
      • @Aspect 类注解,定义方面
      • @Pointcut() 定义可重复用的切点
      • @Before() AOP前置通知
      • @After() AOP后置通知(不管是否完成)
      • @AfterReturning() AOP后置通知(正常执行完成)
      • @AfterThrowing() AOP异常通知
      • @Around() 定义环绕通知
      • @DeclareParents 引入
    • <aop:aspectj-autoproxy />
      • 在Spring上下文创建一个AnnotationAwareAspectJAutoProxyCreator类
    • 注解与XML想比较不好的地方是我们必须是源码可见的。
    • 注解引入
    	@Aspect
    	public class ContestantIntroducer{
    		@DeclareParents(value="com.test.wwq.User+",defaultImpl = GraciousContestant.class)
    		public static Contestant contestant;
    	}
    	
    • value 指定那些Bean需要引入新功能。
    • 被标注的变量contestant就是要引入的接口。
    • defaultImpl 执行引入接口的实现类。

    注入AspectJ

    • AspectJ几乎不会涉及到Spring,当切面依赖于一个或多个类时我们可以用Spring的依赖注入减少耦合度。
    • 使用 AspectJ需要特殊的IDE。
  • 你可能感兴趣的:(《SPRING实战第三版》1~4章笔记)