2019-9-14 2628 0
2019-9-14 1442 0
Python极客

新博客:https://blog.bigdataboy.cn/article/455.html说明上篇:https://blog.bigdataboy.cn/article/454.html接上篇:只能在添加job时一次性修改,而无法实现任务中途动态修改state的值,所以继续改造思路其实改造也很简单,首先通过job_id获取到Job,更新Job的state属性,然后执行相应的停止(pause)、恢复(resume)等函数,所以我们就在停止恢复这些函数上动手脚分析源码job=scheduler.get_job(job_id=job_id)job.pause()继续看pause()内部逻辑,本质调用的是BaseScheduler类下的pause_job()方法defpause(self):"""Temporarilysuspendtheexecutionofthisjob...seealso:::meth:`~apscheduler.schedulers.base.BaseScheduler.pause_job`:returnJob:thisjobinstance"""self._scheduler.pause_job(self.id,self._jobstore_alias)returnself继续看pause_job()方法内部逻辑,发现本质调用的是BaseScheduler类下的modify_job()方法defpause_job(self,job_id,state,jobstore=None):"""Causesthegivenjobnottobeexecuteduntilitisexplicitlyresumed.:paramstr|unicodejob_id:theidentifierofthejob:paramstr|unicodejobstore:aliasofthejobstorethatcontainsthejob:returnJob:therelevantjobinstance"""returnself.modify_job(job_id,jobstore,next_run_time=None)继续看modify_job()方法内部逻辑,发现就更新了job的状态,其实就是修改defmodify_job(self,job_id,jobstore=None,**changes):"""Modifiesthepropertiesofasinglejob.Modificationsarepassedtothismethodasextrakeywordarguments.:paramstr|unicodejob_id:theidentifierofthejob:paramstr|unicodejobstore:aliasofthejobstorethatcontainsthejob:returnJob:therelevantjobinstance"""withself._jobstores_lock:job,jobstore=self._lookup_job(job_id,jobstore)job._modify(**changes)ifjobstore:self._lookup_jobstore(jobstore).update_job(job)self._dispatch_event(JobEvent(EVENT_JOB_MODIFIED,job_id,jobstore))#Wakeuptheschedulersincethejob'snextruntimemayhavebeenchangedifself.state==STATE_RUNNING:self.wakeup()returnjob总结其实需要改动的就在传参上,到最后给modify_job()函数,一并修改就可以了改造apscheduler/job.pydefpause(self):"""Temporarilysuspendtheexecutionofthisjob...seealso:::meth:`~apscheduler.schedulers.base.BaseScheduler.pause_job`:returnJob:thisjobinstance"""#self._scheduler.pause_job(self.id,self._jobstore_alias)#原self._scheduler.pause_job(self.id,self.state,self._jobstore_alias)returnselfapscheduler/schedulers/base.pydefpause_job(self,job_id,state,jobstore=None):#添加state参数"""Causesthegivenjobnottobeexecuteduntilitisexplicitlyresumed.:paramstr|unicodejob_id:theidentifierofthejob:paramstr|unicodejobstore:aliasofthejobstorethatcontainsthejob:returnJob:therelevantjobinstance"""returnself.modify_job(job_id,jobstore,state=state,next_run_time=None)#传入state其他就不用修改了,modify_job()函数内部是不定长获取的,所以恢复(resume)也是一样验证停止任务,然后再使用job_id获取

Python极客

