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

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 代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/343544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【渝粤教育】国家开放大学2018年春季 0269-21T文学概论 参考试题

试卷代号&#xff1a;0269 2017-2018年度第二学期考试 文学概论试题 2018年7月 一、选择题&#xff08;请在下面的答题框内写上正确答案的序号&#xff09;&#xff08;每小题3分&#xff0c;共30分&#xff09; 1. 在研究长篇小说的文体语言的时候&#xff0c;俄国学者____…

HDMI视频光端机常见故障问题及解决方法

HDMI光端机就是光信号传输的终端设备&#xff0c;在广泛领域应用中&#xff0c;往往需要把HDMI信号源输送远处进行处理。最为突出的问题有&#xff1a;远处接收到的信号出现偏色、模糊&#xff0c;信号产生重影和拖尾及网纹干扰。那么&#xff0c;我们在使用HDMI视频光端机时常…

【渝粤教育】国家开放大学2018年春季 0299-22T中国古代文学(1) 参考试题

试卷编号&#xff1a;0299 座位号 2017—2018学年度第2学期期末考试 中国古代文学&#xff08;1&#xff09;试题(闭卷) 2018年5月 1.枚乘的《 》是汉赋发展史上一篇带有转折性的作品。 2. 汉代两位最杰出的史学家是 、 。他们的史学著作里的许多人物传记是传记散文的杰作。 …

将Spring Boot应用程序部署到Tomcat中

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 部署应用程序很困难。 通常&#xff0c;您需要控制台访问服务器&#xff0c;从服务器…

【渝粤教育】国家开放大学2018年春季 0463-22T英语语音 参考试题

科目编号&#xff1a;0463 座位号&#xff1a; 2017─2018学年度第二学期期末考试 英语语音笔试题 (开卷) 2018年7月 根据读音规则为下列单词注音 &#xff08;10分&#xff09; Saturday 2. notify demand 4. country 5. answer 6. police pronounce 8. magazine popular…

python读取excel写入mysql pandas_python pandas 读取文件 写入文件excel

读取数据import pandas as pdimport collectionsdef readLocationCodeForExcel():read_file r"test.xlsx"sheet_names {"库位码","地堆码"}sheet_data pd.ExcelFile(read_file) #读取sheet数据#sheet列表read_sheet_data sheet_data.sheet_n…

【渝粤教育】国家开放大学2018年春季 0553-21T色彩 参考试题

编号&#xff1a;0553 座位号&#xff1a; 2017&#xff5e;2018学年度第二学期期末考试 色彩试题 2018年7月 色彩写生画&#xff08;100分&#xff09; 题目&#xff1a;水粉色彩静物写生。 静物&#xff1a;由10件不同造型、色调、质感的静物组合并配置衬布 红色花瓶、鲜花一…

HD-SDI光端机是什么?其性能特点和技术参数有哪些?

HD-SDI光端机是将SDI信号与光信号互相转换的设备。SDI光端机的原理是发送端将SDI信号通过激光器调制后变为光信号,接收端将激光二极管收到的数据再编码为SDI信号。那么&#xff0c;HD-SDI光端机是什么呢&#xff1f;其性能特点和技术参数有哪些呢&#xff1f;接下来我们就跟随飞…

【渝粤教育】国家开放大学2018年春季 0609-21T中级财务会计(1) 参考试题

科目编号&#xff1a;0609 座位号 2017-2018学年度第二学期期末考试 中级财务会计&#xff08;1&#xff09; 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题3分&#xff0c;共计30分&#xff09; &#xff08;★请考生务必将答案填入到下面对应序…

flask查询mysql数据展示_flask下直接展示mysql数据库 字段

from flask importFlask,request,render_templatefrom flask_sqlalchemy importSQLAlchemyapp Flask(__name__)app.config[SQLALCHEMY_DATABASE_URI] sqlite:///test.db #这里用这个是不行的 注意修改为&#xff4d;&#xff59;&#xff53;&#xff51;&#xff4c; 才可以…

jersey spring_实施Jersey 2 Spring集成

