位置: 首页 > 苏州web前端 > 张家港关于java培训班
张家港关于java培训班
预约试听
张家港关于java培训班
其然IT 教育师资

任小龙(Will、龙17)高级讲师

EasyJF开源团队成员,技术经理,高级讲师。 

擅长技术:JavaSE、Java Web、Spring、Hibernate、MyBatis、Spring MVC 、Struts2、Struts1、 WebService、Lucene、Android等开源技术;以及Oracle、MySQL等数据库技术。

龙老师实战经验丰富,热衷探索新技术,拥有多年的Java开发和培训经验, 授课富有激情又通俗易懂,知识点分析深入,举例贴近生活不乏幽默生动,注重引导学生思维。

讲课风格:课堂幽默生动,思维行云流水,授课水到渠成。

学生点赞:龙哥的视频,苍老师都喜欢。 

张家港关于java培训班

java入门要注意什么

张家港关于java培训班

学习java就像是一个种花的过程,不断地为其施肥浇水,它才会茁壮成长。 而我们学习java,就要不断的充实自己、提升自己,才能获得更多机会。很多开始学习java编程的小白,经常就会被概念、定义什么的搞糊涂。当分类 、对象、接口、构造函数等等各种专业名词出现的时候,你一定是脑子里好像一片空白,根本就搞不懂这些字眼的意思和关系,而且,这种情况下,很 容易导致你丧失自信心,开始逃避、拒绝,这些小白经常遇到的情况在我刚接触java的时候也遇见了,但是好在我足够幸运,遇见了诚筑说。我现在已 经是公司的项目经理了,今天,我为大家来总结了一些经验和建议,希望能够帮助到大家。

一点:熟练基本的j2seAPI

除去java语言本身的语法之外呢,要懂得并且熟练j2seAPI的API也是非常有 必要的,在这里,就建议大家首先去掌握字符串的处理、异常的处理、容器、输入输出、线程等,这些相对来说较为重要的。还有就是API的内容是非 常庞大的,关于API,一定要懂得查询API的文件说明,在了解了其作用用途或者目的才能够进行相对于的程序。

二点:稳固java的语法基础

学习java一定要学会使用java的程序语言,用来编写程序,但是学习程序语 言就要熟悉语法是怎么使用的。程序语言其实也是一种语言,不过跟人类的语言不同,这种语言是要和计算机沟通交流,那怎么做才能熟悉这种语言呢 ,我给出的建议是多看别人写的程序,了解人家是怎么用java来解决问题的。然后再找类似的程序去练习了,这样就能够从实际操作中检验自己是否真 的知道该怎么去解决问题了。

三点:加入贴吧论坛多参与讨论

根据我当时的经验,在大家学习的过程中,如果有人可以参与话题,共同讨 论的话,会加快你学习的速度。所以大家可以和我一样,找一个技术讨论的地方,贴吧啊,论坛啊都可以,在这里进行讨论,毕竟大家有着共同的目标 和理想,有着共同的话题可聊,这样的话,又大大节省了学习的时间。

学完基本的java语法呢,现在就该用java来进行实际的编程了,假如你需要 编写窗口程序,那就学Swing窗口设计;假如你要编写数据库什么的,那就学JDBC等等。

全 新升级企业需求的Java课程


张家港关于java培训班

全新升级企业需求的Java课程

历经16年累计10余万Java学员。打造专业的课程体系,值得你的信赖

Java基础

深入解析Java基础,直击面试常见问题。——184课时

数据库

及Web前端技术

包含主流Oracle和MySQL数据库,先进的Web前端技术,热门的JS语言 ——160课时

Java Web

开发及服务端框架

定制开发企业级框架,教授Spring技术核心,企业开发部署环境,规范代码 开发流程及文档——176课时

综合项目实战

飞扬小鸟、飞机大战、俄罗斯方块、T-DMS数据采集项目、T-netctoss项目 、云笔记系统——136小时2W代码量

搭建自己网站

16小时课程实战演练

——企业扶持

Spring AOP技术(基于AspectJ)的XML开发


>

SPRing AOP技术(基于aspectJ)的xml开发

@(Spring)[aop, spring, xml, Spring, annotation, aspectJ]

