日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

项目学生:使用AOP简化代码

發布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 项目学生:使用AOP简化代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是Project Student的一部分。

許多人堅信方法應適合您的編輯器窗口(例如20行),而有些人則認為方法應小于此范圍。 這個想法是一種方法應該做一件事,而只能做一件事。 如果它做的還不止于此,則應將其分解為多種方法,而舊方法的“一件事”就是協調新方法。

這并不意味著在任意數量的行之后拆分一種方法。 有時方法自然會更大。 仍然是一個很好的問題。

那么,如何識別不只一件事的代碼? 一個好的試金石是代碼是否在多種方法中重復。 典型的例子是持久性類中的事務管理。 每個持久性類都需要它,并且代碼始終看起來相同。

另一個示例是Resource類中未處理的異常處理程序。 每個面向REST的方法都需要處理此問題,并且代碼始終看起來相同。

那是理論。 在實踐中,代碼可能很丑陋并且收益不大。 幸運的是,有一個解決方案:面向方面的編程(AOP)。 這使我們可以在方法調用之前或之后透明地編織代碼。 這通常使我們可以大大簡化我們的方法。

設計決策

AspectJ –我正在通過Spring注入來使用AspectJ。

局限性

使用CRUD方法,AspectJ切入點表達式相對簡單。 當添加了更復雜的功能時,情況可能并非如此。

資源方法中未處理的異常

我們首先關心的是資源方法中未處理的異常。 不管怎樣,Jersey都會返回SERVER INTERNAL ERROR(服務器內部錯誤)(500)消息,但是它可能包含堆棧跟蹤信息和我們不希望攻擊者知道的其他內容。 如果我們自己發送它,我們可以控制它包含的內容。 我們可以在所有方法中添加一個“ catch”塊,但可以將其復制到AOP方法中。 這將使我們所有的Resource方法更加苗條和易于閱讀。

此類還檢查“找不到對象”異常。 在單個Resource類中將很容易處理,但會使代碼混亂。 將異常處理程序放在此處可使我們的方法專注于快樂路徑并保證響應的一致性。

該類有兩個優化。 首先,它顯式檢查UnitTestException并在這種情況下跳過詳細的日志記錄。 我最大的煩惱之一是測試,當一切都按預期方式運行時,將堆棧跟蹤信息充斥日志。 這使得不可能針對明顯的問題瀏覽日志。 單個更改可以使問題更容易發現。

其次,它使用與目標類(例如CourseResource)關聯的記錄器,而不是與AOP類關聯的記錄器。 除了更清晰之外,這還使我們可以有選擇地更改單個Resource(而不是全部)的日志記錄級別。

另一個技巧是在處理程序中調用ExceptionService 。 該服務可以對異常做一些有用的事情,例如,它可以創建或更新Jira票證。 這還沒有實現,所以我只是發表評論以說明它的去向。