ps:好久没发文章,悄悄翻一个新博客:https://blog.bigdataboy.cn/article/454.html说明最近在使用apscheduler过程中,为了控制每个Job的状态,发现有点困难,所以就尝试改造一下目标为Job类添加一个dict类型的属性,用来保存用户自定义的数据,例如当job在执行过程发生错误,就可以把状态改成error并停止job改造地方一apscheduler/job.py#增加state属性__slots__=('_scheduler','_jobstore_alias','id','trigger','executor','func','func_ref','args','kwargs','name','misfire_grace_time','coalesce','max_instances','next_run_time','__weakref__','state')地方二#在返回值中增加statedef__getstate__(self):...return{'version':1,'id':self.id,'func':self.func_ref,'trigger':self.trigger,'executor':self.executor,'args':args,'kwargs':self.kwargs,'name':self.name,'misfire_grace_time':self.misfire_grace_time,'coalesce':self.coalesce,'max_instances':self.max_instances,'next_run_time':self.next_run_time,'state':self.state####}地方三#在设置job属性时,增加statedef__setstate__(self,state):ifstate.get('version',1)>1:raiseValueError('Jobhasversion%s,butonlyversion1canbehandled'%state['version'])self.id=state['id']self.func_ref=state['func']self.func=ref_to_obj(self.func_ref)self.trigger=state['trigger']self.executor=state['executor']self.args=state['args']self.kwargs=state['kwargs']self.name=state['name']self.misfire_grace_time=state['misfire_grace_time']self.coalesce=state['coalesce']self.max_instances=state['max_instances']self.next_run_time=state['next_run_time']self.state=state['state']####地方四#在add_job函数上,添加上state参数defadd_job(self,func,trigger=None,args=None,kwargs=None,id=None,name=None,state={},####misfire_grace_time=undefined,coalesce=undefined,max_instances=undefined,next_run_time=undefined,jobstore='default',executor='default',replace_existing=False,**trigger_args):...#把state参数添加到job_kwargs字典job_kwargs={'trigger':self._create_trigger(trigger,trigger_args),'executor':executor,'func':func,'args':tuple(args)ifargsisnotNoneelse(),'kwargs':dict(kwargs)ifkwargsisnotNoneelse{},'id':id,'name':name,'misfire_grace_time':misfire_grace_time,'coalesce':coalesce,'max_instances':max_instances,'next_run_time':next_run_time,'state':state####}地方五#在参数修复函数里,添加上state,并进行检测def_modify(self,**changes):...if'state'inchanges:state=changes.pop('state')ifnotisinstance(state,dict):raiseTypeError('statemustbeadict')approved['state']=state验证在以上五个地方添加上,就可以实现为每个job开辟一个用户自定义属性的存储添加任务可以看到job能够正常获取到state获取任务获取任务时state也是存在的当任务发生异常时按照官网的方案,监控任务状态需要添加监听#调度监听fromapscheduler.schedulers.backgroundimportBackgroundSchedulerfromapscheduler.eventsimportEVENT_JOB_ERROR,EVENT_JOB_MISSED,\EVENT_JOB_ADDED,EVENT_JOB_REMOVED,EVENT_JOB_EXECUTED,EVENT_JOB_MODIFIEDscheduler=BackgroundScheduler(timezone='Asia/Shanghai')defapscheduler_listener(event):ifevent.code==EVENT_JOB_ERROR:#任务发送错误时job=scheduler.get_job(event.job_id)job.state['state']='error'job.pause()logger.error(f'jobid:{event.job_id}traceback:{event.traceback}')scheduler.add_listener(apscheduler_listener,EVENT_JOB_ADDED|EVENT_JOB_REMOVED|EVENT_JOB_MODIFIED|EVENT_JOB_EXECUTED|EVENT_JOB_ERROR|EVENT_JOB_MISSED)scheduler.start()可以看到当任务发生错误时,就可以改变任务状态,并停止该任务

编程杂谈