Spring AOP技术基于AspectJ的XML开发 Spring AOP的XML的开发 AOP的概述 什么是AOP Spring中的AOP Spring的AOP的底层实现 Spring的底层实现之JDK动态代理 Spring的底层实现之CGLIB动态代理 Spring的AOP的术语 Spring的AOP开发分成两类 AOP开发中的术语 Spring的基于AspectJ的AOP的XML开发 **步引入jar包 第二步创建配置文件 第三步创建需增强包和类 第四步将类交给Spring管理 第五步编写测试 第六步编写切面类和通知 第七步 配置切面类 第八步**配置实现AOP 第九步执行测试 切入点表达式的定义 切入点表达式定义语法 通知类型 前置通知 创建需增强类和方法 创建切面和通知 配置切面和通知 单元测试 后置通知 创建需增强类和方法 创建切面和通知 配置切面和通知 单元测试 环绕通知 创建需增强类和方法 创建切面和通知 配置切面和通知 单元测试 异常抛出通知 创建需增强类和方法 创建切面和通知 配置切面和通知 单元测试 **终通知 创建需增强类和方法 创建切面和通知 配置切面和通知 单元测试 Spring AOP小项目

Spring AOP的XML的开发

AOP的概述

什么是AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,**预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,**对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

从**开始的面向机器编程到面向过程编程到面向对象对象编程直至现在的面向切面编程可以看出,随着计算机软件的不断发展,其越来越符合人的思维习惯,同样的代码量所能实现的功能也越来越多。而AOP则是在不增业务逻辑的代码上,增加新功能。而AOP一般用于框架开发。在实际项目中,使用AOP也是一个趋势。

AOP面向切面编程,是OOP扩展和延伸,使用的是横向抽取机制取代传统的纵向继承体系对程序进行扩展。解决OOP中遇到问题。

Spring中的AOP

Spring的AOP的底层实现