@Aspect @Component public class UnexpectedResourceExceptionHandler {@Around("target(com.invariantproperties.sandbox.student.webservice.server.rest.AbstractResource)")public Object checkForUnhandledException(ProceedingJoinPoint pjp) throws Throwable {Object results = null;Logger log = Logger.getLogger(pjp.getSignature().getClass());try {results = pjp.proceed(pjp.getArgs());} catch (ObjectNotFoundException e) {// this is safe to log since we know that we've passed filtering.String args = Arrays.toString(pjp.getArgs());results = Response.status(Status.NOT_FOUND).entity("object not found: " + args).build();if (log.isDebugEnabled()) {log.debug("object not found: " + args);}} catch (Exception e) {// find the method we called. We can't cache this since the method// may be overloadedMethod method = findMethod(pjp); if ((method != null) && Response.class.isAssignableFrom(method.getReturnType())) {// if the method returns a response we can return a 500 message.if (!(e instanceof UnitTestException)) {if (log.isInfoEnabled()) {log.info(String.format("%s(): unhandled exception: %s", pjp.getSignature().getName(),e.getMessage()), e);}} else if (log.isTraceEnabled()) {log.info("unit test exception: " + e.getMessage());}results = Response.status(Status.INTERNAL_SERVER_ERROR).build();} else {// DO NOT LOG THE EXCEPTION. That just clutters the log - let// the final handler log it.throw e;}}return results;}/*** Find method called via reflection.*/Method findMethod(ProceedingJoinPoint pjp) {Class[] argtypes = new Class[pjp.getArgs().length];for (int i = 0; i < argtypes.length; i++) {argtypes[i] = pjp.getArgs()[i].getClass();}Method method = null;try {// @SuppressWarnings("unchecked")method = pjp.getSignature().getDeclaringType().getMethod(pjp.getSignature().getName(), argtypes);} catch (Exception e) {Logger.getLogger(UnexpectedResourceExceptionHandler.class).info(String.format("could not find method for %s.%s", pjp.getSignature().getDeclaringType().getName(),pjp.getSignature().getName()));}return method;} }

REST發布值檢查

我們的Resource方法也有很多樣板代碼來檢查REST參數。 它們是否為非空,電子郵件地址的格式是否正確,等等。同樣,很容易將大部分代碼移入AOP方法并簡化Resource方法。

我們首先定義一個接口,該接口指示可以驗證REST傳輸對象。 第一個版本使我們可以簡單地接受或拒絕,改進的版本可以使我們有辦法告訴客戶具體問題是什么。

public interface Validatable {boolean validate(); }

現在,我們擴展了先前的REST傳輸對象,以添加一種驗證方法。

兩個筆記。 首先,名稱和電子郵件地址接受Unicode字母,而不僅僅是標準ASCII字母。 隨著我們的世界國際化,這一點很重要。

其次,我添加了一個toString()方法,但是由于它使用了未經處理的值,因此這是不安全的。 我將在稍后處理消毒。

@XmlRootElement public class NameAndEmailAddressRTO implements Validatable {// names must be alphabetic, an apostrophe, a dash or a space. (Anne-Marie,// O'Brien). This pattern should accept non-Latin characters.// digits and colon are added to aid testing. Unlikely but possible in real// names.private static final Pattern NAME_PATTERN = Pattern.compile("^[\\p{L}\\p{Digit}' :-]+$");// email address must be well-formed. This pattern should accept non-Latin// characters.private static final Pattern EMAIL_PATTERN = Pattern.compile("^[^@]+@([\\p{L}\\p{Digit}-]+\\.)?[\\p{L}]+");private String name;private String emailAddress;private String testUuid;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEmailAddress() {return emailAddress;}public void setEmailAddress(String emailAddress) {this.emailAddress = emailAddress;}public String getTestUuid() {return testUuid;}public void setTestUuid(String testUuid) {this.testUuid = testUuid;}/*** Validate values.*/@Overridepublic boolean validate() {if ((name == null) || !NAME_PATTERN.matcher(name).matches()) {return false;}if ((emailAddress == null) || !EMAIL_PATTERN.matcher(emailAddress).matches()) {return false;}if ((testUuid != null) && !StudentUtil.isPossibleUuid(testUuid)) {return false;}return true;}@Overridepublic String toString() {// FIXME: this is unsafe!return String.format("NameAndEmailAddress('%s', '%s', %s)", name, emailAddress, testUuid);} }

我們對其他REST傳輸對象進行了類似的更改。

現在,我們可以編寫AOP方法來檢查CRUD操作的參數。 和以前一樣,使用與資源關聯的記錄器而不是AOP類來寫入日志。

這些方法還記錄Resource方法的條目。 同樣,它是樣板,在此進行簡化了Resource方法。 記錄該方法的退出和運行時間也很簡單,但是在這種情況下,我們應該使用一個股票記錄器AOP類。

@Aspect @Component public class CheckPostValues {/*** Check post values on create method.* * @param pjp* @return* @throws Throwable*/@Around("target(com.invariantproperties.sandbox.student.webservice.server.rest.AbstractResource) && args(validatable,..)")public Object checkParametersCreate(ProceedingJoinPoint pjp, Validatable rto) throws Throwable {final Logger log = Logger.getLogger(pjp.getSignature().getDeclaringType());final String name = pjp.getSignature().getName();Object results = null;if (rto.validate()) {// this should be safe since parameters have been validated.if (log.isDebugEnabled()) {log.debug(String.format("%s(%s): entry", name, Arrays.toString(pjp.getArgs())));}results = pjp.proceed(pjp.getArgs());} else {// FIXME: this is unsafeif (log.isInfoEnabled()) {log.info(String.format("%s(%s): bad arguments", name, Arrays.toString(pjp.getArgs())));}// TODO: tell caller what the problems wereresults = Response.status(Status.BAD_REQUEST).build();}return results;}/*** Check post values on update method.* * @param pjp* @return* @throws Throwable*/@Around("target(com.invariantproperties.sandbox.student.webservice.server.rest.AbstractResource) && args(uuid,validatable,..)")public Object checkParametersUpdate(ProceedingJoinPoint pjp, String uuid, Validatable rto) throws Throwable {final Logger log = Logger.getLogger(pjp.getSignature().getDeclaringType());final String name = pjp.getSignature().getName();Object results = null;if (!StudentUtil.isPossibleUuid(uuid)) {// this is a possible attack.if (log.isInfoEnabled()) {log.info(String.format("%s(): uuid", name));}results = Response.status(Status.BAD_REQUEST).build();} else if (rto.validate()) {// this should be safe since parameters have been validated.if (log.isDebugEnabled()) {log.debug(String.format("%s(%s): entry", name, Arrays.toString(pjp.getArgs())));}results = pjp.proceed(pjp.getArgs());} else {// FIXME: this is unsafeif (log.isInfoEnabled()) {log.info(String.format("%s(%s): bad arguments", name, Arrays.toString(pjp.getArgs())));}// TODO: tell caller what the problems wereresults = Response.status(Status.BAD_REQUEST).build();}return results;}/*** Check post values on delete method. This is actually a no-op but it* allows us to log method entry.* * @param pjp* @return* @throws Throwable*/@Around("target(com.invariantproperties.sandbox.student.webservice.server.rest.AbstractResource) && args(uuid,version) && execution(* *.delete*(..))")public Object checkParametersDelete(ProceedingJoinPoint pjp, String uuid, Integer version) throws Throwable {final Logger log = Logger.getLogger(pjp.getSignature().getDeclaringType());final String name = pjp.getSignature().getName();Object results = null;if (!StudentUtil.isPossibleUuid(uuid)) {// this is a possible attack.if (log.isInfoEnabled()) {log.info(String.format("%s(): uuid", name));}results = Response.status(Status.BAD_REQUEST).build();} else {// this should be safe since parameters have been validated.if (log.isDebugEnabled()) {log.debug(String.format("%s(%s): entry", name, Arrays.toString(pjp.getArgs())));}results = pjp.proceed(pjp.getArgs());}return results;}/*** Check post values on find methods. This is actually a no-op but it allows* us to log method entry.* * @param pjp* @return* @throws Throwable*/@Around("target(com.invariantproperties.sandbox.student.webservice.server.rest.AbstractResource) && execution(* *.find*(..))")public Object checkParametersFind(ProceedingJoinPoint pjp) throws Throwable {final Logger log = Logger.getLogger(pjp.getSignature().getDeclaringType());if (log.isDebugEnabled()) {log.debug(String.format("%s(%s): entry", pjp.getSignature().getName(), Arrays.toString(pjp.getArgs())));}final Object results = pjp.proceed(pjp.getArgs());return results;} }

更新了Spring配置

我們必須告訴Spring搜索AOP類。 這是對我們的配置文件的單行更改。

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><aop:aspectj-autoproxy/> </beans>

更新資源

現在,我們可以簡化資源類。 僅有幾種方法可以簡化為幸福道路。

@Service @Path("/course") public class CourseResource extends AbstractResource {private static final Logger LOG = Logger.getLogger(CourseResource.class);private static final Course[] EMPTY_COURSE_ARRAY = new Course[0];@Resourceprivate CourseFinderService finder;@Resourceprivate CourseManagerService manager;@Resourceprivate TestRunService testRunService;/*** Default constructor.*/public CourseResource() {}/*** Set values used in unit tests. (Required due to AOP)* * @param finder* @param manager* @param testService*/void setServices(CourseFinderService finder, CourseManagerService manager, TestRunService testRunService) {this.finder = finder;this.manager = manager;this.testRunService = testRunService;}/*** Get all Courses.* * @return*/@GET@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response findAllCourses() {final List courses = finder.findAllCourses();final List results = new ArrayList(courses.size());for (Course course : courses) {results.add(scrubCourse(course));}final Response response = Response.ok(results.toArray(EMPTY_COURSE_ARRAY)).build();return response;}/*** Create a Course.* * FIXME: what about uniqueness violations?* * @param req* @return*/@POST@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response createCourse(CourseInfo req) {final String code = req.getCode();final String name = req.getName();Response response = null;Course course = null;if (req.getTestUuid() != null) {TestRun testRun = testRunService.findTestRunByUuid(req.getTestUuid());if (testRun != null) {course = manager.createCourseForTesting(code, name, req.getSummary(), req.getDescription(),req.getCreditHours(), testRun);} else {response = Response.status(Status.BAD_REQUEST).entity("unknown test UUID").build();}} else {course = manager.createCourse(code, name, req.getSummary(), req.getDescription(), req.getCreditHours());}if (course == null) {response = Response.status(Status.INTERNAL_SERVER_ERROR).build();} else {response = Response.created(URI.create(course.getUuid())).entity(scrubCourse(course)).build();}return response;}/*** Get a specific Course.* * @param uuid* @return*/@Path("/{courseId}")@GET@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response getCourse(@PathParam("courseId") String id) {// 'object not found' handled by AOPCourse course = finder.findCourseByUuid(id);final Response response = Response.ok(scrubCourse(course)).build();return response;}/*** Update a Course.* * FIXME: what about uniqueness violations?* * @param id* @param req* @return*/@Path("/{courseId}")@POST@Consumes({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })@Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })public Response updateCourse(@PathParam("courseId") String id, CourseInfo req) {final String name = req.getName();// 'object not found' handled by AOPfinal Course course = finder.findCourseByUuid(id);final Course updatedCourse = manager.updateCourse(course, name, req.getSummary(), req.getDescription(),req.getCreditHours());final Response response = Response.ok(scrubCourse(updatedCourse)).build();return response;}/*** Delete a Course.* * @param id* @return*/@Path("/{courseId}")@DELETEpublic Response deleteCourse(@PathParam("courseId") String id, @PathParam("version") Integer version) {// we don't use AOP handler since it's okay for there to be no matchtry {manager.deleteCourse(id, version);} catch (ObjectNotFoundException exception) {LOG.debug("course not found: " + id);}final Response response = Response.noContent().build();return response;} }

單元測試

單元測試需要對每個測試進行更改,因為我們不能簡單地實例化被測試的對象–我們必須使用Spring,以便正確編織AOP類。 幸運的是,這實際上是唯一的更改–我們檢索資源并通過package-private方法而不是package-private構造函數設置服務。

我們還需要為服務bean創建Spring值。 配置器類負責此工作。

@Configuration @ComponentScan(basePackages = { "com.invariantproperties.sandbox.student.webservice.server.rest" }) @ImportResource({ "classpath:applicationContext-rest.xml" }) // @PropertySource("classpath:application.properties") public class TestRestApplicationContext1 {@Beanpublic CourseFinderService courseFinderService() {return null;}@Beanpublic CourseManagerService courseManagerService() {return null;}....

整合測試

集成測試不需要任何更改。

源代碼

