




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
实验(训)项目列表序号项目名称课时备注1项目一Spring基础82项目二SpringBoot核心配置63项目三SpringBoot的Web开发64项目四SpringBoot的数据访问85项目五SpringBoot的高级配置10
实验训项目(五)项目名称SpringBoot的高级配置时间8班2021.10.2810班2021.10.27地点:一定要写完整名称8班知行楼604操作系统实验室10班知行楼601软件工程实验室(一)小组成员无实验目的与要求:主要仪器设备:装有Win1064位操作系统、JDK1.8、Mavaen3.8、Springtoolsuite4或IntelliJIDEA2019或Eclpise的计算机。实验内容实验方法与步骤:至少手写两个实验内容,每个内容需写完整步骤及关键代码,不够可贴页具体内容请参照本表格后的实验指导实验结果与分析不能写与预期一致,每个实验内容应该有输入,对应的输出或展示效果实验体会写真实体会实验(训)项目五:SpringBoot的高级配置一、实验(训)目的1、掌握SpringSecurity安全控制原理2、掌握点对点通信3、掌握发布/订阅式异步消息通信4、掌握SpringBoot中的单元测试二、实验(训)仪器及设备装有Win1064位操作系统、JDK1.8、Tomcat9.0、Mavaen3.8、Springtoolsuite4或IntelliJIDEA2019或Eclpise的计算机。三、实验(训)学时10学时四、实验(训)内容1、基于SpringDataJPA的SpringBootSecurity操作实例。2、使用JMS实现ActiveMQ进行点对点式通信。3、使用RabbitMAQ实现发布/订阅式异步消息通信。4、使用@WebMvcTest和@SpringBootTest测试某控制器方法。五、实验(训)步骤及要求至少手写两个实验内容,每个内容需写完整步骤及关键代码,不够可贴页(一)基于SpringDataJPA的SpringBootSecurity操作实例在SpringBoot应用中,使用基于SpringDataJPA的SpringSecurity安全框架。1.创建SpringBootWeb应用ch7_1创建基于SpringDataJPA,Thymeleaf及SpringSecurity的Web应用ch7_1,如图7.1所示。2.修改文件,添加MySQL依赖在文件中添加以下依赖: <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency>3.设置Web应用ch7_1的上下文路径及数据源配置信息在ch7_1应用的perties文件中配置以下内容:et.context-path=/ch7_1#####数据源信息配置####数据库地址=jdbc:mysql://localhost:3306/springbootjpa?characterEncoding=utf8#数据库用户名ame=root#数据库密码ord=root#数据库驱动r-class-name=.r#####JPA持久化配置#####指定数据库类型.database=MYSQL#指定是否在日志中显示SQL语句.show-sql=true#指定自动创建、更新数据库表等配置,update表示如果数据库中存在持久化类对应的表就不创建,不存在就创建对应的表.-auto=update#让控制器输出的JSON字符串格式更美观on.t-output=true=false.ity=trace4.整理脚本样式静态文件JS脚本、CSS样式、图片等静态文件默认放置在src/main/resources/static目录下。5.创建用户和权限持久化实体类创建名为.ch7_1.entity的包,并在该包中创建持久化实体类MyUser和Authority。MyUser类用来保存用户数据,用户名唯一。Authority类用来保存权限信息。用户和权限是多对多的关系。MyUser的代码如下:@Entity@Table(name="user")@JsonIgnoreProperties(value={"hibernateLazyInitializer"})publicclassMyUserimplementsSerializable{ privatestaticfinallongserialVersionUID=1L; @Id@GeneratedValue(strategy=ITY)privateintid;privateStringusername;privateStringpassword;//这里不能是懒加载lazy,否则在MyUserSecurityService的loadUserByUsername方法中获得不到权限@ManyToMany(cascade={SH},fetch=)@JoinTable(name="user_authority",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="authority_id"))privateList<Authority>authorityList;//repassword不映射到数据表@TransientprivateStringrepassword; //省略get和set}需要注意的是,在实际开发中MyUser还可以实现org.springframework.security.etails.UserDetails接口,实现该接口后即可成为SpringSecurity所使用的用户。本例为了区分SpringDataJPA的pojo和SpringSecurity的用户对象,并没有实现UserDetails接口,而是在实现UserDetailsService接口的类中进行绑定。Authority的代码如下:@Entity@Table(name="authority")@JsonIgnoreProperties(value={"hibernateLazyInitializer"})publicclassAuthorityimplementsSerializable{ privatestaticfinallongserialVersionUID=1L;@Id@GeneratedValue(strategy=ITY)privateintid;@Column(nullable=false)privateStringname;@ManyToMany(mappedBy="authorityList")@JsonIgnoreprivateList<MyUser>userList;//省略get和set}6.创建数据访问层接口创建名为.ch7_1.repository的包,并在该包中创建名为MyUserRepository的接口,该接口继承了JpaRepository。具体代码如下:publicinterfaceMyUserRepositoryextendsJpaRepository<MyUser,Integer>{ //根据用户名查询用户,方法名命名符合SpringDataJPA规范 MyUserfindByUsername(Stringusername);}7.创建业务层创建名为com.ch.ch7_1.service的包,并在该包中创建UserService接口和UserServiceImpl实现类。UserService的代码如下:publicinterfaceUserService{ publicStringregister(MyUseruserDomain); publicStringloginSuccess(Modelmodel); publicStringmain(Modelmodel); publicStringdeniedAccess(Modelmodel); publicStringlogout(HttpServletRequestrequest,HttpServletResponseresponse);}UserServiceImpl的代码如下:@ServicepublicclassUserServiceImplimplementsUserService{ @Autowired privateMyUserRepositorymyUserRepository; /** *实现注册 */ @Override publicStringregister(MyUseruserDomain){ Stringusername=ername(); List<Authority>authorityList=newArrayList<Authority>(); //管理员权限 if("admin".equals(username)){ Authoritya1=newAuthority(); Authoritya2=newAuthority(); a1.setId(1); a1.setName("ROLE_ADMIN"); a2.setId(2); a2.setName("ROLE_DBA"); (a1); (a2); }else{//用户权限 Authoritya1=newAuthority(); a1.setId(3); a1.setName("ROLE_USER"); (a1); } thorityList(authorityList); //加密密码 Stringsecret=newBCryptPasswordEncoder().encode(ssword()); ssword(secret); MyUsermu=(userDomain); if(mu!=null)//注册成功 return"/login"; return"/register";//注册失败 } /** *用户登录成功 */ @Override publicStringloginSuccess(Modelmodel){ tribute("user",getUname()); tribute("role",getAuthorities()); return"/user/loginSuccess"; } /** *管理员登录成功 */ @Override publicStringmain(Modelmodel){ tribute("user",getUname()); tribute("role",getAuthorities()); return"/admin/main"; } /** *注销用户 */ @Override publicStringlogout(HttpServletRequestrequest,HttpServletResponseresponse){ //获得用户认证信息 Authenticationauthentication=ntext().getAuthentication(); if(authentication!=null){ //注销 newSecurityContextLogoutHandler().logout(request,response,authentication); } return"redirect:/login?logout"; } /** *没有权限拒绝访问 */ @Override publicStringdeniedAccess(Modelmodel){ tribute("user",getUname()); tribute("role",getAuthorities()); return"deniedAccess"; } /** *获得当前用户名称 */ privateStringgetUname(){ returnntext().getAuthentication().getName(); } /** *获得当前用户权限 */ privateStringgetAuthorities(){ Authenticationauthentication=ntext().getAuthentication(); List<String>roles=newArrayList<String>(); for(GrantedAuthorityga:thorities()){ (ga.getAuthority()); } returning(); }}8.创建控制器类创建名为com.ch.ch7_1.controller的包,并在该包中创建控制器类TestSecurityController。具体代码如下:@ControllerpublicclassTestSecurityController{ @Autowired privateUserServiceuserService; @RequestMapping("/") publicStringindex(){ return"/index"; } @RequestMapping("/toLogin") publicStringtoLogin(){ return"/login"; } @RequestMapping("/toRegister") publicStringtoRegister(@ModelAttribute("userDomain")MyUseruserDomain){ return"/register"; } @RequestMapping("/register") publicStringregister(@ModelAttribute("userDomain")MyUseruserDomain){ returnter(userDomain); } @RequestMapping("/login") publicStringlogin(){ //这里什么都不错,由SpringSecurity负责登录验证 return"/login"; } @RequestMapping("/user/loginSuccess") publicStringloginSuccess(Modelmodel){ returnSuccess(model); } @RequestMapping("/admin/main") publicStringmain(Modelmodel){ return(model); } @RequestMapping("/logout") publicStringlogout(HttpServletRequestrequest,HttpServletResponseresponse){ returnt(request,response); } @RequestMapping("/deniedAccess") publicStringdeniedAccess(Modelmodel){ returndAccess(model); }}9.创建应用的安全控制相关实现创建名为.ch7_1.security的包,并在该包中创建MyUserSecurityService,MyAuthenticationSuccessHandler和MySecurityConfigurerAdapter类。MyUserSecurityService实现了UserDetailsService接口,并通过重写loadUserByUser-name(Stringusername)方法查询对应的用户,并将用户名、密码,权限等与认证相关的信息封装在UserDetails对象中。MyUserSecurityService的代码如下:/***获得对应的UserDetails,保存与认证相关的信息*/@ServicepublicclassMyUserSecurityServiceimplementsUserDetailsService{ @Autowired privateMyUserRepositorymyUserRepository; /** *通过重写loadUserByUsername方法查询对应的用户 *UserDetails是SpringSecurity的一个核心接口 *UserDetails定义了可以获取用户名、密码、权限等与认证相关信息的方法 */ @Override publicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{ //根据用户名(页面接收的用户名)查询当前用户 MyUsermyUser=yUsername(username); if(myUser==null){ thrownewUsernameNotFoundException("用户名不存在"); } //GrantedAuthority代表赋予当前用户的权限(认证权限) List<GrantedAuthority>authorities=newArrayList<GrantedAuthority>(); //获得当前用户权限集合 List<Authority>roles=thorityList(); //将当前用户的权限保存为用户的认证权限 for(Authorityauthority:roles){ GrantedAuthoritysg=newSimpleGrantedAuthority(me()); (sg); } //.是SpringSecurity的内部实现, //专门用于保存用户名、密码、权限等与认证相关的信息 Usersu=newUser(ername(),ssword(),authorities); returnsu; }}MyAuthenticationSuccessHandler继承了SimpleUrIAuthenticationSuccessHandler类,并重写了handle(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication)方法,根据当前认证用户的角色指定对应的URL。MyAuthenticationSuccessHandler的代码如下:@Component/***用户授权、认证成功处理类*/publicclassMyAuthenticationSuccessHandlerextendsSimpleUrlAuthenticationSuccessHandler{ //SpringSecurity的重定向策略 privateRedirectStrategyredirectStrategy=newDefaultRedirectStrategy(); /** *重写handle方法,通过RedirectStrategy重定向到指定的URL */ @Override protectedvoidhandle(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication) throwsIOException,ServletException{ //根据当前认证用户的角色返回适当的URL StringtagetURL=getTargetURL(authentication); //重定向到指定的URL edirect(request,response,tagetURL); } /** *从Authentication对象中提取当前登录用户的角色,并根据其角色返回适当的URL */ protectedStringgetTargetURL(Authenticationauthentication){ Stringurl=""; //获得当前登录用户的权限(角色)集合 Collection<?extendsGrantedAuthority>authorities=thorities(); List<String>roles=newArrayList<String>(); //将权限(角色)名称添加到List集合 for(GrantedAuthorityau:authorities){ (au.getAuthority()); } //判断不同角色的用户跳转到不同的URL //这里的URL是控制器的请求匹配路径 if(ins("ROLE_USER")){ url="/user/loginSuccess"; }elseif(ins("ROLE_ADMIN")){ url="/admin/main"; }else{ url="/deniedAccess"; } returnurl; }}MySecurityConfigurerAdapter类继承了WebSecurityConfigurerAdapter类,并通过重写configure(AuthenticationManagerBuilderauth)方法实现用户认证,重写configure(HttpSecurityhttp)方法实现用户授权操作。MySecurityConfigurerAdapter的代码如下:/***认证和授权处理类*/@ConfigurationpublicclassMySecurityConfigurerAdapterextendsWebSecurityConfigurerAdapter{ //依赖注入通用的用户服务类 @Autowired privateMyUserSecurityServicemyUserSecurityService; //依赖注入加密接口 @Autowired privatePasswordEncoderpasswordEncoder; //依赖注入用户认证接口 @Autowired privateAuthenticationProviderauthenticationProvider; //依赖注入认证处理成功类,验证用户成功后处理不同用户跳转到不同的页面 @Autowired privateMyAuthenticationSuccessHandlermyAuthenticationSuccessHandler; /** *BCryptPasswordEncoder是PasswordEncoder的接口实现 *实现加密功能 */ @Bean publicPasswordEncoderpasswordEncoder(){ returnnewBCryptPasswordEncoder(); } /** *DaoAuthenticationProvider是AuthenticationProvider的实现 */ @Bean publicAuthenticationProviderauthenticationProvider(){ DaoAuthenticationProviderprovide=newDaoAuthenticationProvider(); //不隐藏用户未找到异常 deUserNotFoundExceptions(false); //设置自定义认证方式,用户登录认证 erDetailsService(myUserSecurityService); //设置密码加密程序认证 sswordEncoder(passwordEncoder); returnprovide; } /** *用户认证 */ @Override protectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{ .println("configure(AuthenticationManagerBuilderauth)"); //设置认证方式 nticationProvider(authenticationProvider); } /** *请求授权 *用户授权操作 */ @Override protectedvoidconfigure(HttpSecurityhttp)throwsException{ .println("configure(HttpSecurityhttp)"); rizeRequests() //首页、登录、注册页面、登录注册功能、以及静态资源过滤掉,即可任意访问 .antMatchers("/toLogin","/toRegister","/","/login","/register","/css/**","/fonts/**","/js/**").permitAll() //这里默认追加ROLE_,/user/**是控制器的请求匹配路径 .antMatchers("/user/**").hasRole("USER") .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA") //其他所有请求登录后才能访问 .anyRequest().authenticated() .and() //将输入的用户名与密码和授权的进行比较 .formLogin() .loginPage("/login").successHandler(myAuthenticationSuccessHandler) .usernameParameter("username").passwordParameter("password") //登录失败 .failureUrl("/login?error") .and() //注销行为可任意访问 .logout().permitAll() .and() //指定异常处理页面 .exceptionHandling().accessDeniedPage("/deniedAccess"); }}10.创建用于测试的视图页面在src/main/resources/templates目录下创建应用首页、注册、登录以及拒绝访问页面;在src/main/resources/templates/admin目录下创建管理员用户认证成功后访问的页面;在src/main/resources/templates/user目录下创建普通用户认证成功后访问的页面。首页页面的代码如下:<!DOCTYPEhtml><htmlxmlns:th=""><head><metacharset="UTF-8"><title>首页</title><linkrel="stylesheet"th:href="@{css/.css}"/></head><body> <divclass="panelpanel-primary"> <divclass="panel-heading"> <h3class="panel-title">SpringSecurity测试首页</h3> </div> </div> <divclass="container"> <div> <ath:href="@{/toLogin}">去登录</a><br><br> <ath:href="@{/toRegister}">去注册</a> </div> </div></body></html>注册页面register.html的代码如下:<!DOCTYPEhtml><htmlxmlns:th=""><head><metacharset="UTF-8"><title>注册页面</title><linkrel="stylesheet"th:href="@{/css/.css}"/><scriptth:src="@{/js/.js}"></script><scripttype="text/javascript"th:inline="javascript"> functioncheckBpwd(){ if($("#username").val()==""){ alert("用户名必须输入!"); $("#username").focus(); returnfalse; }elseif($("#password").val()==""){ alert("密码必须输入!"); $("#password").focus(); returnfalse; }elseif($("#password").val()!=$("#repassword").val()){ alert("两次密码不一致!"); $("#password").focus(); returnfalse; }else{ m.submit(); } }</script><body> <divclass="container"> <divclass="bg-primary"style="width:100%;height:70px;padding-top:10px;"> <h2align="center">用户注册</h2> </div> <br> <br> <formth:action="@{/register}"name="myform"method="post"th:object="${userDomain}"class="form-horizontal"role="form"> <divclass="form-grouphas-success"> <labelclass="col-sm-2col-md-2control-label">用户名</label> <divclass="col-sm-4col-md-4"> <inputtype="text"class="form-control" placeholder="请输入您的用户名" th:field="*{username}"/> </div> </div> <divclass="form-grouphas-success"> <labelclass="col-sm-2col-md-2control-label">密码</label> <divclass="col-sm-4col-md-4"> <inputtype="password"class="form-control" placeholder="请输入您的密码"th:field="*{password}"/> </div> </div> <divclass="form-grouphas-success"> <labelclass="col-sm-2col-md-2control-label">确认密码</label> <divclass="col-sm-4col-md-4"> <inputtype="password"class="form-control" placeholder="请输入您的密码"th:field="*{repassword}"/> </div> </div> <divclass="form-group"> <divclass="col-sm-offset-2col-sm-10"> <buttontype="button"onclick="checkBpwd()"class="btnbtn-success">注册</button> <buttontype="reset"class="btnbtn-primary">重置</button> </div> </div> </form> </div></body></html>登录页面login.html的代码如下:<!DOCTYPEhtml><htmlxmlns:th=""><head><metacharset="UTF-8"><title>登录页面</title><linkrel="stylesheet"th:href="@{css/.css}"/><scripttype="text/javascript"th:src="@{js/.js}"></script><scripttype="text/javascript"> $(function(){ $("#loginBtn").click(function(){ varusername=$("#username"); varpassword=$("#password"); varmsg=""; if(()==""){ msg="用户名不能为空!"; (); }elseif(()==""){ msg="密码不能为空!"; (); } if(msg!=""){ alert(msg); returnfalse; } $("#myform").submit(); }); });</script><body> <divclass="container"> <divclass="bg-primary"style="width:100%;height:70px;padding-top:10px;"> <h2align="center">用户登录</h2> </div> <br> <br> <formth:action="@{/login}"id="myform"method="post"class="form-horizontal"role="form"> <!--用户名或密码错误--> <divth:if="${!=null}"> <divclass="alertalert-danger"> <p><fontcolor="red">用户名或密码错误!</font></p> </div> </div> <!--注销--> <divth:if="${t!=null}"> <divclass="alertalert-success"> <p><fontcolor="red">用户已注销成功!</font></p> </div> </div> <divclass="form-grouphas-success"> <labelclass="col-sm-2col-md-2control-label">用户名</label> <divclass="col-sm-4col-md-4"> <inputtype="text"class="form-control" placeholder="请输入您的用户名" name="username"id="username"/> </div> </div> <divclass="form-grouphas-success"> <labelclass="col-sm-2col-md-2control-label">密码</label> <divclass="col-sm-4col-md-4"> <inputtype="password"class="form-control" placeholder="请输入您的密码"name="password"id="password"/> </div> </div> <divclass="form-group"> <divclass="col-sm-offset-2col-sm-10"> <buttontype="button"id="loginBtn"class="btnbtn-success">登录</button> <buttontype="reset"class="btnbtn-primary">重置</button> </div> </div> </form> </div></body></html>拒绝访问页面的代码如下:<!DOCTYPEhtml><htmlxmlns:th=""><head><metacharset="UTF-8"><title>首页</title><linkrel="stylesheet"th:href="@{/css/.css}"/></head><body> <divclass="panelpanel-primary"> <divclass="panel-heading"> <h3class="panel-title">拒绝访问页面</h3> </div> </div> <divclass="container"> <div> <h3><spanth:text="${user}"></span>您没有权限访问该页面!您的权限是您的权限是<spanth:text="${role}"></span>。</h3><br><br> <ath:href="@{/logout}">安全退出</a> </div> </div></body></html>管理员用户成功登录后访问的页面的代码如下:<!DOCTYPEhtml><htmlxmlns:th=""><head><metacharset="UTF-8"><title>首页</title><linkrel="stylesheet"th:href="@{/css/.css}"/></head><body> <divclass="panelpanel-primary"> <divclass="panel-heading"> <h3class="panel-title">管理员页面</h3> </div> </div> <divclass="container"> <div> <h3>欢迎<spanth:text="${user}"></span>访问管理员页面!您的权限是<spanth:text="${role}"></span>。</h3><br><br> <ath:href="@{/user/loginSuccess}">去访问用户登录成功页面</a><br><br> <ath:href="@{/logout}">安全退出</a> </div> </div></body></html>普通用户成功登录后访问的页面的代码如下:<!DOCTYPEhtml><htmlxmlns:th="http://www."><head><metacharset="UTF-8"><title>首页</title><linkrel="stylesheet"th:href="@{/css/.css}"/></head><body><divclass="panelpanel-primary"><divclass="panel-heading"><h3class="panel-title">登录成功页面</h3></div></div><divclass="container"><div><h3>欢迎<spanth:text="${user}"></span>登录成功!您的权限是<spanth:text="${role}"></span>。</h3><br><br><ath:href="@{/admin/main}">去访问管理员页面</a><br><br><ath:href="@{/logout}">安全退出</a></div></div></body></html>11.测试应用运行Ch71Application的主方法启动项目。SpringBoot应用启动后,观察控制台,发现MySecurityConfigurerAdapter的两个configure方法都已经被执行,说明自定义的用户认证和用户授权工作已经生效。控制台的输出结果如图7.2所示。在浏览器地址栏中输入“http://localhost:8080/ch7_1”"/toLogin”"/toRegister"“/1”“/login”"/register”等其中任何一个请求都将正常访问,其他请求都将被重定向到“http://localhost:8080/ch7_1/login”登录页面,因为没有登录,用户没有访问权限,如图7.3所示。可以通过“http://localhost:8080/ch7_1/”访问首页面,如图7.4所示﹔然后,单击“去注册”超链接打开注册页面,如图7.5所示。用户成功注册后,打开登录页面进行用户登录。如果在图7.5中输入的用户名不是admin,那么就是注册了一个普通用户,其权限为ROLE_USER;如果在图7.5中输入的用户名是admin,那么就是注册了一个管理员用户,其权限为ROLE_ADMIN和ROLE_DBA。在图7.3登录页面中任意输入用户名和密码,单击“登录”按钮,提示用户名或密码错误,如图7.6所示。在图7.3登录页面中输入管理员用户名和密码,成功登录后打开管理员主页面,如图7.7所示。单击图7.7中的“去访问用户登录成功页面”,显示拒绝访问页面,如图7.8所示。在图7.3登录页面中输人普通用户名和密码,成功登录后打开用户登录成功页面,如图7.9所示。单击图7.9中的“去访问管理员页面”,显示拒绝访问页面,如图7.10所示。(二)使用JMS实现ActiveMQ进行点对点式通信启动ActiveMQ,双击“apache-activemq-5.15.10\bin\win64”下的“”或“”启动ActiveMQ,如图8.4所示。然后,通过“http://localhost:8161”运行ActiveMQ的管理界面,管理员账号和密码默认为admin/admin,如图8.5所示。1.创建基于ApacheActiveMQ5的SpringBoot应用ch8_1Sender(消息发送者)创建基于ApacheActiveMQ5的SpringBoot应用ch8_1Sender,该应用作为消息发送者,如图8.6所示。2.配置ActiveMQ的消息代理地址。在ch8_1Sender应用的配置文件perties中,配置ActiveMQ的消息代理地址。具体代码如下:r-url=tcp://localhost:616163.定义消息。在.ch8_lsender包下,创建消息定义类MyMessage,该类需要实现MessageCreator接口,并重写接口方法createMessage进行消息定义。具体代码如下:publicclassMyMessageimplementsMessageCreator{ @Override publicMessagecreateMessage(Sessionsession)throwsJMSException{ MapMessagemapm=eMapMessage(); ArrayList<String>arrayList=newArrayList<String>(); ("陈恒1"); ("陈恒2"); ject("mesg1",arrayList);//只能存Java的基本对象 ring("mesg2","测试消息2"); returnmapm; }}4.发送消息。在ch8_1Sender应用的主类Ch81SenderApplication中,实现SpringBoot的CommandLineRunner接口,并重写run方法,用于程序启动后执行的代码。在该run方法中,使用JmsTemplate的send方法向目的地mydestination发送MyMessage的消息,也相当于在消息代理上定义了一个叫作mydestination的目的地。具体代码如下:@SpringBootApplicationpublicclassCh81SenderApplicationimplementsCommandLineRunner{ @Autowired privateJmsTemplatejmsTemplate; publicstaticvoidmain(String[]args){ (,args); } /** *我们这里为了方便操作使用run方法发送消息, *当然完全可以使用控制器通过Web访问 */ @Override publicvoidrun(String...args)throwsException{ //newMyMessage()回调接口方法createMessage产生消息 ("mydestination",newMyMessage()); }}5.创建消息接收者(ch8_1Receive)。按照步骤1创建SpringBoot应用ch8_lReceive,该应用作为消息接收者,并按照步骤⒉配置ch8_1Receive的ActiveMQ的消息代理地址。r-url=tcp://localhost:616166.定义消息监听器接收消息。在应用ch8_lReceive的.ch8_lreceive包中,创建消息监听器类ReceiverMsg.在该类中使用@JmsListener注解不停地监听目的地mydestination是否有消息发送过来,如果有就获取消息。具体代码如下:@ComponentpublicclassReceiverMsg{ @JmsListener(destination="mydestination") publicvoidreceiverMessage(MapMessagemapm)throwsJMSException{ @SuppressWarnings("unchecked") ArrayList<String>arrayList=(ArrayList<String>)ject("mesg1"); .println(arrayList); .println(ring("mesg2")); }}7.运行测试。先启动消息接收者ch8_lReceive应用,然后单击图8.5中的Queues,可看到如图8.7所示的界面。从图8.7可以看出目的地mydestination有一个消费者,正在等待接收消息。此时,启动消息发送者ch8_ISender应用后,可在接收者ch8_1Receive应用的控制台上看到有消息打印,如图8.8所示。再去刷新图8.7,可看到如图8.9所示的界面。从图8.9可以看出目的地mydestination有一个消息入列(表示发送成功),有一个消息出列(表示接收成功)。(三)使用RabbitMAQ实现发布/订阅式异步消息通信在本例中,创建一个发布者应用、两个订阅者应用。该实例中的三个应用都是使用SpringBoot默认为我们配置的RabbitMQ,主机为localhost、端口号为5672,所以无须在配置文件中配置RabbitMQ的连接信息。另外,三个应用需要使用Weather实体类封装消息,并且使用JSON数据格式发布和订阅消息。1.创建发布者应用ch8_2Sender创建发布者应用ch8_2Sender,包括以下步骤。(1)创建基于RabbitMQ的SpringBoot应用ch8_2Sender,如图8.15所示。(2)在ch8_2Sender应用的pom.xml中添加spring-boot-starter-json依赖。具体代码如下: <dependency> <groupId></groupId> <artifactId>spring-boot-starter-json</artifactId> </dependency>(3)在ch8_2Sender应用中创建名为.ch8_2Sender.entity的包,并在该包中创建Weather实体类。具体代码如下:publicclassWeatherimplementsSerializable{ privatestaticfinallongserialVersionUID=-L; privateStringid; privateStringcity; privateStringweatherDetail; //省略get和set}(4)在ch8_2Sender应用的主类Ch82SenderApplication中,实现SpringBoot的CommandLineRunner接口,并重写run方法,用于程序启动后执行的代码。在该run方法中,使用RabbitTemplate的convertAndSend方法将特定的路由weather.message发送Weather消息对象到指定的交换机weather-exchange。在发布消息前,需要使用ObjectMapper将Weather对象转换成byte[门类型的JSON数据。具体代码如下:@SpringBootApplicationpublicclassCh82SenderApplicationimplementsCommandLineRunner{ @AutowiredprivateObjectMapperobjectMapper; @Autowired RabbitTemplaterabbitTemplate; publicstaticvoidmain(String[]args){ (,args); } /** *定义发布者 */ @Override publicvoidrun(String...args)throwsException{ //定义消息对象 Weatherweather=newWeather(); ("010"); ty("北京"); atherDetail("今天晴到多云,南风5-6级,温度19-26°C"); //指定Json转换器,Jackson2JsonMessageConverter默认将消息转换成byte[]类型的消息 ssageConverter(newJackson2JsonMessageConverter()); //objectMapper将weather对象转换为JSON字节数组 Messagemsg=ody(ValueAsBytes(weather)) .setDeliveryMode(_PERSISTENT).build();//消息唯一IDCorrelationDatacorrelationData=newCorrelationData(()); //使用已封装好的convertAndSend(Stringexchange,StringroutingKey,Objectmessage,CorrelationDatacorrelationData) //将特定的路由key发送消息到指定的交换机 rtAndSend( "weather-exchange",//分发消息的交换机名称 "ge",//用来匹配消息的路由Key msg,//消息体 correlationData); }}2.创建订阅者应用ch8_2Receiver-1创建订阅者应用ch8_2Receiver-1,具体步骤如下。(1)创建基于RabbitMQ的SpringBoot应用ch8_2Receiver-1。(2)在ch8_2Receiver-1应用的pom.xml中添加spring-boot-starter-json依赖。(3)将ch8_2Sender中的Weather实体类复制到.ch8_2Receiverl包中。(4)在com.ch.ch8_2Receiverl包中创建订阅者类Receiverl,在该类中使用@RabbitListener和@RabbitHandler注解监听发布者并接收消息,具体代码如下:/***定义订阅者Receiver1*/@ComponentpublicclassReceiver1{ @AutowiredprivateObjectMapperobjectMapper; @RabbitListener( bindings= @QueueBinding( //队列名weather-queue1保证和别的订阅者不一样 value=@Queue(value="weather-queue1",durable="true"), //weather-exchange与发布者的交换机名相同 exchange=@Exchange(value="weather-exchange",durable="true",type="topic"), //ge与发布者的消息的路由Key相同 key="ge" ) )@RabbitHandlerpublicvoidreceiveWeather(@Payloadbyte[]weatherMessage)throwsException{.println("订阅者Receiver1接收到消息");//将JSON字节数组转换为Weather对象Weatherw=alue(weatherMessage,);.println("Receiver1收到的消息内容:"+w);}}3.创建订阅者应用ch8_2Receiver-2与创建订阅者应用ch8_2Receiver-1的步骤一样,这里不再赘述。但需要注意的是两个订阅者的队列名不同。4.测试运行。首先,运行发布者应用ch8_2Sender的主类Ch82SenderApplication。其次,运行订阅者应用ch8_2Receiver-1的主类Ch82ReceiverlApplication,此时接收到的消息如图8.16所示。最后,运行订阅者应用ch8_2Receiver-2的主类Ch82Receiver2Application,此时接收到的消息如图8.17所示。从例8-2可以看出,一个发布者发布的消息,可以被多个订阅者订阅,这就是所谓的发布/订阅式异步消息通信。(四)使用@WebMvcTest和@SpringBootTest两种方式测试控制器方法。1.创建基于SpringDataJPA的Web应用ch9_2创建基于SpringDataJPA的Web应用ch9_2,如图9.3所示。2.修改文件,引入MySQL依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency>3.配置数据库连接等基本属性修改配置文件perties的内容,配置数据库连接等基本属性。具体代码如下:et.context-path=/ch9_2#####数据源信息配置####数据库地址=jdbc:mysql://localhost:3306/springbootjpa?characterEncoding=utf8#数据库用户名ame=root#数据库密码ord=root#数据库驱动r-class-name=.r#####JPA持久化配置#####指定数据库类型.database=MYSQL#指定是否在日志中显示SQL语句.show-sql=true#指定自动创建、更新数据库表等配置,update表示如果数据库中存在持久化类对应的表就不创建,不存在就创建对应的表.-auto=update#让控制器输出的JSON字符串格式更美观on.t-output=true4.创建持久化实体类创建名为.ch9_2.entity的包,并在该包中创建名为Student的持久化实体类。具体代码如下:@Entity@Table(name="student_table")/**解决Noserializerfoundforclass.uddyInterceptor异常*/@JsonIgnoreProperties(value={"hibernateLazyInitializer"})publicclassStudentimplementsSerializable{ privatestaticfinallongserialVersionUID=1L; @Id @GeneratedValue(strategy=ITY) privateintid;//主键 privateStringsno; privateStringsname; privateStringssex; publicStudent(){ super(); } publicStudent(intid,Stringsno,Stringsname,Stringssex){ super(); =id; =sno; =sname; =ssex; }}5.创建数据访问层创建名为.ch9_2.repository的包,并在该包中创建数据访问接口StudentRepository。具体代码如下:publicinterfaceStudentRepositoryextendsJpaRepository<Student,Integer>{ }6.创建控制器层创建名为com.ch.ch9_2.controller的包,并在该包中创建控制器类StudentController。具体代码如下:@RestController@RequestMapping("/student")publicclassStudentController{ @Autowired privateStudentRepositorystudentRepository; /** *保存学生信息 */ @PostMapping("/save") publicStringsave(@RequestBodyStudentstudent){ (student); return"success"; } /** *根据id查询学生信息 */ @GetMapping("/getOne/{id}") publicStudentgetOne(@PathV
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工程安装现场安全管理责任免除协议
- 村级农业种植技术承包协议
- 技术转让保密协议
- 塔机劳务用工合同
- 第3课时课件--大分子营养物质的消化课件 2024-2025学年北师大版 生物七年级下册
- 住宅小区绿化施工合同
- 公司口罩采购合同
- 服装店转让合同书
- 电气安装劳务合同
- 商业广场场地租赁合同书
- 房屋水电装修合同
- 医疗器械经营质量管理制度和工作程序目录
- 浙江省杭州市2023-2024学年高一上学期期末考试物理试题(含答案)5
- 2024钢结构工程施工合同范本
- 2023年浙江农商联合银行招聘考试真题
- 工业气体分离技术与设备管理手册
- 汽车底盘课件 课题7 行驶系统概述
- 小学教师读书分享活动课件
- 《一桥飞架连天堑》课件 2024-2025学年岭南美版 (2024)初中美术七年级上册
- 《经济法学》(第三版)电子教案
- 营地布景规划方案
评论
0/150
提交评论