Book2: Spring揭秘(245/673)
########################################################################################
######## 20161207~20161208 ##############################
########################################################################################
1、我搭建的第一个Spring测试环境。导入了以下6个jar包,参考:http://www.importnew.com/13246.html
- org.springframework.aop-3.2.9
- org.springframework.aspects-3.2.9
- org.springframework.beans-3.2.9
- org.springframework.context-3.2.9
- org.springframework.context.support-3.2.9
- org.springframework.core-3.2.9
- org.springframework.expression-3.2.9
- commons-logging-1.2
2、实例:
package com.importnew;
public classHelloWorld {
private String message;
public voidsetMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
public voidprintMessage(){
System.out.println("Your Message : "+ message);
}
}
配置:
<?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-3.0.xsd">
<bean id="helloWorld"class="com.importnew.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
package com.importnew;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.printMessage();
}
}
另外一种方式:
<bean id=" bean" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean2.xml”);
HelloApi bean = beanFactory.getBean("bean", HelloApi.class);
bean.sayHello();
3、注释:
1、两个最基本最重要的包是 org.springframework.beans 和 org.springframework.context.
这两个包中的代码为Spring的反向控制 特性(也叫作依赖注射)提供了基础。
2、ApplicationContext是BeanFactory的子类。
3、org.springframework.beans.factory.xml.XmlBeanFactory是接口BeanFactory的一个实现
4、在ApplicationContext的诸多实现中,有三个经常用到:
ClassPathXmlApplicationContext:从类路径中的XML文件载入上下文定义信息,把上下文定义文件当作类路径资源。
FileSystemXmlApplicationContext:从文件系统中的XML文件载入上下文定义信息。
XmlWebApplicationContext:从Web系统中的XMl文件载入上下文信息。
4、aop的原理即是java的动态代理?
5、配置简写
让我们来总结一下依赖注入配置及简写形式,其实我们已经在以上部分穿插着进行简化配置了:
一、构造器注入:
1)常量值
简写:<constructor-arg index="0" value="常量"/>
全写:<constructor-arg index="0"><value>常量</value></constructor-arg>
2)引用
简写:<constructor-arg index="0" ref="引用"/>
全写:<constructor-arg index="0"><ref bean="引用"/></constructor-arg>
二、setter注入:
1)常量值
简写:<property name="message" value="常量"/>
全写:<property name="message"><value>常量</value></ property>
2)引用
简写:<property name="message" ref="引用"/>
全写:<property name="message"><ref bean="引用"/></ property>
3)数组:<array>没有简写形式
4)列表:<list>没有简写形式
5)集合:<set>没有简写形式
6)字典
简写:<map>
<entry key="键常量" value="值常量"/>
<entry key-ref="键引用" value-ref="值引用"/>
</map>
全写:<map>
<entry><key><value>键常量</value></key><value>值常量</value></entry>
<entry><key><ref bean="键引用"/></key><ref bean="值引用"/></entry>
</map>
################################################################################
######## 20161209 ###########################################
################################################################################
1、延迟初始化Bean: lazy-init
延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。
<bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl" lazy-init="true"/>
不建议使用:除非是那种消耗资源大,且不常用的bean。
2、depends-on
3、依赖检查
上一节介绍的自动装配,很可能发生没有匹配的Bean进行自动装配,如果此种情况发生,只有在程序运行过程中发生了空指针异常才能发现错误,如果能提前发现该多好啊,这就是依赖检查的作用。依赖检查:用于检查Bean定义的属性都注入数据了,不管是自动装配的还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。Spring3+也不推荐配置方式依赖检查了,建议采用Java5+ @Required注解方式,测试时请将XML schema降低为2.5版本的,和自动装配中“autodetect”配置方式的xsd一样。
依赖检查有none、simple、object、all四种方式,接下来让我们详细介绍一下:
一、none:默认方式,表示不检查;
二、objects:检查除基本类型外的依赖对象,配置方式为:dependency-check="objects",此处我们为HelloApiDecorator添加一个String类型属
性“message”,来测试如果有简单数据类型的属性为null,也不报错;
- <bean id="helloApi" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
- <!-- 注意我们没有注入helloApi,所以测试时会报错 -->
- <bean id="bean"
- class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
- dependency-check="objects">
- <property name="message" value="Haha"/>
- </bean>
注意由于我们没有注入bean需要的依赖“helloApi”,所以应该抛出异常UnsatisfiedDependencyException,表示没有发现满
足的依赖:
三、simple:对基本类型进行依赖检查,包括数组类型,其他依赖不报错;配置方式为:dependency-check="simple",以下配置中没有注入message属性,所以会抛出异常:
四、all:对所以类型进行依赖检查,配置方式为:dependency-check="all",如下配置方式中如果两个属性其中一个没配置将报错。
#####################################################################################
######## 20161210 #############################################
#####################################################################################
singleton:指“singleton”作用域的Bean只会在每个Spring IoC容器中存在一个实例,而且其完整生命周期完全由Spring容器管理。对于所有获取该Bean的操作Spring容器将只返回同一个Bean。
prototype:即原型,指每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。
######################################################################################
######## 20161211 ###### 201612114 ###########################
######################################################################################
1、AOP:低耦合的新增一些功能。
- 用于横切关注点的分离和织入横切关注点到系统;比如上边提到的日志等等;
- 完善OOP;
- 降低组件和模块之间的耦合性;
- 使系统容易扩展;
- 而且由于关注点分离从而可以获得组件的更好复用。
AOP代理就是AOP框架通过代理模式创建的对象,Spring使用JDK动态代理或CGLIB代理来实现,Spring缺省使用JDK
动态代理来实现,从而任何接口都可别代理,如果被代理的对象实现不是接口将默认使用CGLIB代理,不过CGLIB代理当
然也可应用到接口。
2、需要新增一个包啦:
###################################################################################
######## 20170216 #########################################
###################################################################################
3、aop基本概率
在进行AOP开发前,先熟悉几个概念:
- 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP中表示为“在哪里干”;
- 切入点(Pointcut):选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为“在哪里干的集合”;
- 通知(Advice):在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为“干什么”;
- 方面/切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是通知、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为“在哪干和干什么集合”;
- 引入(inter-type declaration):也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在AOP中表示为“干什么(引入什么)”;
- 目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为“被通知对象”;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中表示为“对谁干”;
- AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。
- 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。
接下来再让我们具体看看Spring有哪些通知类型:
- 前置通知(Before Advice):在切入点选择的连接点处的方法之前执行的通知,该通知不影响正常程序执行流程(除非该通知抛出异常,该异常将中断当前方法链的执行而返回)。
-
后置通知(After Advice):在切入点选择的连接点处的方法之后执行的通知,包括如下类型的后置通知:
- 后置返回通知(After returning Advice):在切入点选择的连接点处的方法正常执行完毕时执行的通知,必须是连接点处的方法没抛出任何异常正常返回时才调用后置通知。
- 后置异常通知(After throwing Advice): 在切入点选择的连接点处的方法抛出异常返回时执行的通知,必须是连接点处的方法抛出任何异常返回时才调用异常通知。
- 后置最终通知(After finally Advice): 在切入点选择的连接点处的方法返回时执行的通知,不管抛没抛出异常都执行,类似于Java中的finally块。
- 环绕通知(Around Advices):环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等等。
4、基于schema的aop实例
定义目标接口:
- package cn.javass.spring.chapter6.service;
- public interface IHelloWorldService {
- public void sayHello();
- }
定义目标接口实现:
- package cn.javass.spring.chapter6.service.impl;
- import cn.javass.spring.chapter6.service.IHelloWorldService;
- public class HelloWorldService implements IHelloWorldService {
- @Override
- public void sayHello() {
- System.out.println("============Hello World!");
- }
- }
定义切面支持类
- package cn.javass.spring.chapter6.aop;
- public class HelloWorldAspect {
- //前置通知
- public void beforeAdvice() {
- System.out.println("===========before advice");
- }
- //后置最终通知
- public void afterFinallyAdvice() {
- System.out.println("===========after finally advice");
- }
- }
此处HelloWorldAspect类不是真正的切面实现,只是定义了通知实现的类,在此我们可以把它看作就是缺少了切入点的切面。
在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"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd”>
- <bean id="helloWorldService” class="cn.javass.spring.chapter6.service.impl.HelloWorldService”/>
- <bean id="aspect" class="cn.javass.spring.chapter6.aop.HelloWorldAspect"/>
- <aop:config>
- <aop:pointcut id="pointcut" expression="execution(* cn.javass..*.*(..))"/>
- <aop:aspect ref="aspect">
- <aop:before pointcut-ref="pointcut" method="beforeAdvice"/>
- <aop:after pointcut="execution(* cn.javass..*.*(..))" method="afterFinallyAdvice"/>
- </aop:aspect>
- </aop:config>
- </beans>
切入点使用<aop:config>标签下的<aop:pointcut>配置,expression属性用于定义切入点模式,默认是AspectJ语法,“execution(* cn.javass..*.*(..))”表示匹配cn.javass包及子包下的任何方法执行。切面使用<aop:config>标签下的<aop:aspect>标签配置,其中“ref”用来引用切面支持类的方法。
前置通知使用<aop:aspect>标签下的<aop:before>标签来定义,pointcut-ref属性用于引用切入点Bean,而method用来引用切面通知实现类中的方法,该方法就是通知实现,即在目标类方法执行之前调用的方法。pointcut和pointcut-ref:二者选一,指定切入点;
最终通知使用<aop:aspect>标签下的<aop:after >标签来定义,切入点除了使用pointcut-ref属性来引用已经存在的切入点,也可以使用pointcut属性来定义,如pointcut="execution(* cn.javass..*.*(..))",method属性同样是指定通知实现,即在目标类方法执行之后调用的方法。
5、aop原理
静态代理的缺陷:每次都需要创建一个代理类,不实用。
aop的两种实现原理:
1.动态代理:通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。
动态代理要求:被代理的对象必须实现接口。即必须针对接口编程,因此有了CGLIB。
2.CGLIB:通过实现net.sf.cglib.proxy.MethodInterceptor接口(或者其扩展接口:MethodInterceptor接口)
CGLIB要求:无法对final方法进行覆写。
Spring揭秘第九章第一代aop需仔细阅读,搞清楚原理。
###################################################################################
######## 20170217 ##########################################
###################################################################################
6、aop:基于@AspectJ的aop
1)使用@Aspect将POJO声明为切面;
2)使用@Pointcut进行命名切入点声明,同时指定目标方法第一个参数类型必须是java.lang.String,对于其他匹配的方法但参数类型不一致的将也是不匹配的,通过
argNames = "param"指定了将把该匹配的目标方法参数传递给通知同名的参数上;
3)使用@Before进行前置通知声明,其中value用于定义切入点表达式或引用命名切入点;
4)配置文件需要使用<aop:aspectj-autoproxy/>来开启注解风格的@AspectJ支持;
5)需要将切面注册为Bean,如“aspect”Bean;
6)测试代码完全一样。
通配符:
* :匹配任何数量字符;
.. :(两个点)匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+ :匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
实例:
声明切面:使用@Aspect
@Aspect()
public class Aspect{
@Pointcut(value="execution(* cn.javass..*.sayAdvisorBefore(..)) && args(param)”,
argNames = "param”)
public void beforePointcut(String param) {}
@Before(value = "beforePointcut(param)”,
argNames = "param”)
public void beforeAdvice(String param) {
System.out.println("===========before advice param:" + param);
}
@AfterReturning(value = "beforePointcut(param)”,
argNames="retVal”,
returning="retVal”)
public void afterReturningAdvice(Object retVal) {
System.out.println("===========after returning advice retVal:" + retVal);
}
@AfterThrowing( value="execution(* cn.javass..*.sayAfterThrowing(..))",
argNames="exception”,
throwing="exception")
public void afterThrowingAdvice(Exception exception) {
System.out.println("===========after throwing advice exception:" + exception);
}
@After(value="execution(* cn.javass..*.sayAfterFinally(..))")
public void afterFinallyAdvice() {
System.out.println("===========after finally advice");
}
//看他的参数:ProceedingJoinPoint pjp,与其他通知不同
@Around(value="execution(* cn.javass..*.sayAround(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("===========around before advice");
Object retVal = pjp.proceed(new Object[] {"replace"});
System.out.println("===========around after advice");
return retVal;
}
}
切面需在配置文件中声明,Spring就能自动识别并进行aop方面的配置:
<bean id="aspect" class="……Aspect"/>
###############################################################################
######## 20170221 ##################################
###############################################################################
7、BeanFactory的XML之旅
<beans>作为所有<bean>的“统帅”,它拥有相应的属性(attribute)对所辖的<bean>进行统一 的默认行为设置,包括如下几个:
default-lazy-init。其值可以指定为true或者false,默认值为false。用来标志是否对所有的<bean>进行延迟初始化。
default-autowire。可以取值为no、byName、byType、constructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用 哪一种默认绑定方式。
default-dependency-check。可以取值none、objects、simple以及all,默认值为none,即不做依赖检查。
default-init-method。如果所管辖的<bean>按照某种规则,都有同样名称的初始化方法的 话,可以在这里统一指定这个初始化方法名。
default-destroy-method。与default-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法。
其中default-lazy-init是配置全局的懒加载模式,也可以单独在每个bean上进行配置:<bean id="lazy-init-bean" class="..." lazy-init="true"/>
其中default-autowire是配置全局的自动配置模式,也可以单独在每个bean上进行配置。
自动绑定和手动明确绑定各有利弊。自动绑定的优点有如下两点:
(1) 某种程度上可以有效减少手动敲入配置信息的工作量。
(2) 某些情况下,即使为当前对象增加了新的依赖关系,但只要容器中存在相应的依赖对象,就不需要更改任何配置信息。
自动绑定的缺点有如下几点:
(1) 自动绑定不如明确依赖关系一目了然。我们可以根据明确的依赖关系对整个系统有一个明确的认识,但使用自动绑定的话,就可能需要在类定义以及配置文件之间,甚至各个配置 文件之间来回转换以取得相应的信息。
(2) 某些情况下,自动绑定无法满足系统需要,甚至导致系统行为异常或者不可预知。根 据类型(byType)匹配进行的自动绑定,如果系统中增加了另一个相同类型的bean定义,那么 整个系统就会崩溃;根据名字(byName)匹配进行的自动绑定,如果把原来系统中相同名称的 bean定义类型给换掉,就会造成问题,而这些可能都是在不经意间发生的。
(3) 使用自动绑定,我们可能无法获得某些工具的良好支持,比如Spring IDE。
标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例; 而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。通常情况下,如果你不指定bean的scope,singleton便是容器默认的scope。以下三种定义都代表是singleton的scope的:
<bean id="mockObject1" class="...MockBusinessObject"/>
<bean id="mockObject1" class="...MockBusinessObject" singleton="true"/>
<bean id="mockObject1" class="...MockBusinessObject" scope="singleton"/
针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新 生成一个新的对象实例给请求方:
<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>
<bean id="mockObject1" class="...MockBusinessObject" scope="prototype"/>
###############################################################################
#######20170222 ##################################
###############################################################################
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
容器启动阶段
Configuration MetaData
|| 加载并分析配置信息
BeanDefinition
|| 注册
BeanDefinitionRegister
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Bean实例化阶段
getBean
BeanFactoryPostProcessor:干预容器启动阶段
该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改,
比如修改其中bean定义的某些属性,为bean定义增加其他信息等。配置信息,修改一些配置参数
具体用其两个实现类:
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
org.springframework.beans.factory.config.PropertyOverrideConfigurer
BeanPostProcessor接口:
public interface BeanPostProcessor
Object postProcessBeforeInitialization(Object bean, String beanName) throws ➥ BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws ➥ BeansException;
}
我们看下其中一个方法的实现:
只要某个bean实现了某一个aware接口,就会被BeanPostProcessor进行处理。BeanPostProcessor的两个方法中都传入了原来的对象实例的引用,
这为我们扩展容器的对象实例化过程中的行为提供了极大的便利,我们几乎可以对传入的对象实例执行任 何的操作。
我们可以自定义BeanPostProcessor,通过实现BeanPostProcessor接口即可,但需要将自定义的BeanPostProcessor注册到容器。
除了检查标记接口以便应用自定义逻辑,还可以通过BeanPostProcessor对当前对象实例做更多的处理。比如替换当前对象实例或者字节码增强当前对象实例等。
Spring的AOP则更多地使用 BeanPostProcessor来为对象生成相应的代理对象,如org.springframework.aop.framework. autoproxy.BeanNameAutoProxyCreator。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
###############################################################################
#######20170222 1818 ##################################
###############################################################################
1、ApplicationContext
BeanFactory:XmlBeanFactory实现。
ApplicationContext:三种实现