新博客:https://blog.bigdataboy.cn/article/453.html什么是事务事务是逻辑上的一组操作,主要是对数据库的数据的操作,要么都执行,要么都不执行,重点是都,所以当这组操作不一致时,那么就自动进行回滚,把数据恢复到这组操作之前,这样数据库的数据就不会出错了。转账案例事务这个概念最早是在数据库接触到,但是感觉并不明显,这里可以通过这个案例感受一下a给b转账100正常情况a的余额减少100b的余额增加100publicvoidtransfer(Stringout,Stringin,Doublemoney){//转账accountDao.inMoney(in,money);accountDao.outMoney(out,money);}如果在转账过程中出现异常,不使用事务的话,就会出现,一方钱减少了,而另一方确没有增加publicvoidtransfer(Stringout,Stringin,Doublemoney){//转账accountDao.inMoney(in,money);//期间出现报错accountDao.outMoney(out,money);}Spring事务处理第一步、定义该函数需要事务处理,在接口上定义项目使用的DataSource要一样,不然事务无法实现,案例中指MyBatis初始化SqlSessionFactoryBean时传入的DataSource和JDBC交给Spring的事务的DataSource一致publicinterfaceAccountService{@Transactional//该注解开启事务publicvoidtransfer(Stringout,Stringin,Doublemoney);}第二步、在JdbcConfig类,定义事务管理Bean交给Spring容器管理@ConfigurationpublicclassJdbcConfig{@Value("${jdbc.driver}")publicStringdriverClassName;@Value("${jdbc.url}")publicStringurl;@Value("${jdbc.username}")publicStringusername;@Value("${jdbc.password}")publicStringpassword;@BeanpublicDataSourcedataSource(){DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName(driverClassName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);returndataSource;}@Bean//把事务管理器交给Spring管理publicDataSourceTransactionManagerdataSourceTransactionManager(DataSourcedataSource){DataSourceTransactionManagertransactionManager=newDataSourceTransactionManager();transactionManager.setDataSource(dataSource);returntransactionManager;}}第三步、在SpringConfig使用注解开启事务@Configuration@ComponentScan({"cn.bigdataboy"})@PropertySource({"classpath:jdbc.properties"})@Import({MyBatisConfig.class,JdbcConfig.class})@EnableTransactionManagement//开启事务管理器publicclassSpringConfig{}测试注意默认状态下并不是所有的异常都会回滚,可以通过事务属性进行控制事务角色在上述案例中,小事务一和二(事务协调员)会被加入到大的事务(事务管理员),由事务管理员统一管理事务管理员:在Spring中通常指开启事务的方法事务协调员:在Spring中通常指数据层方法,也可以是业务层方法事务的属性事务的属性能使事务管理员对事务协调员精细化的管理属性作用实例readOnly设置是否为只读事务readOnly=true只读事务timeout设置事务超时时间timeout=-1(永不超时)rollbackFor设置事务回滚异常(class)rollbackFor={NullPointerException.class}rollbackForClassName设置事务回滚异常(String)同上,只是参数为字符串noRollbackFor设置事务不回滚异常(class)noRollbackFor={NullPointerException.class}noRollbackForClassName设置事务不回滚异常(String)同上,只是参数为字符串propagation事务传播见下面事务传播默认情况下,发送错误时,子事务全部都要回滚,那有一些事务是不需要回滚(记录日志到数据库),这也是需要实现的,子事务可以控制自己是否需要主事务控制。publicinterfaceAccountLogService{@Transactional(propagation=Propagation.REQUIRES_NEW)//该注解开启事务publicvoidadd(Stringout,Stringin,Doublemoney);}propagation参数说明(T代表事务)传播属性事务管理员事务协调员REQUIRED(默认)开启T/无加入T/新建T2REQUIRES_NEW开启T/无新建T2/新建T2SUPPORTS开启T/无加入T/无NOT_SUPPORTED开启T/无无/无MANDATORY开启T/无加入T/ERRORNEVER开启T/无ERROT/无NESTED开启T/无设置savePoint,一但事务回滚,事务将回滚到savaPoint处,交由客户端提交/回滚案例代码:https://pan.bigdataboy.cn/s/QgVFn

编程杂谈

