学习资源
http://spring.io/
http://projects.spring.io/spring-framework/
Spring:
是一个
轻量级的控制反转(IOC)
面向切面(AOP)的容器框架
包含并管理应用对象的配置和生命周期,这个意义上是一种容器
DI:依赖注入,是IOC得一种实现方式
IOC :控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护。
目的:创建对象并且组装对象之间的关系。
单元测试类
1、下载junit-*.jar并引入工程
2、创建UnitTestBase类,完成对Spring配置文件的加载、销毁
3、所有的单元测试类都继承自UnitTestBase,通过它的getBean("beanName")方法获取想要得到的对象
4、子类(具体指向单元测试的类)加注解:@RunWith(BlockJUnit4ClassRunner.class)
5、单元测试方法加注解:@Test
6、右键选择要执行的单元测试方法执行或执行一个类的全部单元测试方法
BeanFactory提供配置结构和基本功能,加载并初始化Bean
ApplicationContext保存了Bean对象并在Sring中被广泛应用
初始化ApplicationContext的方式:
1、本地文件:FileSystemXmlApplicationContext
2、Classpath(相对路径):ClassPathXmlApplicationContext
3、Web应用中依赖servlet或Listener
ContextLoaderListener
ContextLoaderServlet
Spring 注入
是指在启动Spring容器加载Bean配置的时候,完成对变量的赋值行为
常用的两种注入方式:
设置注入 <property name="" ref=""/>
构造注入 <constructor-org name="" ref=""/>
Bean的配置项
Id
Scope:作用域
singleton:单例(默认),指一个Bean容器中只存在一份
prototype:每次请求(每次使用)创建新的实例,destroy方式不生效(可通过HashCode来区分singleton)
request:每次http请求创建一个实例且仅在当前request内有效
session:同上,每次http请求创建,当前session内有效
global session:基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
class
Constructor arguments
property
Autowiring mode
lazy-initialization mode
initialization
bean的生命周期
1、定义
2、初始化:(1)实现InitializingBean接口,覆盖afterProrertiesSet()方法
(2)配置bean属性 init-method=“init”:加载bean的init()方法
3、使用
4、销毁:(1)实现DisposableBean接口,覆盖destroy方法(2)配置bean的destroy-method属性
配置全局默认初始化、销毁方法:在<beans>中配置属性default-init-method=“init”和default-destroy-method=“destroy”
Aware结尾的接口:
BeanNameAware:获得Bean的名称
ApplicationContextAware:获得Bean容器
Bean的自动装配(Autowiring)<beans default-autowire="">
No
byName
byType
Constructor(是根据构造器中所传参数,然后根据该参数所对应的Bean类型(byType)进行查找的)
Resource:针对于资源文件的统一接口
UrlResource url:http:....
ClassPathResource classpath:config.txt
FileSystemResource file:D://study
ServletContextResource
InputStreamResource
ByteArrayResource
ResourceLoader:所有的ApplicationContext都实现了这个类,因此所有的ApplicationContext都可以获取Resource实例
定义Bean的方式有两种:
xml配置、注解
@Component 是一个通用注解,可用于任何bean
@Repository、@Service、@Controller是更具有针对性的注解
-@Repository:通常用于注解DAO类,即持久层
-@Service:通常用于注解Service类,即服务层
-@Controller:通常用于Controller类,即控制层(MVC)
元注解(Meta-annotations):注解的注解
<context:annotation-config> 仅会查找同一个applicationcontext中的bean注解
<context:component-scan base-package="">
<context:component-scan>包含<context:annotation-config>
默认情况下,类被自动发现并被注册bean的条件是:使用@Component、@Repository、@Service、@Controller注解或使用@Component的自定义注解
可使用过滤器进行自定义扫描:<context:include-filter>、<context:exclude-filter>
还可以使用use-default-filters="false"禁用自动发现与注册
BeanNameGenerator:用于给扫描过程中被自动检测的组件,自动生成Bean名称(以类名首字母小写作为BeanName)
自定义Bean名称策略:实现BeanNameGenerator接口,并一定要包含一个无参数构造函器
<context:component-scan base-package="" name-generator="org.example.MyNameGenerator">
@Scope("singleton"):作用域
自定义Scope策略:实现ScopeMetaResolver接口并提供一个无参数构造器
<context:component-scan base-package="" scope-resolver="org.example.MyScopeResolver">
@Required注解适用于bean属性的setter方法
表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确属性值
@Autowired :可以用在setter方法上、构造器、成员变量上
@Autowired(required=false):通过设置required属性值为false可以避免因找不到合适的bean而导致注入失败抛出异常。
每个类只能有一个构造器被标记为required=true
@Autowired :
可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中得所有特定类型的bean
private Set<MovieCatalog> movieCatalog;
@Autowired
public void setMovieCatalog(Set<MovieCatalog> movieCatalog){
this.movieCatalog=movieCatalog;
}
可以用于装配key为String的Map
private Set<MovieCatalog> movieCatalog;
@Autowired
public void setMovieCatalog(Map<String,MovieCatalog> movieCatalog){
this.movieCatalog=movieCatalog;
}
如果希望数组有序可以让bean实现org.springframework.core.Ordered接口或使用@Order注解
eg:
@Component
public Interface BeanInterface{
}
@Component
public class BeanInvoker{
@Autowired
private List<BeanInterface> list;
@Autowired
private Map<String,BeanInterface> map;
public void say(){
if(null!=list){
System.out.println("list...");
for(BeanInterface bean:list){
System.out.println(bean.getClass().getName());
}
}else{
System.out.println("list is null");
}
if(null!=map && 0!=map.size()){
System.out.println("map...");
for(Map.Entry<String,BeanInterface> entry:map.entrySet()){
System.out.println(entry.getKey()+" "+entry.getValue().getClass().getName());
}
}else{
System.out.println("map is null");
}
}
}
@Order(1) // 设置顺序
@Component
public class BeanImplOne implements BeanInterface{
}
@Order(2) // 设置顺序
@Component
public class BeanImplTwo implements BeanInterface{
}
@Autowired是由Spring BeanPostProcessor处理的
@Qualifier
按类型自动装配可能有多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数
可用于注解集合类型变量
public class MovieRecommender{
@Autowired
@Qualifier("beanImplOne")
private BeanInterface beanInterface;
}
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;
}
}
@Resource(name="") //name值作为被注入Bean的名称,
若没有显示指定@Resource 的name,默认名称是从属性名或者setter方法得出
注解提供的名字被解析为一个Bean的名称,这是由AC中得CommonAnnotationBeanPostProcessor发现并处理的
通过名字进行注解,主要使用的是@Resource注解
@Autowired适用于 字段、构造器、多参方法,这些,允许在参数级别使用@Qualifier注解缩小范围的情况
@Resource 适用于成员变量、只有一个参数的setter方法
所以在目标是构造器或一个多参数方法时,最好是使用@Qualifier
自定义@Qualifier注解:
@Qualifier
public @Interface Genre{
}
之后便可以使用@Genre("name")
基于java的容器注解
1、@Bean表示一个用于配置和初始化一个有SpringIoc容器管理的新对象的方法,类似于XML配置文件的<bean/>
可以在Spring的@Component 注解的类中使用@Bean注解任何方法(仅仅是可以),在方法中创建类并返回
上一点中,@Component同通常使用的 @Configuration
@Configuration
public class AppConfig{
@Bean(name="myService" initMethod="init" destroyMethod="destroy")//init方法和destroy方法在MyServiceImpl类中
public MyService myService(){
return new MyServiceImpl();
}
}
使用@Bean注解时,在没有指定Bean名称时,Bean的名称是方法的名称
2、@ImportResource("classpath:/com/acme/jdbc.properties")
等同于在xml中得如下配置
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"> //其作用是加载资源文件
之后便可以使用${jdbc.url}的方式引用资源文件中的信息
<bean class="org.springframework.jdbc.dataSource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}">
<property name="username" value="${jdbc.username}">
<property name="password" value="${jdbc.password}">
</bean>
@Configuration
@ImportResource("classpath:/com/acme/jdbc.properties")
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);
}
}
@Bean和@Scope
默认@Bean是单例的,使用@Scope来设置Bean的范围
@Bean
@Scope(value="session" proxyMode=ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences(){
reurn new UserPreferences();
}
@Bean
public Service userService(){
UserService service = new SimpleUserService();
service.setUserPreference(userPreferences());
return service;
}
基于泛型的自动装配
注解提供的名字被解析为一个Bean的名称,这是由AC中得CommonAnnotationBeanPostProcessor发现并处理的
CommonAnnotationBeanPostProcessor可识别以下三个注解:
@Resource 、@PostConstruct(初始化回调) 、@PreDestroy(销毁回调)
前提是:CommonAnnotationBeanPostProcessor已经在IOC容器中注册
JSR330标准注解:依赖包javax.inject
@Inject:等效于@Autowired,可适用于类、属性、方法、构造器
@Named:如果想使用特定名称进行依赖注入,可使用@Named
@Named和@Component是等效的
AOP(面向切面的编程):Aspect Oriented Programing 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
主要功能:日志记录、事务管理、安全验证
实现方式:
预编译 -AspectJ
运行期动态管理(JDK动态管理、CGLib动态管理)-SpringAOP、JbossAOP
基本概念
切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象
连接点(joinpoint):程序执行过程中得某个特定的点
通知(Advice):在切面的某个特定的连接点上执行的动作
切入点(Pointcut):匹配连接点得断言,在AOP中通知和一个切入点表达式关联
引入(Introduction):在不修改类代码的前提下,为类添加新的方法和属性
目标对象(Target Object):被一个或多个切面所通知的对象
AOP代理(AOP Proxy):AOP框架创建的对象,,用来实现切面契约(Aspect contract)(包括通知方法执行等功能)
织入(weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
Spring中的AOP 配置了大量的Spring代理机制
Schema-based AOP
<aop:config>
<aop:aspect id="aspect" ref="指定某个类">
<aop:pointcut id="pointcut" expression="execution(匹配的东西)"/>
//在aspect所指定类中设置一个before方法,该方法在切面中所匹配到的方法执行之前执行
<aop:before pointcut-ref="pointcut" method="before"/> 或者 <aop:before pointcut="execution()" method="">
//在aspect所指定类中设置一个afterR方法,该方法在切面中所匹配到的方法执行之后执行
<aop:after-returning pointcut-ref="pointcut" method="afterR"/> 或者 <aop:after-returning pointcut-ref="pointcut" returning="retVal" method=""/>
//指定在切面类中得方法执行中异常抛出后所执行的方法,使用throwing属性来指定可被传递的异常的参数名称
<aop:after-throwing pointcut-ref="pointcut" method="afterT"/> 或者 <aop:after-throwing pointcut-ref="pointcut" throwing="dataAccessEx" method=""/>
//放在最后,在after-returning之后执行,无论方法是否正常结束,after方法都会执行
<aop:after method="after" point-cut="pointcut">
//环绕通知方法的第一个参数必须是ProceedingJoinPoint,调用其proceed()方法
<aop:around method="around" point-cut="pointcut">
</aop:aspect>
</aop:config>
Advice parameter (expression="execution()") and args(变量名1,变量名2)
Introductions
允许一个切面声明一个 实现指定接口 的通知对象,并且提供了一个接口实现类来代表这些对象
<aop:aspect>中得<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent
<aop:declare-parents
type-matching="表达式,找到匹配的类"
implement-interface="接口"
default-impl="接口实现类"/>
则匹配到的类便可以被强制转换为接口类。
Advisors,只有一个advice
Spring AOP 的 API
pointcut 接口
实现类之一:NameMatchMethodPointcut,根据方法的名字进行匹配
成员变量:mappedNames,匹配的方法名集合
<bean id="" class="com.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>sa*</list>
</property>
</bean>
BeforeAdvice/MethodBeforeAdvice:一个简单的通知类型
只是在进入方法之前被调用,不需要MethodInvocation对象
前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
ThrowsAdvice:如果连接点抛出异常,ThrowsAdvice在连接点返回后被调用;
如果ThrowsAdvice的方法抛出异常,那么他将覆盖原有异常
接口ThrowsAdvice不含任何方法,仅仅是一个声明,实现类必须实现它的afterThrowing([method,arg,target],ThrowableSubclass)方法
AfterReturningAdvice:后置通知必须实现该接口,可以访问返回值(但不能修改)、被调用的方法、方法的参数和目标
如果抛出异常,将会抛出拦截器链,代替返回值
Interception around Advice
Spring的切入点模型是的切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点
Interceptor接口:Object Invoke(MethodInvocation invocation)
MethodInterceptor接口:Object Invoke(MethodInvocation invocation)
public interface MethodInterceptor extends Interceptor{
@override
Object Invoke(MethodInvocation invocation)throws Throwable;
}
public interface DebugInterceptor extends MethodInterceptor{
@override
public Object Invoke(MethodInvocation invocation)throws Throwable{
System.out.println("before:"+invocation.getStaticPart().getClass)().getName());
Object retVal = invocation.proceed();
System.out.println("invocation returned:"+retVal);
return retVal;
}
}
Introduction advice (引用通知)
Spring把引入通知作为一种特殊的拦截通知
需要IntroductionAdvisor和IntroductionInterceptor
仅适用于类,不能和任何切入点一起使用
public interface IntroductionInterceptor extends MethodInterceptor{
boolean implementsInterface(Class inf);
}
public interface IntroductionAdvisor extends Advisor,IntroductionInfo{
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo{
Class[] getInterfaces();
}
om.springframework.aop.support.DelegatingIntroductionInterceptor
eg:public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable{
}
Advisor 是一个仅包含一个切入点表达式关联的单个通知的方面
除了introductions,Advisor可以用于任何通知
DefaultPointcutAdvisor是最常用的Advisor类,它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
它可以混合在Spring同一个AOP代理的advisor和advice
ProxyFactoryBean(org.springframework.aop.framework.ProxyFactoryBean)
创建SpringAOP代理的基本方法是使用ProxyFactoryBean
这可以完全控制切入点和通知(advice)以及他们的顺序
一个实现ProxyFactoryBean的类调用getObject()方法时,将创建一个AOP代理包装一个目标对象(target)
被代理类没有实现任何接口,则使用的时CGLIB代理,否则为JDK代理
通过设置proxyTargetClass为true,可强制使用CGLIB
如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建
如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者更多)接口,
那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,将创建一个基于JDK的代理。
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
<bean id="beforeAdvice" class="com.mycompany.BeforeAdvice">
<bean id="ponitcutBean" class="com.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
<list>
</property>
</bean>
<bean id="defaultAdvisor" class="com.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="beforeAdvice"/>
<property name="ponitcut" ref="ponitcutBean"/>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor>
</bean>
<bean id="person"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target" ref="personTarget"/>
<property name="interceptorNames">
<list>
<value>defaultAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
// PersonUser类有一个Person类型的属性,最终赋值给person的Bean是PersonImpl类
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
AspectJ的风格类似纯java注解的普通java类
Spring可以使用AspectJ 来做切入点解析
AOP的运行时仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性
Spring中配置AspectJ:使用需加入包aspectjweaver.jar(版本1.6.8或更高版本)到classpath中
xml配置
<aop:aspectj-autoproxy/>
注解方式
@Configuration
@EnableAspectJAutoProxy
public class AppConfig{}
@Aspect注解:
@AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何Bean将被Spring自动识别并应用
用@Aspect注解的类可以用方法和字段,他们也可能包含切入点(pointcut)、通知(advice)和引用(introduction)声明
@Aspect是不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者在XML中配置Bean
一个类中的@Aspect注解表示它为一个切面,并且将自己从自动代理中排除
@pointcut("execution(*transfer(..))")
private void anyOldTransfer(){} //切入点通过一个普通的方法来提供,方法返回类型必须为void。
组合pointcut
切入点表达式可使用 &&、|| 、! 进行组合
AspectJ是编译期的AOP
一个好的切入点应该包括以下几点
--选择特定类型的连接点,如:execution,get,set,call,handler
--确定连接点的范围,如:within、withincode
--匹配上下文信息,入:this、target、@annotation
AspectJ 中的 BeforeAdvice
@Configuration
@Aspect
public class Aspect{
@pointcut("execution(*transfer(..))")
private void pointcut(){}
@Before(execution(""))
public void before(){
}
@Before(pointcut())//使用已经定义好的切入点
public void before(){
}
@AfterReturning("")或者
@AfterReturning(pointcut="pointcut()" ,returning="retVal")
public void afterReturing(Object retVal){
}
@AfterThrowing("")或者
@AfterThrowing(pointcut="pointcut()" ,throwing="e")
public void afterThrowing(RuntimeException e){
}
//After(finally)Advice
@After(pointcut())//最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源
public void after(){
}
//环绕通知,通知方法的第一个参数必须是ProceedingJoinPoint类型
//在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,传入一个Object[]对象,数组中的值将被作为参数传递给方法
@Around(pointcut())
public Object around(ProceedingJoinPoint pjp) throw Throwable{
System.out.println("before:");
Object obj = pjp.proceed();
System.out.println("after:");
return obj;
}
//引用通知,表示被匹配到的类的实现都实现了一个给定的父类接口,并且该父类接口拥有defaultImpl指定的实现类
@DeclareParents(value="expression",defaultImpl="")
}