jersey springJersey是Oracle提供的出色的Java JAX-RS规范参考实现。 去年&#xff0c;当我们开始为大容量网站构建RESTful后端Web服务时&#xff0c;我们选择使用JAX-RS API作为我们的REST框架和Spring框架来进行依赖项注入。 泽西岛是我们选择的JAX-RS实现。 项目启动时&…

【渝粤教育】国家开放大学2018年春季 0688-21T老年精神障碍护理 参考试题

编号&#xff1a;0688 座位号 2017&#xff5e;2018学年度第二学期期末考试 老年精神障碍护理&#xff08;开卷&#xff09;试题 2018年 5月 名词解释&#xff08;每题5分&#xff0c;共30分,&#xff09; 1、器质性精神障碍 2、谵妄 3、遗忘综合征 4、器质性幻觉症 5、老…

HD-SDI光端机有哪些优势?

3G-SDI光端机适合电视行业的高清可靠、高清性能的标准&#xff0c;杭州飞畅的3G-SDI光端机采用最新科技&#xff0c;采用H.264编码方式&#xff0c;采用高清SDI专用VIDIO SFP光模块&#xff0c;一般情况下采用SDI接口。其产品最早为广电行业客户开发使用&#xff0c;应用于电视…

【渝粤教育】国家开放大学2018年春季 0703-21T经济学基础 参考试题

科目编号&#xff1a;0703 座位号 □□ 2017-2018学年度第二学期期末考试 经济学基础 试题 2018年7月 一、单项选择题&#xff1a;&#xff08;每题4分&#xff0c;共40分&#xff09; 经济学可以定义为&#xff08; &#xff09; 政府对市场制度的干预 企业赚取利润的活动 研…

elementui可编辑单元格_ElementUI 表格可编辑单元格

页面export default{components: {EditableCell: () > import(/components/EditableCell/EditableCell.vue),},data() {return {tableData:[{date:"2020-12-01",name:"my name is elementui",address:"浙江省杭州市西湖区",}]}},methods: {on…

【渝粤教育】国家开放大学2018年春季 3896T人文英语1 参考试题

试卷代号&#xff1a;3896 &#xff08;中央广播电视大学&#xff09;2018年春季学期“开放专科”期末考试 人文英语1 试题 2018年7月 注 意 事 项 一、将你的学号、姓名及分校&#xff08;工作站&#xff09;名称填写在答题纸的规定栏内。考试结束后&#xff0c;把试卷和答题纸…

如何以及为什么使用Spoon分析,生成和转换Java代码

Spoon是分析&#xff0c;生成和转换Java代码的工具。 在本文中&#xff0c;我们将看到通过使用以编程方式处理代码的技术可以实现什么。 我认为这些技术不是很广为人知或使用&#xff0c;这很遗憾&#xff0c;因为它们可能非常有用。 谁知道&#xff0c;即使您不想使用Spoon甚…

PDH-SDH光端机指示灯具体含义介绍

最近有很多客户朋友来咨询有关PDH/SDH光端机各个指示灯含义的问题&#xff0c;其实在很多故障发生的时候我们都可以通过光端机各个指示灯的状态来分析故障问题&#xff0c;从而帮助解决问题。接下来我们就来为大家详细介绍一下PDH/SDH光端机指示灯的具体含义&#xff0c;感兴趣…

【渝粤教育】21秋期末考试工程项目管理10324k2

1、()是项目投产前由建设单位进行的一项重要工作&#xff0c;是在完成建设阶段任务以后转入到建筑产品的生产以及产品经营的必要条件 &#xff08;2.5 分&#xff09; A&#xff0e;可行性研究 B&#xff0e;资金筹备 C&#xff0e;招标 D&#xff0e;生产准备 2、基础设施工程…

【渝粤教育】21秋期末考试建设工程法规10221k1

1、当事人既约定违约金&#xff0c;又约定定金的&#xff0c;一方违约时&#xff0c;这两种违约责任&#xff08;  &#xff09;。&#xff08;1 分&#xff09; A&#xff0e;可合并使用 B&#xff0e;适用数值较小者 C&#xff0e;适用数值较大者 D&#xff0e;只能选择其一…