新博客:https://blog.bigdataboy.cn/article/451.html说明学校的小组课程,自行拟定项目,我们小组做的是客户端控制风扇、警报、大门、灯泡,打开摄像头并识别其中的车牌号,还实现了温度异常监控,自动开关灯,因为没有板子,用仿真软件模拟的控制设备。实现效果采用技术我是使用Python实现上述功能服务端:FastAPI、apscheduler客户端:PyQt5通信方式客户端---post--->服务端(单向)客户端<---WebSocket--->服务端(双向)服务端<--串口-->仿真平台架构图服务端启动事件在FastAPI的事件里,连接串口,启动后台监控任务@app.on_event("startup")asyncdefstartup_event():start_serial("COM41",115200)logger.info("串口连接成功")scheduler.add_job(MonitorService().run,trigger="interval",seconds=1,max_instances=10)scheduler.start()logger.info("后台监控任务启动成功")后台监控任务后台监控任务启动时,FastAPI没有启动完成,所以这时后台监控任务的WebSocket是不能及时连接上,所以需要做判断,没有连上或者断开了就连接self.ws:WebSocket=websocket.WebSocket()...try:#测试是否连通self.ws.recv()#骚操作第一次测试连接,后面防止断开exceptWebSocketConnectionClosedException:#尝试连接try:self.ws.connect(f"ws://127.0.0.1:8080/ws/data?user=monitor")exceptWebSocketExceptionase:logger.error(f"连接失败:{e}")exceptConnectionRefusedErrorase:logger.error(f"连接失败:{e}")else:logger.success("监控任务连接成功")exceptConnectionAbortedErrorase:try:self.ws.connect(f"ws://127.0.0.1:8080/ws/data?user=monitor")exceptWebSocketExceptionase:logger.error(f"连接失败:{e}")exceptConnectionRefusedErrorase:logger.error(f"连接失败:{e}")else:logger.success("重新连接成功")群发仿真平台环境数据后台监控任务连接上WebSocket,就会把环境信息发给WebSocket接口就会群发到其他连接的客户端上。群发的实现就是把连接后的WebSocket对象保存起来,然后循环发送。classConnectionManager:def__init__(self):#存放需要广播的的链接self.active_connections:List[Dict[str,WebSocket]]=[]asyncdefconnect(self,user:str,ws:WebSocket):#链接awaitws.accept()self.active_connections.append({"user":user,"ws":ws})defdisconnect(self,user:str,ws:WebSocket):#关闭时移除ws对象self.active_connections.remove({"user":user,"ws":ws})asyncdefbroadcast_json(self,data:dict):#广播消息forconnectioninself.active_connections:awaitconnection['ws'].send_json(data)manager=ConnectionManager()@ws_app.websocket("/data")asyncdefwebsocket_endpoint(websocket:WebSocket,user:str):awaitmanager.connect(user,websocket)try:whileTrue:data=awaitwebsocket.receive_json()#等待请求,才响应#logger.debug(data)ifdata.get("user")=="monitor":data.get("data")["park_all"]=ParkingService.get_all()#全部车位data.get("data")["park_remain_num"]=ParkingService.get_remain_num()#剩余车位awaitmanager.broadcast_json(data["data"])#是监控服务发来的直接广播消息exceptConnectionClosedError:logger.error(f"{user}ConnectionClosedError")manager.disconnect(user,websocket)exceptWebSocketDisconnect:logger.error(f"{user}WebSocketDisconnect")manager.disconnect(user,websocket)客户端为了防止界面“假死”,耗时操作使用线程完成,这里的WebSocket连接更新环境数据,摄像头的使用,识别车牌均使用了线程PyQt使用摄像摄像头使用的cv2库,界面显示的画面,其实一帧一帧的显示的显示到Label组件上的,所以子线程也是把画面一帧一帧的发送出来。classCameraThread(QThread):frame_signal=pyqtSignal(numpy.ndarray)#获取的换面其实一种矩阵is_open_signal=pyqtSignal(bool)close_video_signal=pyqtSignal(bool)def__init__(self,parent=None):super().__init__(parent)self.is_open_video=True#defrun(self)->None:capture=cv2.VideoCapture(0)whileself.is_open_video:#读取摄像头显示视频ifcapture.isOpened():ret,frame=capture.read()#读取画面frame=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)self.frame_signal.emit(frame)#把图片发射出去else:self.is_open_signal.emit(capture.isOpened())#打开摄像头失败ifnotself.is_open_video:#摄像头关闭清理资源发送信号capture.release()self.close_video_signal.emit(True)#打开摄像头失败WebSocket连接classWSThread(QThread):info_signal=pyqtSignal(dict)ws:WebSocket=Nonedef__init__(self,parent=None):super().__init__(parent)defrun(self)->None:print("WSThreadrunning")whileTrue:date=json.loads(self.ws.recv())print(date)#发送信号self.info_signal.emit(date)time.sleep(0.2)全部代码:https://pan.bigdataboy.cn/s/dm9tk

编程杂谈