Spring提供两种代理机制实现AOP JDK动态代理 :JDK只能对实现了接口的类产生代理。 CGLIB动态代理 :可以对没有实现接口的类产生代理,用的是底层字节码增强技术。产生类继承父类。
Spring的底层实现之JDK动态代理
package com.pc.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK动态代理实现 * 需要提供类的接口 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class JDKProxy<T> implements InvocationHandler { // 持有需要被增强的对象的引用 T target; /** * 构造方法必须提供被代理对象 * * @param target */ public JDKProxy(T target) { this.target = target; } /** * 创建代理对象 * * @return */ public T createProxy() { T proxy = (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return proxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强,这里实现前置通知 System.out.println("前置通知。。。。。"); // 调用原有接口方法 return method.invoke(target, args); } } package com.pc.test; import org.junit.Test; import com.pc.aop.JDKProxy; import com.pc.service.UserService; import com.pc.service.impl.UserServiceImpl; /** * 代理测试类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class ProxyTest { @Test public void testJDKProxy() { // 创建被代理对象 UserService userService = new UserServiceImpl(); // 未增强之前的方法 // 输出:保存用户 userService.save(); JDKProxy<UserService> jdkProxy = new JDKProxy<>(userService); // 创建代理对象 userService = jdkProxy.createProxy(); // 增强后的方法 // 输出: // 前置通知。。。。。 // 保存用户 userService.save(); } }
Spring的底层实现之CGLIB动态代理
package com.pc.aop; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; /** * CGLIB实现代理 * 使用字节码增强技术,生成代理类的子类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class CGLIBProxy<T> implements MethodInterceptor { // 持有需要被增强的对象的引用 T target; /** * 构造方法必须提供被代理对象 * * @param target */ public CGLIBProxy(T target) { this.target = target; } /** * 创建代理对象 * @return */ public T createProxy() { // 创建代理核心对象 Enhancer enhancer = new Enhancer(); // 设置父类 enhancer.setSuperclass(target.getClass()); // 设置回调 enhancer.setCallback(this); // 创建代理对象 T proxy = (T) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 增强,这里实现后置通知 // 调用原有类方法,获得返回值 Object result = methodProxy.invokeSuper(proxy, args); // 增强 System.out.println("后置通知。。。。。"); return result; } } package com.pc.test; import org.junit.Test; import com.pc.aop.CGLIBProxy; import com.pc.service.UserService; import com.pc.service.impl.UserServiceImpl; /** * 代理测试类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class ProxyTest { @Test public void testCGLIBProxy() { // 创建被代理对象 UserService userService = new UserServiceImpl(); // 未增强之前的方法 // 输出:保存用户 userService.save(); CGLIBProxy<UserService> cglibProxy = new CGLIBProxy<>(userService); // 创建代理对象 userService = cglibProxy.createProxy(); // 增强后的方法 // 输出: // 保存用户 // 后置通知。。。。。 userService.save(); } }

Spring的AOP的术语

Spring的AOP开发分成两类

传统AOP开发 基于AspectJ的AOP的开发(XML和注解)

AOP开发中的术语


切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。 连接点(join point):应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。 通知(advice):切面的实际实现,它通知系统新的行为。如在日志通知包含了实现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。 切入点(pointcut):定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。 引介(introduction):为类添加新方法和属性。 目标对象(target):被通知的对象。既可以是自己编写的类也可以是第三方类。 代理(proxy):将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变。 织入(Weaving):将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上: 编译期:切面在目标对象编译时织入。这需要一个特殊的编译器。 类装载期:切面在目标对象被载入JVM时织入。这需要一个特殊的类载入器。 运行期:切面在应用系统运行时织入。不需要特殊的编译器。

PS:spring只支持方法连接点,不提供属性接入点,spring的观点是属性拦截破坏了封装。面向对象的概念是对象自己处理工作,其他对象只能**方法调用的得到的结果。

Spring的基于AspectJ的AOP的XML开发

**步引入jar包


第二步创建配置文件

引入AOP的约束 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>

第三步创建需增强包和类

创建接口 package com.pc.aop.dao; /** * 客户持久层接口 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public interface CustomerDao { /** * 保存用户 */ public void save(); } 创建实现类 package com.pc.aop.dao.impl; import com.pc.aop.dao.CustomerDao; /** * 用户持久层实现类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class CustomerDaoImpl implements CustomerDao { @Override public void save() { System.out.println("保存用户了。。。"); } }

第四步将类交给Spring管理

<bean id="customerDao" class="com.pc.aop.dao.impl.CustomerDaoImpl" />

第五步编写测试

package com.pc.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.pc.aop.dao.CustomerDao; /** * 面向切面编程测试类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ // 配置Spring单元测试环境 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试Spring单元测试集成 @Test public void testSpring() { customerDao.save(); } }

输出

保存用户了。。。

PS:这时候没用使用AOP进行任何增强

第六步编写切面类和通知

package com.pc.aop.advice; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ public class MyAspect { // 通知:校验权限 public void check() { System.out.println("校验权限。。。。。。"); } }

第七步 配置切面类

<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/>

第八步**配置实现AOP

<!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:前置通知 --> <aop:before method="check" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config>

第九步执行测试

package com.pc.test; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.pc.aop.dao.CustomerDao; /** * 面向切面编程测试类 * * @author Switch * @data 2016年11月23日 * @version V1.0 */ // 配置Spring单元测试环境 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试Spring单元测试集成 @Test public void testSpring() { customerDao.save(); } }

输出

校验权限。。。。。。 保存用户了。。。

切入点表达式的定义

切入点表达式定义语法

切入点表达式基于execution之类的方法来实现的。在方法内部就可以编写切入点表达式: 表达式语法: [方法访问修饰符] 方法返回值 [包名.类名.]方法名(参数) [异常类型]


举例:

// 所有的public方法 public * *(..) // 所有service中的public方法 public * com.pc.service.*.*(..) // 所有以find开头的方法 * find*(..) // public修饰符,void返回类型,全路径匹配 public void com.pc.aop.dao.impl.CustomerDaoImpl.save(..) // 任意返回类型,全路径匹配 * com.pc.aop.dao.impl.CustomerDaoImpl.save(..) // 任意返回类型,类名以Dao结尾下的任意方法 * com.pc.dao.*Dao.*(..) // 任意返回类型,CustomerDao及其子类下的任意方法 * com.pc.dao.CustomerDao .*(..) // 任意返回类型,com.pc.dao包下的任意方法 * com.pc.dao..*.*(..)

通知类型


前置通知

前置通知:在目标方法执行之前完成的增强。获得切入点信息。 PS:接入点信息,其他类型的增强也可以**这种方法使用。

创建需增强类和方法
public class CustomerDaoImpl implements CustomerDao { @Override public void save() { System.out.println("保存用户了。。。"); } }
创建切面和通知
public class MyAspect { // 通知:校验权限 public void check(JoinPoint joinPoint) { System.out.println("校验权限。。。。。。"); // 输出接入点信息 System.out.println(joinPoint.toString()); } }
配置切面和通知
<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/> <!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:前置通知 --> <aop:before method="check" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config>
单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试前置通知 @Test public void testBefore() { customerDao.save(); } }

输出

校验权限。。。。。。 execution(void com.pc.aop.dao.CustomerDao.save()) 保存用户了。。。

后置通知

后置通知:在目标方法执行之后完成的增强。获得方法的返回值。

创建需增强类和方法
public class CustomerDaoImpl implements CustomerDao { @Override public String delete() { System.out.println("删除用户了。。。"); return "delete"; } }
创建切面和通知
public class MyAspect { // 通知:打印日志 public void printLog(String retVal) { System.out.println("打印日志。。。。。"); System.out.println("返回值为:" retVal); } }
配置切面和通知
<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/> <!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.*.delete(..))" id="pointcut2"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:后置通知 --> <aop:after-returning method="printLog" pointcut-ref="pointcut2" returning="retVal"/> </aop:aspect> </aop:config>
单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试后置通知 @Test public void testAfterRunning() { String delete = customerDao.delete(); System.out.println(delete); } }

