Spring知识集锦
注入方式 | 说明 | ||
@Resource | 来源:JSR250 (Common Annotations for Java) 注入方式:
|
||
@Inject | 来源:JSR330 (Dependency Injection for Java) 注入方式:
|
||
@Autowired | 来源:Spring私有 注入方式:与@Inject相同 支持required属性,可以配置为false,即使找不到依赖也不会抛出异常 |
||
@Qualifier | 来源:Spring私有 配合上面注解之一使用,相当于一个分类器,在被依赖的Bean上、依赖字段上同时配置该注解,用于确定其关联性,例如:
|
传播属性 | 说明 |
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起(方法结束后此新事务被提交) |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
Scope | 说明 |
singleton | 默认Scope,这样的Bean在整个IoC容器的生命周期内仅仅有一个实例 |
prototype | 每次向IoC容器请求时,都会得到一个全新的实例 |
request | 仅仅在Web-aware的Application Context中才可用,在一个HTTP请求范围内,只有一个实例 |
session | 仅仅在Web-aware的Application Context中才可用,在一个HTTP会话范围内,只有一个实例 |
global-session | 仅仅在Web-aware的Application Context中才可用,在一个Servlet上下文范围内,只有一个实例 |
可以在基于XML、注解的配置中使用Spring EL,语法格式:
1 |
#{ <expression string> } |
XML配置示例:
1 2 3 4 5 6 7 8 9 10 11 |
<!-- 调用静态方法 --> <bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> </bean> <!-- 你可以直接引用其它Bean的属性,或者调用其方法:--> <property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> <!-- 变量systemProperties是预定义的,你可以通过它访问系统属性 --> <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/> |
注解配置示例:
1 2 3 4 |
# 访问系统属性 @Value("#{systemProperties['user.country']}") # 访问环境变量 #{systemEnvironment['DDEPUB_ORDINAL']} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!-- Spring --> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
即,在web.xml中直接声明XML配置文件的路径,如果需要引入额外的XML配置,可以使用<import>指令:
1 |
<import resource="core.xml"/> |
如果要引入额外的JavaConfig类,可以定义Bean:
1 |
<bean class="cc.gmem.BeanDefinitionRegistrar" /> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!-- Spring --> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>cc.gmem.SpringBeanDefinitionRegistrar</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>cc.gmem.SpringMVCBeanDefinitionRegistrar</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
即,在web.xml中直接声明JavaConfig类的权限定名,如果需要引入额外的XML配置,需要在JavaConfig类中引入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
@Configuration /* 引入额外的XML配置文件 */ @ImportResource ( "classpath:pems-application.xml" ) public class SpringBeanDefinitionRegistrar { /* Auto injected spring context */ @SuppressWarnings ( "unused" ) @Inject private ApplicationContext applicationContext; /* 编程式的创建Bean,默认是单例的 */ @Bean ( name = "helper" ) public Helper helper() { Helper helper = new Helper(); return helper; } } @Configuration /* 引入额外的XML配置文件 */ @ImportResource ( "classpath:pems-application-mvc.xml" ) public class SpringMVCBeanDefinitionRegistrar { } |
JavaConfig方式中,尝试通过XML Bean的方式引入额外的JavaConfig类,会出现问题(3.1.2版本的Spring),行为不符合预期。
格式 | 含义 |
classpath*:conf/appCtx.xml | 类路径下所有的JAR中的conf/appCtx.xml并合并为一个上下文 |
classpath:conf/appCtx.xml | 仅仅使用第一个找到的conf/appCtx.xml |
即使合理配置了Hibernate Session Factory、TransactionManager,在@PostConstruct方法上也加了事务配置(@Transactional),依旧无法进行数据库操作,报错:No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
原因:在此(InitializingBean.afterPropertiesSet )阶段,无法保证所有PostProcessors 完成其工作
解决:有两种方案:
- 注册 ContextRefreshedEvent 的监听器,在监听方法里,可以确保事务机制可用
- 在@PostConstruct方法里面使用TransactionTemplate,例如:
123456789101112131415private PlatformTransactionManager transactionManager;@PostConstructpublic void init() throws Throwable{TransactionTemplate tt = new TransactionTemplate( transactionManager);tt.execute( new TransactionCallback<Object>() {@SuppressWarnings ( "unchecked" )public Object doInTransaction( TransactionStatus status ){List<User> = userService.getAllUsers();return null;}} );}
如果@Configurable无法在构造器里面使用注入的实例变量的问题,是因为默认情况下,仅在构造器执行完毕后Spring才进行依赖注入,要改变此行为,可以配置: @Configurable(preConstruction=true)
下面是一个动态注册Hibernate SessionFactory的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
ctx = new GenericApplicationContext(); ctx.addBeanFactoryPostProcessor( new BeanFactoryPostProcessor() { @Override public void postProcessBeanFactory( ConfigurableListableBeanFactory bf ) throws BeansException { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) bf; beanFactory.registerSingleton( "memDS", memDS ); BeanDefinitionBuilder sffbBeanDefBuilder = BeanDefinitionBuilder .rootBeanDefinition( TestHibernate3LocalSessionFactoryBean.class ); sffbBeanDefBuilder.addPropertyReference( "dataSource", "memDS" ); Properties hprops = new Properties(); hprops.setProperty( "hibernate.dialect", "org.hibernate.dialect.HSQLDialect" ); hprops.setProperty( "hibernate.jdbc.batch_size", "20" ); hprops.setProperty( "hibernate.show_sql", "true" ); hprops.setProperty( "hibernate.format_sql", "false" ); hprops.setProperty( "hibernate.generate_statistics", "true" ); hprops.setProperty( "hibernate.default_entity_mode", "dom4j" ); hprops.setProperty( "hibernate.transaction.factory_class", "org.hibernate.transaction.JDBCTransactionFactory" ); hprops.setProperty( "hibernate.current_session_context_class", "org.springframework.orm.hibernate3.SpringSessionContext" ); hprops.setProperty( "hibernate.default_entity_mode", "dom4j" ); sffbBeanDefBuilder.addPropertyValue( "hibernateProperties", hprops ); beanFactory.registerBeanDefinition( "sessionFactory", sffbBeanDefBuilder.getBeanDefinition() ); } } ); ctx.refresh(); |
下面是运行时获取beanFactory引用,并注册任意Bean的例子:
1 2 3 4 5 6 |
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition("cc.gmem.ClassQName"); builder.addPropertyReference("refProp", "refBeanId"); builder.addPropertyValue("prop", object); BeanDefinitionRegistry registry = (BeanDefinitionRegistry)this.beanFactory; registry.registerBeanDefinition("myNewBean", builder.getBeanDefinition()); registry.removeBeanDefinition("myNewBean"); |
参考下例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import java.util.List; import org.hibernate.Query; import org.hibernate.SessionFactory; import org.hibernate.classic.Session; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; /*设置单元测试运行类*/ @RunWith ( SpringJUnit4ClassRunner.class ) /*加载Spring配置文件*/ @ContextConfiguration ( { "/applicationContext.xml" } ) /*事务配置,defaultRollback 表示是否强制回滚事务 */ @TransactionConfiguration ( transactionManager = "txManager", defaultRollback = false ) public class HibernateTest { private static final Logger LOGGER = LoggerFactory.getLogger( HibernateTest.class ); @Test @Transactional public void testPolymorphicHqlQuery() { String hql = "from AbstractDevice ad where ad.status = 1"; Session sess = Env.get( SessionFactory.class ).getCurrentSession(); Query q = sess.createQuery( hql ); @SuppressWarnings ( "unchecked" ) List ads = q.list(); for ( AbstractDevice ad : ads ) { LOGGER.debug( ad.toString() ); } } } |
添加依赖:
1 2 3 4 5 |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> |
报错信息:Exiting with throwable: java.lang.IllegalArgumentException: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: URL [jar:file:]; nested exception is java.lang.ArrayIndexOutOfBoundsException: 51880
原因:Spring 3.x无法支持Java8的Lambda,需要升级到Spring 4.x
原因:Maven传递依赖 cxf-rt-transports-http -> spring-web-3.0.6,导致本来应该用的3.1.2被覆盖
解决:排除传递性依赖
解决:排除扫描@Configuration类,参考如下:
1 2 |
<context:component-scan base-package="cc.gmem.demo"> <context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration" /> |
在Advice类上添加注解:数字越小,越先执行,例如:@Order(1)
Spring AOP具有以下缺点:
- 无法对通知类进行通知
- 目标对象调用自身的方法,则该方法上的通知不被执行(无法感知)——这是动态代理机制的本身特点决定的
解决方法:
- 避免被代理对象的自我调用
- 使用AopContext.currentProxy() 得到当前对象的代理,对代理调用被通知方法,注意,使用currentProxy时,应使用getter/setter,避免直接访问字段。需要配置:
1234<!—基于AspectJ注解风格的Spring AOP事务配置--><aop:aspectj-autoproxy expose-proxy="true"/><!—基于XML的声明式事务配置--><aop:config expose-proxy="true">
Spring配置文件的例子:使用Spring AOP,结合AspectJ风格的切面声明
基于AspectJ注解的切面类的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Aspect //加这个注解表示为通知类 @Component public class LoggerPointcutsDefinition //切入点定义 { //注意:Spring AOP支持AspectJ切入点的子集 @Pointcut ( "@annotation(cc.gmem.mgr.common.aop.AutoLogError)" ) public void inServiceLayer(){} } @Aspect @Component public class LoggerAdvice //通知定义 { private static final Logger LOGGER = LoggerFactory.getLogger( LoggerAdvice.class ); @AfterThrowing ( value = "cc.gmem.mgr.common.aop.LoggerPointcutsDefinition.inServiceLayer()", throwing = "t" ) public void logError( Throwable t ) { LOGGER.error( t.getMessage(), t ); } } |
FactoryBean接口包含方法:
getObject()
调用applicationContext.getBean("factoryBean"),得到的是factoryBean.getObject()方法的返回值。如果Bean是单例的,那么只会调用getObject()一次。
如果想得到FactoryBean本身,需要调用applicationContext.getBean("&factoryBean")
1 |
<import resource="classpath:spring-config.xml" /> |
Leave a Reply