新博客:https://blog.bigdataboy.cn/article/450.htmlMyBatis说明mybatis-spring官网:https://mybatis.org/spring/zh/index.html介绍:MyBatis-Spring会帮助你将MyBatis代码无缝地整合到Spring中。它将允许MyBatis参与到Spring的事务管理之中,创建映射器mapper和SqlSession并注入到bean中,以及将Mybatis的异常转换为Spring的DataAccessException。添加Jar依赖pom.xml文件添加,注意版本问题<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><!--springjdbc操作--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.2</version></dependency><!--阿里巴巴数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.24</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><!--mybatisspring结合--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.7</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.8</version></dependency></dependencies>添加配置类和配置文件数据库Sql文件SETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructureforaccount------------------------------DROPTABLEIFEXISTS`account`;CREATETABLE`account`(`id`int(11)NOTNULLAUTO_INCREMENT,`users`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_binNULLDEFAULTNULL,`money`double(255,0)NULLDEFAULTNULL,PRIMARYKEY(`id`)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=3CHARACTERSET=utf8mb4COLLATE=utf8mb4_binROW_FORMAT=Compact;--------------------------------Recordsofaccount------------------------------INSERTINTO`account`VALUES(1,'a',100);INSERTINTO`account`VALUES(2,'b',200);SETFOREIGN_KEY_CHECKS=1;jdbc.properties在resources文件夹下jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/spring_dbjdbc.username=rootjdbc.password=rootJdbcConfig配置类Spring第三方Bean管理@ConfigurationpublicclassJdbcConfig{@Value("${jdbc.driver}")publicStringdriverClassName;@Value("${jdbc.url}")publicStringurl;@Value("${jdbc.username}")publicStringusername;@Value("${jdbc.password}")publicStringpassword;@BeanpublicDataSourcedataSource(){DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName(driverClassName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);returndataSource;}}MyBatisConfig配置类Spring第三方Bean管理@ConfigurationpublicclassMyBatisConfig{@Bean//连接工厂BeanpublicSqlSessionFactoryBeansqlSessionFactory(DataSourcedataSource)throwsException{SqlSessionFactoryBeanfactoryBean=newSqlSessionFactoryBean();factoryBean.setDataSource(dataSource);returnfactoryBean;}@Bean//MapperBeanpublicMapperScannerConfigurermapperScannerConfigurer(){MapperScannerConfigurermapperScannerConfigurer=newMapperScannerConfigurer();mapperScannerConfigurer.setBasePackage("cn.bigdataboy.dao");//设置Mapper映射的包,扫描Mapper注解,已经转化成Mapper,相对于以前的Mapper配置returnmapperScannerConfigurer;}}Spring配置类@Configuration@ComponentScan({"cn.bigdataboy"})@PropertySource({"classpath:jdbc.properties"})@Import({MyBatisConfig.class,JdbcConfig.class})//导入配置publicclassSpringConfig{}添加实体类实体类,就是查询结果的映射类,在domain文件夹下publicclassAccount{privateintid;privateStringusers;privateDoublemoney;@OverridepublicStringtoString(){return"Account{"+"id="+id+",users='"+users+'\''+",money="+money+'}';}}添加dao层dao层是数据处理,结合Mybatis后,就是原来的Mapper配置@MapperpublicinterfaceAccountDao{@Select("SELECT*FROMaccountWHEREid=#{userId}")AccountgetUserById(@Param("userId")intuserId);@Select("SELECT*FROMaccount")List<Account>getAllUser();}添加service层业务处理层,实现两个功能,按id查询和查询全部@ServicepublicclassAccountServiceImplimplementsAccountService{@AutowiredprivateAccountDaoaccountDao;@OverridepublicAccountgetUserById(intid){returnaccountDao.getUserById(id);}@OverridepublicList<Account>getAllUser(){returnaccountDao.getAllUser();}}测试publicclassApp{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(SpringConfig.class);AccountServiceaccountService=context.getBean(AccountService.class);Accountaccount=accountService.getUserById(1);System.out.println(account);System.out.println(accountService.getAllUser());}}案例代码:https://pan.bigdataboy.cn/s/mXnux

编程杂谈