输出

删除用户了。。。 打印日志。。。。。 返回值为:delete delete

环绕通知

环绕通知:在目标方法执行前和执行后完成的增强。阻止目标方法的执行,获得方法参数。

创建需增强类和方法
public class CustomerDaoImpl implements CustomerDao { @Override public void update(Integer id) { System.out.println("更新用户了。。。"); } }
创建切面和通知
public class MyAspect { // 通知:计算方法耗时 public void calTime(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("方法执行前。。。。。。"); // 打印参数 System.out.println("参数为:" joinPoint.getArgs()[0]); // 执行目标方法 joinPoint.proceed(); System.out.println("方法执行后。。。。。。"); } }
配置切面和通知
<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/> <!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.*.update(..))" id="pointcut3"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:环绕通知 --> <aop:around method="calTime" pointcut-ref="pointcut3"/> </aop:aspect> </aop:config>
单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试环绕通知 @Test public void testAround() { customerDao.update(6166); } }

输出

方法执行前。。。。。。 参数为:6166 更新用户了。。。 方法执行后。。。。。。

异常抛出通知

异常抛出通知:在目标方法执行出现异常的时候完成的增强。获得异常的信息。

创建需增强类和方法
public class CustomerDaoImpl implements CustomerDao { @Override public void find() { System.out.println("查询用户了。。。"); int i = 1 / 0; } }
创建切面和通知
public class MyAspect { // 通知:异常处理 public void throwHandler(Throwable ex) { System.out.println("异常处理。。。。。。" ex.getMessage()); } }
配置切面和通知
<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/> <!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.*.find())" id="pointcut4"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:异常通知 --> <aop:after-throwing method="throwHandler" pointcut-ref="pointcut4" throwing="ex"/> </aop:aspect> </aop:config>
单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试异常通知 @Test public void testAfterThrowing() { customerDao.find(); } }

输出

查询用户了。。。 异常处理。。。。。。/ by zero

**终通知

**终通知:无论目标方法是否出现异常总是执行的增强。

PS:该案例和异常测试案例对同一个target进行增强。

创建需增强类和方法
public class CustomerDaoImpl implements CustomerDao { @Override public void find() { System.out.println("查询用户了。。。"); int i = 1 / 0; } }
创建切面和通知
public class MyAspect { // 通知:关闭资源 public void close() { System.out.println("关闭资源。。。。。。"); } }
配置切面和通知
<!-- 配置切面类 --> <bean id="myAspect" class="com.pc.aop.advice.MyAspect"/> <!-- 面向切面配置,基于aspectJ --> <aop:config> <!-- 配置切入点 ,切入点是**表达式来实现的--> <aop:pointcut expression="execution(* com.pc.aop.dao.impl.*.find())" id="pointcut4"/> <!-- 配置切面,切面是由具体的切入点和通知组成的 --> <aop:aspect ref="myAspect"> <!-- 增强:**终通知 --> <aop:after method="close" pointcut-ref="pointcut4"/> </aop:aspect> </aop:config>
单元测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringAOPTest { // 注入依赖 @Resource(name = "customerDao") private CustomerDao customerDao; // 测试**终通知 @Test public void testFinally() { customerDao.find(); } }

输出

查询用户了。。。 关闭资源。。。。。。 异常处理。。。。。。/ by zero

PS:之后会在《Spring AOP技术(基于AspectJ)的Annotation开发中》中会介绍怎么使用注解进行AOP开发,用注解进行AOP开发相对于XML来说更便捷,也更容易理解。这两种掌握一种就可以了,但是如果要对AOP进行集中管理,**好还是使用XML方式。

Spring AOP小项目

GitHub:Spring AOP小项目 GitHub:MyStore-netease


相关推荐:


苏州JAVA培训   苏州JAVA培训班   苏州JAVA培训机构

苏州其然软件开发培训

进入机构首页
苏州其然软件开发

上课地址:苏州市昆山市震川西路111号名仕大厦

预约试听

倒计时:
11 : 58 : 41
其他试听课程
机构全部课程

学校课程导航