AOP, 远程调用 , 数据库封装, Security, DI, JavaMS ,定时服务
容器负责将协作组件的引用给于多个组件
国际化支持、载入资源的通用方案、向注册为监听器的bean发送事件, 有ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, XmlWebApplicationContext(web系统的xml文件中载入)
注入方式:
Constructor-arg 通过 value (值) ref(对象) index(同一类型有多个参数) 实现;
Property 方式 。
value ref map list(Collection和array) set(Collection和array) props(对应Properties) : 这些元素可按需要彼此嵌套。
<null /> (装配空值)
自动装配:
byName(属性名和bean id相同)
byType(属性类型与bean类型相同,且唯一)
constructor
autodect四种
好处是减少配置信息,坏处是可读性差,如果重构修改了property的名称,或class就麻烦了。
手动装配:
可与自动装配混合使用,解决自动状态当有多个类实例,使用byType抛异常的问题
Bean范围:
singleton(单例,默认);
prototype(每次使用创建新实例);
request(spring-mvc可用,request中可用),session(session范围可用),
global-session(全局http会话中,portlet中使用)
工厂方式创建bean
不使用构造函数,如单例模式,可使用factory-method 指定创建函数
初始化或销毁bean
Init-method
Destroy-method
将共同的配置移动到父bean, 减少spring中的冗余配置信息。Class和property中有公用的都可以放到父bean中,如下:
<bean id="authFactory" abstract="true">
<bean id="config" class="XX" parent="authFactory">
方法替换
可在运行时使用新的实现替换现有方法(抽象或具体的)。
场景:当开发时不能确定方法的实现,可以在程序的classpath中放置jar包,在其中放置实际的方法替换类。
使用 <replace-method>与MethodReplacer的实现类配合实现
获取器注入:
在运行时用新的实现代替并返回特定的bean
只需要 <lookup-method>即可,但其适用范围较窄,只针对返回值不是void的方法,用指定的bean代替返回值
注入不是通过spring实例化的bean
自动转换(复杂属性的配置):<property name=”phoneNumber” value=”888-5555”>, 可以有String_>class, date, File(文件路径),StringArray(逗号分隔的字符串),URL
自定义属性转换: extends PropertyEditorSupport
创建和初始化后对bean进行处理: 实现BeanPostProcessor中的方法
BeanFactoryPostProcessor对整个Spring进行处理,在全部bean加载后,在任何一个bean被实例化之前。
从属性文件家在信息:
应该将程序特性和部署特性分开,部署需要的属性写在独立的properties文件中
配置propertyplaceholderConfigurer, 或者引入 context的namespace,如下:
<context:property-placeholder location="server.properties" />
自定义事件:
spring中任何bean都可作为事件的发布者和监听者。
1. 继承ApplicationEvent类自定义事件
2. 发布事件: context.publishEvent
3. 监听者实现ApplicationListener接口,实现onApplicationEvent函数
提取文本消息:
将文本放在独自的properties文件中,用于文本经常改变,程序需要实现国际化。
在xml中创建 ResourceBundelMessageSource的实例; 通过context.getMessage获取文本
让bean了解容器
如果需要将bean的名称或id 记录到日志中,需要实现BeanNameAware接口。 当加载xml时,spring看到bean实现了BeanNameAware接口会自动调用setBeanName 将id或name传递进去。
通过applicationContextAware 可通过编程从容器获取属性
脚本话bean
作用:在程序部署完后改变程序的行为,而不需要编译和部署
使用java实现框架和可变部分的接口,使用脚本语言,如ruby、groovy、beanshell 实现接口, 需要用到标签 <lang-groovy> <lang-property> ,并设置脚本刷新频率<refresh-check-delay>
AOP作用
主要解决交叉事务和横向业务逻辑的耦合,如认证、日志、事务等功能, 使用AOP,对于业务逻辑来说并不知道辅助功能的存在
被标记为final的方法不能被通知
基本概念
通知 advice: 具体的功能和什么时候执行:方法之前、之后
连接点: 可插入的点,如函数
切入点: 缩小连接点的范围,一般使用正则表达式 aspectJ定义
切面:通知和切入点的集合
创建spring切面
1. 创建通知:
利用MethodInterceptor 可实现方法执行前、返回后、抛出异常后通知的结合(只是把原函数作为一种函数调用,抛出异常用try catch, 返回后对函数返回值处理)
原函数的执行使用 proceed()方法
如果只在执行前调用,则实现接口 MethodBeforeAdvice
无法实现属性更新和构造器的切面
2. 定义切入点
使用正则表达式(RegexpMethodPointcutAdvisor)和AspectJ定义(配置AspectJExpressionPointCutAdvisor 定义切入点和通知类, 切入点如 execution(* *.perform(..))) 执行方法时(返回类型 类名.方法名(参数))
3. 使用ProxyFactoryBean
自动代理:根据定义的切面自动创建代理类<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
定义纯粹的POJO切面
使用namespace aop进行创建
<aop:config>
<aop:aspect ref=”提供切面功能的pojo类”>
<aop:pointCut …>
<aop:before <aop:after..
</aop:config>
实例:
<aop:config> <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor"> <aop:pointcut id="idempotentOperation" expression="execution(* com.xyz.myapp.service.*.*(..))"/> <aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation"/> </aop:aspect> </aop:config> <bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor"> <property name="maxRetries" value="3"/> <property name="order" value="100"/> </bean>
注入AspectJ切面
使用AspectJ提供更强大的AOP功能
spring的数据库访问哲学
spring的数据访问异常丰富,而且与特定的持久层实现无关,这些异常都继承于DataAcessException,不需要强制捕获
spring中将固定与变化分为两类:模板和回调,模板管理固定部分,而回调处理自定义数据代码
编写DAO时,可继承于JdbcDAOSupport类
配置数据源
从连接池通过JNDI获取数据源(JndiObjectFactoryBean 类指定);小程序使用(DriverManagerDataSource)分配链接
在spring中使用JDBC
JDBC模板能够管理资源和处理异常,我们只需编写从数据库读取数据所需的代码,有以下三种
1. JdbcTemplate
使用update(sql,Object[]) 完成update delete insert操作,sql可使用? 参数化,第二个参数使用object数组传入相应参数;
使用query(sql,Object[],new RowMapper()) 执行查询后调用RowMapper对每一行数据进行包装,并返回List;
execute(StatementCallBack<T> action) 执行ddl语句;
batchUpdate;
public int[] batchUpdate(final List actors) {
int[] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, ((Actor)actors.get(i)).getFirstName());
ps.setString(2, ((Actor)actors.get(i)).getLastName());
ps.setLong(3, ((Actor)actors.get(i)).getId().longValue()); }
public int getBatchSize() {
return actors.size();
}
} );
return updateCounts; }
call(存储过程)
2. NamedParameterJdbcTemplate
在sql中支持命名参数 :name,而不是?
3. SimpleJdbcTemplate
SimpleJdbcTemplate的update支持变长参数
RowMapper可以映射到具体的对象,而不是object
自定义Dao类继承于JDBCDaoSupport(NamedParameterJdbcDaoSupport:获取Named*Template , SimpleJDBCDaoSupport获取SimpleJdbcTemplate),通过getTemplate()获取template 进行后续操作
Hibernate支持更多功能
延迟加载:如PurchaseOrder对象集合,其中每个对象包含LineItem对象的集合,如果只是插入PurchaseOrder属性,则没必要加载LineItem
期望获取
级联
缓存
效果:对数据库查询结果记住,以备下次查询时使用; 缓存过期、刷新和溢出
spring使用切面来实现缓存,将通知透明地应用于bean的方法来实现缓存
依赖于第三方的缓存方案,如EHCache、JBoss Cache等,使用以下配置元素配置
配置元素 用途 <namespace:annotations> 以java5注释来声明被缓存的方法 <namespace:commons-attributes> jakarta通用属性来声明被缓存的方法 <namespace:config> 在spring xml中配置 <namespace:proxy> 声明个代理来声明被缓存的方法
缓存配置: 时间,容量,缓存替换方案
缓存使用: 缓存的bean.method,刷新缓存,
理解事务
spring使用回调机制将真实的事务实现从事务代码中抽象出来,如果应用程序要实现跨越多个资源的事务请求,需要使用支持分布式的JTA实现
有两种方法:程序事务(精确控制);声明事务(简单操作)
选择事务管理器
当使用JDBC时,可使用DataSourceTransactionManager提供事务管理,在xml中定义bean (transactionManager), 其实使用java.sql.Connection来管理事务,调用commit提交,rollback回滚
当使用hibernate,使用org.spring.framework.orm.hibernate3.HibernateTransactionManager中的类,底层委托给从当前Hibernate会话中检索到的org.hibernate.Transaction对象,调用其commit和rollback
在spring中编写事务
以内隐类方式实现TransactionCallBack接口的doTransaction(org.springframework.transaction.support.transactionTemplate tx)方法,调用tx.setRollBackOnly()回滚,默认执行事务 ,如下:
public class SimpleService implements Service { // single TransactionTemplate shared amongst all methods in this instance private final TransactionTemplate transactionTemplate; // use constructor-injection to supply the PlatformTransactionManager public SimpleService(PlatformTransactionManager transactionManager) { Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); } public Object someServiceMethod() { return transactionTemplate.execute(new TransactionCallback() { // the code in this method executes in a transactional context public Object doInTransaction(TransactionStatus status) { updateOperation1(); return resultOfUpdateOperation2(); } }); } }如果不需要返回值,更方便的方式是创建一个 TransactionCallbackWithoutResult 的匿名类,如下:
transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateOperation1(); updateOperation2(); } });
声明式事务
1. 事务的五个属性
传播行为:
定义新的事务应该被启动还是挂起,一个方法是否应该在事务上下文中运行,常用的有PROPAGATION_REQUIRED(必须在一个事务中运行,现在没有则开启新事务)和 PROPAGATION_MANDATORY(无事务抛出异常)
隔离级别:
受其他并发事务影响的程度,多个事务操作同一数据会有如下问题:
1. 脏数据在临时更新(脏读)中产生。事务A更新了某个数据项X,但是由于某种原因,事务A出现了问题,于是要把A回滚。但是在回滚之前,另一个事务B读取了数据项X的值(A更新后),A回滚了事务,数据项恢复了原值。事务B读取的就是数据项X的就是一个“临时”的值,就是脏数据。是 未提交
2. 不可重复读: 在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
DEFAULT:默认隔离级别, SERIALIZABLE:最高隔离级别可解决上面问题
只读:
是只读事务还是读写事务,如果是只读事务数据库可以优化。
优化只对可能启动一个新事务的传播行为有效
超时:
防止数据库被长时间锁定,只对启动一个新事务的传播行为有效
回滚规则:
那些异常引起回滚,那些不引起
在spring中使用AOP实现事务的控制支持
1. 先来一个DataSource
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean>2. 得到TransactionManager
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>3. 创建事务Advice实例
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) –>
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... –>
<tx:attributes>
<!-- all methods starting with 'get' are read-only 什么异常回滚 –>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>/>
<!-- other methods use the default transaction settings (see below) –>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
其中 method相关参数:
4. 定义切面
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
5. 调用方式
和原来类似
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
FooService fooService = (FooService) ctx.getBean("fooService");
fooService.insertFoo (new Foo());
}
注释驱动的事务
使用@Transaction 对类和类的方法进行定义,不建议在接口定义, 声明式事务的推荐做法
@Transactional(readOnly = true) public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { // do something } // these settings have precedence for this method @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public void updateFoo(Foo foo) { // do something } }
选择声明还是编程
大量的选择声明式事务,默认也选择