新博客:https://blog.bigdataboy.cn/article/449.html概念AOP(面向切面编程)是一种编程思想,Python的装饰器有这种思想,逆向中的Hook技术也有这种思想在不改变原来方法的情况下,增加功能,只是在不同框架,不同语言下,实现方式和写法不一样实现思路:获取原来函数-执行顺序-参数&返回值增强方法,那肯定会让原来的方法肯执行,所以得需要获取原来方法既然还要增强方法,那得考虑增强的逻辑什么时候执行吧,是在原来的函数执行前,还是执行后吧最后是参数和返回值的问题,既然是增强,那我的参数和返回值要符合原来的函数的规则Spring的实现方式在pom.xml添加依赖<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><!--用来解析切入点表达式--><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency></dependencies>编写增强方法类,这个类还是需要按照Spring的规则变成交给容器管理的Bean@Component//记得BeanpublicclassAdvice{}获取原来的方法,Spring使用了一种用表达式匹配的方式,后面详细介绍@Component//记得BeanpublicclassAdvice{@Pointcut("execution(voidcn.bigdataboy.dao.NameDao.getById())")//切入点表达式privatevoidpt(){}//可以理解为代表原方法}编写增强逻辑的方法,并设定执行的时机,有五种,后面详细介绍@Component//记得BeanpublicclassAdvice{@Pointcut("execution(voidcn.bigdataboy.dao.NameDao.getById())")privatevoidpt(){}//可以理解为代表原方法@Before("pt()")//增强方法执行时机,有五种publicvoidmethod(){//增强的方法在这里System.out.println("当前执行时间:"+System.currentTimeMillis());}}添加注解,告诉Spring这是AOP的类@Component//记得Bean@Aspect//添加注解publicclassAdvice{@Pointcut("execution(voidcn.bigdataboy.dao.NameDao.getById())")privatevoidpt(){}//可以理解为代表原方法@Before("pt()")//增强方法执行时机,有五种,参数是原方法publicvoidmethod(){//增强的方法在这里System.out.println("当前执行时间:"+System.currentTimeMillis());}}最后在配置类添加注解,开启AOP功能@Configuration@ComponentScan({"cn.bigdataboy.dao","cn.bigdataboy.aop"})//注意Bean不要漏了@EnableAspectJAutoProxy//开启aop代理publicclassSpringConfig{}五种执行时机@Before("")在原函数执行前执行@Before("pt()")//之前执行publicvoidmethodBefore(){//增强的方法在这里System.out.println("@Beforerunning...");}@After("")在原函数执行后执行,如果原函数报错也会执行@After("pt()")//之后执行,如果原方法报错,也会执行publicvoidmethodAfter(){//增强的方法在这里System.out.println("@Afterrunning...");}@Around("")重点常用环绕执行,所以它有点特殊,有一个参数,包含原方法和它的各种信息,相对于执行锚点,能控制在增强函数的什么位置执行@Around("pt()")//环绕执行publicvoidmethodAround(ProceedingJoinPointpjp)throwsThrowable{//增强的方法在这里System.out.println("当前执行时间@Around:"+System.currentTimeMillis());pjp.proceed();//相对于传入锚点,执行位置System.out.println("当前执行时间@Around:"+System.currentTimeMillis());}@AfterReturning("")原方法成功执行时触发,原方法报错,则不会执行@AfterReturning("pt()")//在原方法正常执行后才会触发,也就说入原方法报错,就不会触发了publicvoidmethodAfterReturning(){//增强的方法在这里System.out.println("@AfterReturningrunning...");}@AfterThrowing("")在原方法报错触发@AfterThrowing("pt()")//在原方法报错触发publicvoidmethodAfterThrowing(){//增强的方法在这里System.out.println("@AfterThrowingrunning...");}切入点表达式切入点表达式,是Spring用来表达增强方法对哪些方法生效的式子@Pointcut("execution(voidcn.bigdataboy.dao.NameDao.getById())")privatevoidpt(){}//可以理解为代表原方法格式:动作关键字(访问修饰符返回值类型包名.类/接口名.方法名(参数)异常名)(有些是可以省略的)动作关键字:几乎都是execution()访问修饰符:public、private...可以省略返回值:是什么写什么异常名:方法中定义的抛出异常,可以省略切入点表达式通配快速描述*:单个独立的任意符号,可以独立出现,可以作为前缀或者后缀的匹配符出现execution(*cn.bigdataboy.dao.*Dao.getBy*())..:多个连续的任意符号,可以独立出现,常用于简化包名和参数出现execution(*cn..NameDao.getById(..))+:专用于匹配子类型execution(*cn.bigdataboy.dao.NameDao+.*())参数&返回值处理既然是增强方法,那参数和返回值就要处理,那同时,如果方法异常,那异常信息也要处理。都是使用形参获取的。参数处理@Before、@After、@AfterReturning、@AfterThrowing都是传入JoinPoint对象执行getArgs()获取原始方法参数数组JoinPoint对象包含大量信息对于@Around是传入ProceedingJoinPoint对象执行getArgs()获取原始方法参数数组返回值处理对于返回值,只有@Around、@AfterReturning有,其他的不涉及返回值@Around方式设计参数和返回值,所以为了规范,返回值可以是Object@Around("pt()")//环绕执行publicObjectmethodAround(ProceedingJoinPointpjp)throwsThrowable{//增强的方法在这里System.out.println("@Aroundrunningargs:"+Arrays.toString(pjp.getArgs()));Objectproceed=pjp.proceed(pjp.getArgs());//相对于锚点,执行位置System.out.println("@Aroundrunningres:"+proceed);returnproceed;}@AfterReturning获取返回值有点特殊,需要指定注解的returning参数@AfterReturning(value="pt()",returning="ret")//在原方法正常执行后才会触发,也就说入原方法报错,就不会触发了publicvoidmethodAfterReturning(Objectret){//增强的方法在这里System.out.println("@AfterReturningrunningres:"+ret);}案例代码:https://pan.bigdataboy.cn/s/Lglh5

