/pySimpleSpringFramework

模仿 java spring 实现的简易python版

Primary LanguagePythonApache License 2.0Apache-2.0

pySimpleSpringFramework

Python版的Spring框架,实现 IOC、AOP、数据库、多数据源支持、事务及事务传递性、线程池、进程池、http服务等spring最常用功能。

让java程序员可以快上手的python框架 规范代码 快速开发

依赖库

conda install pandas  -c conda-forge -y
conda install colorlog  -c conda-forge -y
pip install PyYAML -i  https://pypi.tuna.tsinghua.edu.cn/simple/
pip install sqlalchemy -i  https://pypi.tuna.tsinghua.edu.cn/simple/
pip install uvicorn -i  https://pypi.tuna.tsinghua.edu.cn/simple/
pip install fastapi -i  https://pypi.tuna.tsinghua.edu.cn/simple/
pip install tqdm -i  https://pypi.tuna.tsinghua.edu.cn/simple/

一. 装饰器

使用装饰器可以像java中使用注解一样

1. ComponentScan 
	类装饰器添加要扫描的模块支持多个
    
    @ComponentScan("../../test_modules1", "../../test_modules2")
    class ServiceApplication(ApplicationStarter):
        pass
    
2. ConfigDirectories
	类装饰器添加要扫描的配置文件目录
    
    @ConfigDirectories("../../config")
    class ServiceApplication(ApplicationStarter):
        pass

3. Component
	类装饰器标记为组件从而可以被扫描到默认为单例
    
    @Component
    class A:
        pass
    
4. Order
	/方法 装饰器, 排序从小到大@Component
    @Order(1)
    class A:
        pass
    
5. Autowired
	方法 装饰器, 注入 bean方法名必须以 set  _set 开头. 例子如下: 
    
    @Component
    class A:
        pass
    
    @Component
    class B:
        @Autowired
        def set_params(self, a):
            self.a = a
    
6. Value
	方法 装饰器, 注入配置文件信息@Component
    class A:
   		@Value({"spring.profiles.include": "v1"})
        def __init__(self):
            self.v1 = None
    
    spring.profiles.include为配置文件的 key, v1 是自己定义的变量把对应的值赋值给 self.v1
    
7. Scope
	 装饰器, SCOPE("singleton") / SCOPE("prototype") , singleton为单例prototype为原型
    
    @Component
    @Scope("prototype")
    class A:
        pass

8. Aspect
	 装饰器, 标记为AOP类
    
    @Component
    @Aspect
    class AspectTest1:
        pass
    
9. Pointcut
	方法 装饰器, 定义切入点. 例子如下:
        
    @Pointcut({"execution": ["A.f1", "B.f2"]})
    def aspectPointcut1(self):
        pass
    
   	@Pointcut({"execution": ["*.*"]})
    def aspectPointcut2(self):
        pass
    
10. Before
	方法 装饰器
    
	@Before(["aspectPointcut1"])
    def aspectBefore1(self, joinPoint):
        print(">>>>>>>>> aspectBefore1 = > ", joinPoint.target, joinPoint.method, joinPoint.args, joinPoint.kwargs)
        
11. After
	方法 装饰器
    
	@After(["aspectPointcut1", "aspectPointcut4"])
    def aspectAfter1(self, joinPoint):
        print(">>>>>>>>> aspectAfter1 = > ", joinPoint.target, joinPoint.method, joinPoint.args, joinPoint.kwargs)
        
12. AfterReturning
	方法 装饰器
    
    def aspectAfterReturning2(self, joinPoint, return_object):
        print(">>>>>>>>> aspectAfterReturning2 = > ", joinPoint.target, joinPoint.method, joinPoint.args,
        joinPoint.kwargs,
        return_object)

13. AfterReturning
	方法 装饰器
    
    def aspectAfterReturning3(self, joinPoint, return_object):
        return_object.return_value = return_object.return_value + 10
        print(">>>>>>>>> aspectAfterReturning3 = > ", joinPoint.target, joinPoint.method, joinPoint.args, 
        joinPoint.kwargs, 
        return_object)        

14. Round
	方法 装饰器, 这个比较特殊不自动调用原方法需要自己调用
    
    @Around(["aspectPointcut1"])
    def aspectAround2(self, proceed_join_point):
        print(">>>>>>>>>  around before aspectAround2", proceed_join_point)
        result = proceed_join_point.proceed()
        # print("执行程序,返回结果: ", result)
        print(">>>>>>>>>  around after aspectAround2", proceed_join_point)
        return result

15. Ds
	/方法 装饰器,切换数据源类上加的话所有方法都会切换到指定数据源方法上的优先级最高
    
    @Component
    @Ds("ds1")
    class A:
        @Ds("ds2")
        def insert_user():
            pass
    
    
16. SelectInsertupdateDelete
	方法 装饰器
   
	@Component
    class Mapping:
        @Select("select * from user")
        def get_users(self):
            pass

        @Insert("insert into user values('#{username}', '#{password}')")
        def insert_user1(self, username, password):
            pass

        @Update("update user set password='#{password}' where username='#{username}'")
        def insert_user2(self, username, password):
            pass

        @Delete("delete from user")
        def delete_users(self):
            pass
    
17. Transactional
	/方法 装饰器. 
    @Transactional(Propagation.REQUIRED) :  如果当前没有事物就创建一个事务如果已有就使用当前事务
    @Transactional(Propagation.REQUIRES_NEW): 启用一个新事物执行
    
    @Component
	@Transactional()  # 默认为 Propagation.REQUIRED
    class DsTest:
        @Transactional(Propagation.REQUIRES)
        def insert_user1(self, username, password):
            self._mapping.insert_user(username, password)
    	
        @Transactional(Propagation.REQUIRES_NEW)
        def insert_user2(self, username, password):
            self._mapping.insert_user(username, password)
           
    