  • 源代碼位于https://github.com/beargiles/project-student [github]和http://beargiles.github.io/project-student/ [github頁面]。

參考: 項目學生:來自Invariant Properties博客的JCG合作伙伴 Bear Giles 使用AOP簡化代碼 。

翻譯自: https://www.javacodegeeks.com/2014/01/project-student-simplifying-code-with-aop.html

總結

以上是生活随笔為你收集整理的项目学生:使用AOP简化代码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩五月天| 日本高清视频www夜色资源 | julia一区二区三区中文字幕 | 激情拍拍| 蜜桃视频污在线观看 | 啪视频免费 | 青青草在线播放 | 亚洲精品免费在线视频 | 欧美激情亚洲 | 成人免费在线观看 | 亚洲AV成人无码久久精品巨臀 | 91中文字幕在线 | 亚洲欧美色图片 | 成人一级片在线观看 | 午夜视频在线观看免费视频 | 欧美日韩精品区别 | 久久精品一区二区免费播放 | 视频在线观看电影完整版高清免费 | xxxx日本高清| 高清视频一区 | 成人夜视频 | 成人av手机在线观看 | 欧美成人久久 | 国产精品福利小视频 | 在线观看免费成人 | 自拍1页 | 日本黄色中文字幕 | 波多野结衣av在线免费观看 | 免费色视频 | 久久精品在这里 | 午夜偷拍福利 | 日日操网 | 国产夜夜操 | 2019中文字幕在线观看 | 91成人在线免费视频 | 国产成人精品无码免费看在线 | 久久婷婷网 | 欧美一二 | 国产夫妻自拍小视频 | 成人在线视频免费观看 | 国产在线18 | 91麻豆国产视频 | 女性向av免费网站 | 超碰神马| 国产女主播一区二区三区 | 国产精品99久久久 | 性欧美巨大乳 | 乱子伦一区 | 69pao| 手机av网| 中文字幕乱妇无码av在线 | 国产午夜亚洲精品午夜鲁丝片 | 欧美日韩视频免费 | 无码精品视频一区二区三区 | 国产午夜视频在线 | 毛片随便看 | 影音先锋丝袜制服 | 日韩黄色片在线观看 | 欧美日韩成人一区二区三区 | 男男做的视频 | 欧美一级片观看 | 少妇学院在线观看 | 欧美系列第一页 | 91本色| 亚洲伦理一区二区三区 | 香港三级日本三级 | 亚洲成av人片在线观看 | 亚洲一区二区三区色 | 女人脱下裤子让男人捅 | 性xxx法国hd极品 | 国产jzjzjz丝袜老师水多 | 玩偶游戏在线观看免费 | 伊人网站| 免费观看黄色网页 | 手机免费在线观看av | 一区二区三区视频免费视 | 国产精品福利一区 | 成人久久18免费网站图片 | 国产制服丝袜在线 | 日本资源在线 | 色老头影视 | zzjj国产精品一区二区 | 午夜在线观看视频网站 | 免费瑟瑟网站 | 一级黄色免费网站 | 青青草精品视频 | 欧美黄色大片免费观看 | 操综合| 日韩成人高清在线 | 日韩人妻无码一区二区三区99 | 欧美性高潮 | 国产精品欧美综合 | 国产一区二区在 | hs视频在线观看 | 亚洲AV无码国产日韩久久 | 欧美极品一区二区 | 一区二区乱子伦在线播放 | 日b视频在线观看 | 波多野结衣免费在线视频 |