Spring的影响实在太大了,连Python也在向其靠拢了。
一直以为Spring只是跟Java非常亲密,原来Spring早就潜入Python了。今天本来只是想Spring如何应用在Python中,于是就Google了下,发现原来Python早已经有个叫SpringPython东东了。于是到其官网下载了springpython-reference.pdf,粗略的翻翻学习了下。感觉其实跟Spring Java非常的相似,只是类名等不同而已。其IoC、AOP、数据访问、事务等都差不多了。
于是我边看文档,边整理了一下。因为现在我还没有Python项目,用不上它,所以现在只是做个笔记,知道个大概,为以后应用它时能够快速定位做个准备。
AOP那一章节没有写,因为一是跟Spring非常的像,二是项目中一般都很少用它。
一、IoC容器
1、ObjectContainer和ApplicationContainer比较
ApplicationContainer继承ObjectContainer对象,和Spring Java一样,它同样是增强了功能,提供了Bean的pre-和post-创建逻辑。
任何实现了ApplicationContextAware的对象将会有额外的app_context属性,它代表了ApplicationContext对象的一个引用。
from springpython.context import ApplicationContext from springpython.config import XMLConfig
container = ApplicationContext(XMLConfig("app-context.xml")) service = container.get_object("MovieLister") |
继承ObjectPostProcessor对象并且定义了post_process_after_initialization方法的任何对象,在它实例创建之后 ,ApplicationContext都会执行该方法。
继承springpython.context.DisposableObject对象并且定义了destroy或是destroy_method方法的对象,在ApplicationContext销毁时,都会执行该方法。我们可以把销毁实例、释放内存等工作放在该方法之中。
需要注意的是,当同时定义了destroy和destroy_method两个方法时,ApplicationContext会优先执行destroy方法;继承自DisposableObject的对象必须提供destroy或是destroy_method方法,否则会报错,错误日志会被记录在SpringPython日志文件中。
2、对象生命周期
SpringPython提供了对象的两种生命周期:SINGLETON和PROTOTYPE。
默认情况下为SINGLETON,当类实例化时,容器会注入对象所有属性。
使用方法和Spring Java一样:scope=“prototype”。
3、SpringPython的配置
目前常见的配置有两种:XMLConfig和YamlConfig。受到Spring JavaConfig的启发,通过扩展PythonConfig并且使用@Object装饰,我们可以用纯Python代码来配置Bean。同样的,我们可以通过扩展Config来定义自己的格式。
XMLConfig跟Spring Java 2.5 XSD非常相似,下面是一个简单的例子。
<?xml version="1.0" encoding="UTF-8"?> <objects xmlns="http://www.springframework.org/springpython/schema/objects/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/springpython/schema/objects/1.1 http://springpython.webfactional.com/schema/context/spring-python-context-1.1.xsd"> <object id="MovieLister" class="springpythontest.support.testSupportClasses.MovieLister" scope="prototype"> <property name="finder" ref="MovieFinder"/> <property name="description"><ref object="SingletonString"/></property> </object> <object id="MovieFinder" class="springpythontest.support.testSupportClasses.ColonMovieFinder" scope="singleton"> <property name="filename"><value>support/movies1.txt</value></property> </object> <object id="SingletonString" class="springpythontest.support.testSupportClasses.StringHolder" lazy-init="<property name="str" value="There should only be one copy of this string"></property> </object> </objects> |
内部对象:
<object id="MovieLister3" class="springpythontest.support.testSupportClasses.MovieLister"> <property name="finder"> <object id="named" class="springpythontest.support.testSupportClasses.ColonMovieFinder"> <property name="filename"><value>support/movies1.txt</value></property> </object> </property> <property name="description"><ref object="SingletonString"/></property> </object> |
SpringPython支持多种集合属性:字典(dict)、列表(list)、属性(props)、集合(set、frozenset)、元组(tuple);
<object id="ValueHolder" class="springpythontest.support.testSupportClasses.ValueHolder"> <constructor-arg><ref object="SingletonString"/></constructor-arg> <property name="some_dict"> <dict> <entry><key><value>Hello</value></key><value>World</value></entry> <entry><key><value>Spring</value></key><value>Python</value></entry> <entry><key><value>holder</value></key><ref object="SingletonString"/></entry> <entry><key><value>another copy</value></key><ref object="SingletonString"/></entry> </dict> </property> <property name="some_list"> <list> <value>Hello, world!</value> <ref object="SingletonString"/> <value>Spring Python</value> </list> </property> <property name="some_props"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property> <property name="some_set"> <set> <value>Hello, world!</value> <ref object="SingletonString"/> <value>Spring Python</value> </set> </property> <property name="some_frozen_set"> <frozenset> <value>Hello, world!</value> <ref object="SingletonString"/> <value>Spring Python</value> </frozenset> </property> <property name="some_tuple"> <tuple> <value>Hello, world!</value> <ref object="SingletonString"/> <value>Spring Python</value> </tuple> </property> </object> |
注意set和frozenset的关系和区别(http://docs.python.org/release/2.5.2/lib/types-set.html):
构造函数配置方法:
<object id="AnotherSingletonString" class="springpythontest.support.testSupportClasses.StringHolder"> <constructor-arg value="attributed value"/> </object>
<object id="MultiValueHolder" class="springpythontest.support.testSupportClasses.MultiValueHolder"> <constructor-arg name="a"><value>alt a</value></constructor-arg> <constructor-arg name="b"><value>alt b</value></constructor-arg> </object> |
Python的基本类型(str, unicode, int, long, float, decimal.Decimal, bool和complex)在XML配置文件中有着不同的简短的表示方法:
<str id="MyString">My string</str> <unicode id="MyUnicode">Za#ó## g##l# ja##</unicode> <int id="MyInt">10</int> <long id="MyLong">100000000000000000000000</long> <float id="MyFloat">3.14</float> <decimal id="MyDecimal">12.34</decimal> <bool id="MyBool">False</bool> <complex id="MyComplex">10+0j</complex> |
同样的,XMLConfig也可以定义抽象(abstract="True")和使用继承(parent):
<object id="crm_service" parent="service" abstract="True"> <property name="port"><value>3393</value></property> </object> <object id="get_customer_id" parent="crm_service"> <property name="path"><value>/soap/invoke/get_customer_id</value></property> </object> |
当在程序中需要引用一个抽象类型的对象时,必须加上属性(ignore_abstract=True),否则会引发AbstractObjectException异常:
service = ctx.get_object("service", ignore_abstract=True) |
4、对象工厂
SpringPython提供了两种类型的工厂:ReflectiveObjectFactory和PythonObjectFactory。它们很少被我们用到,主要是用在不的同配置扫描器中。
5、在运行时查询或是修改ApplicationContent
ApplicationContext实例暴露了两个属性和一个方法使用我们在运行时能够知道其当前状态并且动态改变它:object_defs、objects、get_objects_by_type(type, include_type=True);
二、AOP编程
三、数据访问
1、数据库模板:
在没有使用数据库模板情况下,我们传统的写法如下:
conn = MySQL.connection(username="me", password"secret", hostname="localhost", db="springpython") cursor = conn.cursor() results = [] try: cursor.execute("select title, air_date, episode_number, writer from tv_shows where name = %s", ("Monty Python",)) for row in cursor.fetchall(): tvShow = TvShow(title=row[0], airDate=row[1], episodeNumber=row[2], writer=row[3]) results.append(tvShow) finally: try: cursor.close() except Exception: pass conn.close() return results |
在传统的数据库访问中,所有的数据库操作都是建立连接、执行查询、获取数据、释放连接,最后是返回结果。
下面是在使用了数据库模板情况下的做法:
""" The following part only has to be done once.""" from springpython.database.core import * from springpython.database.factory import * connectionFactory = MySQLConnectionFactory(username="me", password="secret", hostname="localhost", db="springpython") dt = DatabaseTemplate(connectionFactory)
class TvShowMapper(RowMapper): """This will handle one row of database. It can be reused for many queries if they are returning the same columns.""" def map_row(self, row, metadata=None): return TvShow(title=row[0], airDate=row[1], episodeNumber=row[2], writer=row[3])
results = dt.query("select title, air_date, episode_number, writer from tv_shows where name = %s", \ ("Monty Python",), TvShowMapper()) (100,), TvShowMapper()) results = dt.query("select title, air_date, episode_number, writer from tv_shows where upper(title) like %s", \ ("%CHEESE%",), TvShowMapper()) results = dt.query("select title, air_date, episode_number, writer from tv_shows where writer in ('Cleese', 'Graham')", rowhandler=TvShowMapper()) |
从上面可以看出,在使用了模板后,程序所要做的,就是提供一个RowMapper,从而使它返回最终结果。
一种便利的RowMapper:SimpleRowMapper(TvShow)。它自动把数据表列和对象属性关联起来。
2、把数据行映射成字典
results = dt.query("select title, air_date, episode_number, writer from tv_shows where episode_number < %s", \ (100,), DictionaryRowMapper()) |
四、事务管理
1、事务模板TransactionTemplate
class Bank: def __init__(self): self.factory = factory.MySQLConnectionFactory("springpython", "springpython", "localhost", "springpython") self.dt = DatabaseTemplate(self.factory) self.txManager = ConnectionFactoryTransactionManager(self.factory) self.txTemplate = TransactionTemplate(self.txManager)
try: self.txTemplate.execute(txDefinition()) print "If you made it to here, then your transaction has already been committed." except InvalidBankAccount, InsufficientFunds: print "If you made it to here, then your transaction has already been rolled back." |
2、使用装饰@transactional
@transactional def transfer(self, transfer_amount, source_account_num, target_account_num): self.withdraw(transfer_amount, source_account_num) self.deposit(transfer_amount, target_account_num) |
3、事务传播属性
当使用@transactional装饰时,同时也可以指定事务传播属性,比如:@transactional(["PROPAGATION_REQUIRED"])。
事务一共有4种传播属性:
PROPAGATION_SUPPORTS:程序能够在有事务或是没有事务的环境中运行,也就是有没有无所谓;
PROPAGATION_REQUIRED:如果当前没事务,则开始一个事务;
PROPAGATION_MANDATORY:程序逻辑只能在事务中运行,如果没有,则产生异常;
PROPAGATION_NEVER:与上面的相反,程序只能在无事务的环境中运行,如果有事务,则产生异常。
===========================================
如有批评、指教、疑惑,请:obullxl@163.com
祝大家使用JAVA愉快!