二. 配置文件

配置文件使用 yaml 和 properties ,和java 一样。

目录结构如下 :

config
   application.properties
   application-dev.properties
   application-dev.yaml
   application-prod.properties
   application-prod.yaml

application.properties

# 指定使用哪个配置
spring.profiles.include=dev

application-dev.yaml

数据库操作使用的是 pandas 和 sqlalchemy

数据库配置和线程池配置如下:

datasource:
  # 是否要打印sql
  debug_sql: true
  sources:
    # 数据源1
    source1:
      url: postgresql+psycopg2://192.168.101.152:5432/test
      username: postgres
      password: postgres
      # 指定schema, 这里用 public
      connect_args: {"options": "-c search_path=public"}
      # 如果不设置就全部使用默认值
      pool:
        #    -pool_size=5, 连接数大小,默认为 5,正式环境该数值太小,需根据实际情况调大
        size: 20
        #    -pool_recycle, 默认为 600, 推荐设置为 7200, 即如果 connection 空闲了 7200 秒,自动重新获取,以防止 connection 被 db server 关闭。
        recycle: 600
        #    -max_overflow=10, 超出 pool_size 后可允许的最大连接数,默认为 10, 这 10 个连接在使用过后,不放在 pool 中,而是被真正关闭的。
        max_overflow: 1000
        #    -pool_timeout=30, 获取连接的超时阈值,默认为 30 秒
        timeout: 10
    # 数据源2
    source2:
      url: mysql+pymysql://127.0.0.1:3306/test
      username: root
      password: 112233QQwwee
      pool:
        size: 20
        recycle: 600
        max_overflow: 1000
        timeout: 10


# 不设置默认为当前机器的cpu数量
task:
  execution:
    pool:
      max_size: 20      # 设置线程池的最大线程数

三. 代码模块创建器

from pySimpleSpringFramework.spring_core.util.codeGenerator.generator import AppCodeGenerator

if __name__ == '__main__':
    AppCodeGenerator.generate_app_and_rest_template()
    # 如果不需要 http服务,使用 AppCodeGenerator.generate_app_template

指定以上代码后会创建3个文件:

  1. applicationStarter.py 类似java springboot的 SpringApplication
import os
import sys

from pySimpleSpringFramework.spring_core.applicationStarter import ApplicationStarter
from pySimpleSpringFramework.spring_core.type.annotation.classAnnotation import ComponentScan, ConfigDirectories


# 把父目录放入path, 父目录就是包。 这个需要自己调整
root_model_path = os.path.dirname(os.path.dirname(os.getcwd()))
print("root_model_path=", root_model_path)
sys.path.append(root_model_path)


# 基于 root_model_path 的相对的位置, 因为 root_model_path 就是包
@ComponentScan("../../pySimpleSpringFramework/spring_test/test_modules")
# 这里修改成自己的配置文件位置(相对当前这个启动文件的位置)
@ConfigDirectories("../../config")
class ServiceApplication(ApplicationStarter):
    def __init__(self):
        super().__init__()
        self.__application_context = None
    
    @property
    def application_context(self):
        return self.__application_context

    def main(self, application_context):
        self.__application_context = application_context
        
        # 这里执行自己的启动逻辑
        # ...


serviceApplication = ServiceApplication()

if __name__ == '__main__':
    serviceApplication.run(True)

        
  1. restService.py

    使用 fastapi 作为http服务组件。你也可以用自己喜欢的

import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel

# 在这里导入自己的serviceApplication实例
from pySimpleSpringFramework.spring_test.applicationEntrypoint import serviceApplication

rest_app = FastAPI()


@rest_app.get("/get_test/id={opId}")
async def get_template(opId: int):
    print("opId = ", opId)
    # 这里是获取bean的例子
    # myBean = serviceApplication.application_context.get_bean("myBean")
    return "ok"


class TodoItem(BaseModel):
    title: str
    description: str


@rest_app.post("/post_test", response_model=TodoItem)
async def post_template(todo: TodoItem):
    # 这里是获取bean的例子
    dsTest = serviceApplication.application_context.get_bean("dsTest")
    try:
        dsTest.delete_users()
    except:
        pass

    print("insert_user: ", dsTest.insert_user('ww', '112345'))
    print(dsTest.get_users())
    return todo


def start_rest_service(port):
    # 启动rest服务
    uvicorn.run(rest_app, host="0.0.0.0", port=port, reload=False)
  1. start.py
# 在这里导入自己的serviceApplication 和 start_rest_service
from pySimpleSpringFramework.spring_test.applicationEntrypoint import serviceApplication
from pySimpleSpringFramework.spring_test.restService import start_rest_service

if __name__ == '__main__':
    # 启动app
    serviceApplication.run(True)

    # 启动rest服务
    start_rest_service(8000)

根据自己实际情况修改好后,执行 start.py 启动程序

四. 使用

把 pySimpleSpringFramework文件夹放到 site-packages 下面即可

相关的系统bean名称

​ 配置文件操作: applicationEnvironment

​ 数据库操作: databaseManager

​ 线程池 / 进程池操作: executorTaskManager

applicationEnvironment:

self._application_environment = self.application_context.get_bean("applicationEnvironment")
max_size = self._application_environment.get("task.execution.pool.max_size")

executorTaskManager:

def callback_function(future):
	future.result()

if __name__ == '__main__':	
    serviceApplication = ServiceApplication()
    serviceApplication.run(True)
    
	executorTaskManager = serviceApplication.application_context.get_bean("executorTaskManager")
	executorTaskManager.submit(task_parse, True, callback_function, start, end)
	executorTaskManager.wait_completed()