编程杂谈

新博客:https://blog.bigdataboy.cn/article/448.html管理第三方Bean第三方Bean没有办法写给它注解,所以就只能添加,Spring的处理是在配置类中写一个函数,该函数使用@Beam注解第三方Bean配置类第三方Bean的配置类,建议单独文件编写,这样不冗余,全部写一个配置类,会变得一大坨importcom.alibaba.druid.pool.DruidDataSource;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.sql.DataSource;@ConfigurationpublicclassJdbcConfig{@Bean//表示返回一个Bean,也可以指定id->@Bean("dataSource")publicDataSourcedataSource(){//参数先空着DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName("");dataSource.setUrl("");dataSource.setUsername("");dataSource.setPassword("");returndataSource;}}在主配置类导入第三方配置类@Configuration@ComponentScan({"cn.bigdataboy.dao"})@Import({JdbcConfig.class})//导入publicclassSpringConfig{}获取第三方Bean第三方Bean的注入资源上面的例子,JDBC的链接还空着,所以我们需要为其注入参数注入简单类型注入方式与注入自己的Bean一样,使用成员属性添加@Value()注解importcn.bigdataboy.dao.NameDao;importcom.alibaba.druid.pool.DruidDataSource;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.sql.DataSource;@ConfigurationpublicclassJdbcConfig{@Value("${jdbc.driver}")publicStringdriverClassName;@Value("${jdbc.url}")publicStringurl;@Value("${jdbc.username}")publicStringusername;@Value("${jdbc.password}")publicStringpassword;@Bean//表示返回一个Bean,也可以指定id->@Bean("dataSource")publicDataSourcedataSource(){System.out.println(this.driverClassName);System.out.println(this.url);DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName(this.driverClassName);dataSource.setUrl(this.url);dataSource.setUsername(this.username);dataSource.setPassword(this.password);returndataSource;}}注入引入类型这里注入引入类型,有点不一样,直接添加成方法参数就好,因为Spring容器中已经存在相应的Bean,那么它也就能自己找到,因此也容易想到它是使用类型方式找的importcn.bigdataboy.dao.NameDao;importcom.alibaba.druid.pool.DruidDataSource;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.sql.DataSource;@ConfigurationpublicclassJdbcConfig{@Bean//表示返回一个Bean,也可以指定id->@Bean("dataSource")publicDataSourcedataSource(NameDaonameDao){//添加成参数System.out.println(nameDao);//打印一下,表示注入成功DruidDataSourcedataSource=newDruidDataSource();//相应配置returndataSource;}}案例代码:https://pan.bigdataboy.cn/s/NO5sp