项目架构及开发流程梳理(二)
前言
学习完后首次整理,为了梳理面试时的项目讲解思路及整合贯通所学技术而整理;
温习分布式项目(double、zookeeper等技术)时二次梳理。
1 关于传统项目及SSM框架
1.1 网络编程
(详见链接:https://www.cnblogs.com/szrs/p/12121032.html)
1.2 对于架构与框架的浅述
- 软件开发的整体架构B/S、C/S 架构:单一的应用架构;
- SOA架构(Service-OrientedArchitecture-面向服务架构)。
- 架构的决策策又体现在框架和开发出的软件程序上;MVC是一种开发模式,多种框架都是这种模式的编程思想,将前台页面与功能调用与功能实现分开处理。
1.3 框架讲解流程
- 配置mybatis.xml;
- 整合web.xml-applicationContext-mabatis.xml;
- SpringMVC整合mabatis.xml和applicationContext.xml-->三个配置文件: web.xml-->springmvc.xml-->applicationContext-mabatis.xml后两个一般不写在一起。
- SOA项目:将springmvc.xml拆分为多个配置文件。aop、Dubbo、静态资源读取。
1.3.1 第一步:谈框架:B/S项目,SSM框架
B/S项目==》服务器和网页浏览器的方式==》就是一个单一应用架构。
访问量小,主要技术点是提高数据库访问效率的框架:简化增删改查工作量的数据访问框架(ORM)对象。
关系映射: object relational mapping是关键;
只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
肯定啊,不需要分布式开发-->分布式:服务框架--》针对于服务Dubbo:服务提供者、注册中心、消费者。
相对于部署在多个Linux上部署项目==》成本是低的多。
下面有对B/S项目、分布式项目搭建的详细讲解。
1.3.2 第二步:谈搭建开发环境、配置开发环境
搭建开发流程:首先呢,我用的是SSM框架,那么。我们先搭建开发的框架,在搭建框架前,我们先把我们需要的jar包导进项目。
jar包:核心包。
log4j:日志包。
Asm: java文件处理包,spring和mybatis依赖于此包运行。
Cglib: mybatis 的动态代理包。凭此可以实现接口的实现类的实例化和方法的执行。
Commons: spring 的日志记录包
Javaassist: 字节码文件助理包,专门负责处理字节码文件。
Log4j: java程序日志包。4—>for
Mybatis -3.2.7.jar : mybatis框架执行的核心包
Slf4j: 另一种格式的日志包。因为mybatis支持多种格式日志,可以在运行时进行选择。
1.3.3 第三步:谈配置文件的编码及原理
1.3.3.1谈一:Mybatis框架的配置
1、src目录下mabatis.xml:
首先改.dt文档规范验证器的路径为本地,在Windows的xml的xmlcatalog下。
【1】根标签:configuration。标签都写在下面。settings,开启log4j日志;typeAliases给包配置别名。
【2】environments:mybatis框架的使用环境,有个default属性,必须要跟他的子标签environment的name一样。
【3】datasource标签:数据源-->连接数据库-->子标签properties:Driver,url,username,password。也可以配置在configuration下,或从外部引入。连接池技术。
【4】transactionManager
<!--mybatis的事务管理 --> <transactionManagertype="JDBC"></transactionManager>自己处理 <transactionManagertype="MANAGED"></transactionManager> 交给其他框架如Spring处理
【5】mappers:引入加载任何资源文件到当前的配置文件当中;
<mappers> <!-- <package name="com.bjsxt.mapper"/>-->包的位置,一般写包,xml文件和接口都包括。 <mapperresource="com/bjsxt/mapper/FlowerMapper.xml"/>只有xml文件时; <mapperclass="com.bjsxt.mapper.ClassroomMapper"/>提供路径,在接口中写注解; <mapperclass="com.bjsxt.mapper.StudentMapper"/> </mappers>
类如pojo-->实体类。
mapper-->接口-->只写方法。
对于复杂的SQL语句,建议用xml来写,例如动态SQL。分页,模糊查询。
查询的SQL语句看 Mybatis8注解的自动装备,手动装配及N+1查询。-->参数与SQL查询语句属性的对应。
2、mapper.xml文件。
<!-- 命名空间相当于持久化层接口的名称 -->
<mappernamespace="com.bjsxt.mapper.ClassroomMapper">
测试:分为只有mapper.xml文件;还有mapper接口:前者测试时,通过resources对象获得SqlSessionFactory工厂后,利用构建者模式SqlSessionFactoryBuilder()快速生成某个实例化的对象。factory,然后获得封装了对数据库进行CURD的所有操作的API接口的SqlSession对象session;通过session对象,进行增删改查操作,而有了mapper接口以后,还可以get得到mapper接口的动态代
理类,来调用mapper中的方法。
// 加载当前的mybatis.xml配置文件 InputStream is = Resources.getResourceAsStream("mybatis.xml") // 通过构建器构建SqlSessionFactory工厂对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); // 获取SqlSession 对象 SqlSession session = factory.openSession(); FlowerMapper fm = session.getMapper(FlowerMapper.class); //int n = fm.insFlower("玫瑰", 12.23, "中国"); Flower f=new Flower(); f.setName("杜鹃"); f.setPrice(12.3); f.setProduction("北京"); //增加新的花卉品种 int n = fm.insFlower2(f);//或者: int n = session.insert("com.bjsxt.mapper.FlowerMapper.insFlower",f); System.out.println(n); session.commit(); session.close();
拓展Mybatis的缓存:测试完成以后一定要关闭session,要是DML操作还需要提交事务。
Mybatis缓存:减少与数据库之间的交互,在数据库与应用程序之间建立的中间件。
(1)一级缓存:基于sqlSessin对象.缓存的是PrepareStament对象。是针对于标签的。
二级缓存:基于SqlsessionFactory。每次创建一个sqlsession对象都会有缓存。效率更高,但是可能会造成数据的冗余,使数据不准确。避免pojo的繁多的实例化,在Mybatis.xml配置文件中添加:
<cachereadOnly="true" ></cache>
数据标识符:映射文件中查询id标签的id值。
(2)ThreadLocal对象:类似容器类,存储一个对象(object),并且在同一个线程下的任意位置可以访问。创建一个工具类,工厂设计模式,使所有session为同一个对象。
1.3.3.1谈二:Spring的配置
1、配置Spring,第一步,导包。创建applicationContext.xml:
- Schema比.dtd的功能更加强大,文档验证器:规范,提示;schema能通过扩展验证组建扩充了新的功能,可以不断适应xml文档内容和功能的变化。
2、单纯的一个spring的配置文件:就是实例化,配置bean标签,id,calss属性:IOC和DI注入。
Spring不重复的造轮子,它的出现最大的服务的就是Service层,业务层,不再需要new对象。
==》IOC,控制反转,以前就是反转,inversion,来new对象。现在只需要在配置文件中bean一个需要实例化的对象,把创建对象托管给Spring容器。有三种方式:
type:
name:
index:
==》DI:依赖注入:和IOC一个意思。依赖是类之间的关系,这里用关联更贴切,因为是一个类作为另一个类的属性,给该属性赋值。
//测试: 类路径: 从classes文件包中查找xml配置文件,生成spring容器 ApplicationContext ac = new ClassPathXmlApplicationCon text("applicationContext.xml"); //获取People实例 People people = ac.getBean("peo", People.class);
==》关于依赖注入,有自动注入:autowire。byName,byType.constructor。为NO时根据default-autowire的属性值来确定。这里的type和name指的是作为属性的类的bean标签的id值。
后面引入注解,当配置文件中引入:
<context:component-scan base-package="com.bjsxt.service.impl"></context:component-scan>
支持注解扫描com.bjsxt.service.impl这个类,(我认为已经封装了bean形式的name和class属性)即支持注解时,@Autowired根据byName注入,注解@Resource依次byName和byType注入。而Spring还有其他的子标签。我们只是介绍了它实例化对象即配置bean(和支持注解)的配置。
3、下面来谈下它Spring整合Mybatis和其他子标签(aop、tx、context)的使用--》他们都需要引入新的文档验证器扩展文件。
3.1 spring整合mabatis
1、两个整合
- 配置web.xml文件:监听和路径,自动加载application.xml文件。<context-param>和<listener>
- applicationContenxt-mabatis.xml:
【1】配置数据源dataSource context:property-placeholderlocation从外部引入配置文件,注意此时的property的name不能随便起,从源文件中找。
【2】配置SqlSessionFactory工厂对象,即由spring托管mybatis工厂类的实例
【3】配置spring 托管原mybatis.xml对mapper下接口的监控
【4】配置serviceImpl的bean。这步一般都有注解支持,不需要每创建一个接口和它实现类时都在配置xml文件添加bean了,只需要配置context:component-scan标签,使该实现类支持注解扫描即可。这个标签需要添加文档验证器的context扩展文件。而在没有注解时,测试时用WebApplicationContext来得到实例化的bean对象。且在没有用注解来实例化时,是需要写作为属性的类的get、set方法的。
在整合SpringMCV后,扫描controller层的注解扫描文件一般和业务层的扫描文件是分开配置的。
现在已经大致说完了Spring和Mybatis的整合。
当然,Spring还有对事务tx和切面编程aop的支持,我们下面在项目功能实现的时候可以详细说。
3.2 aop
tx:
编程时事务-->代码必须与事务直接关联,如commit,rollback.,
声明时事务-->把事务交给spring来处理,只需要声明需要添加事务的方法即可。跟aop相关。
在SSM项目中,在业务层,即service层添加事务,service层的方法作为切点,切异常只能抛出,如果在业务层处理了异常,则通知中接收不到事务。在配置文件中添加事务的配置:
applicationContext.xml中:
(1)添加封装了事务的类的bean,事务管理器让Spring加载封装事务的处理类;
(2)事务需要处理的方法:tx标签
<tx:advice id="tran" transaction-manager="transactionManager"> <tx:attributes><!-- 切点的方法标签中还可定义事务的多种属性 --> <tx:method name="register" read-only="false"/><!-- 默认值为false,表示处理事务。如果为true,则表示不处理事务,一般用于查询方法。 --> </tx:attributes> </tx:advice>
(3)添加通知:就是aop:aop:config-->aop:pointcut-->aop:advisor
Spring 底层采用的是jdk的动态代理,默认不生成接口真正的实体类对象。造成的问题是,如果要执行某个接口的方法,必然要生成一个动态代理对象,此时如果要将动态代理转换真正的实体类,必然抛出动态代理转换异常$proxy
如何解决:
在spring 配置文件中,添加标签
<aop:aspectj-autoproxyproxy-target-class="true"></aop:aspectj-autoproxy>
此标签的含义即将底层的动态代理模式由jdk转换为cglib.转换之后,无论是否转换类型,都不会再报错。注意:即使是jdk模式的动态代理,如果不发生转换,不会报错。
proxy-target-class 的值,false为默认值,表示不转换成实体类,true正好相反。
事务的传播性:了解:特定的需求下,有的方法在执行时交叉,涉及到多个事务的相互调用,如何控制事务的执行,就是事务的传播性,接触的比较少。Propagation:6种传播行为:Required,Supports等。
关于事务的隔离级别:
脏读:两个事务之间:一个事务修改了数据库中的数据,还没有提交,另一个事务恰好读取了该数据,这个数据是不准确的,即出现了脏读。
不可重复读:在同一个事务中出现的现象:即在一个事务中第一和第二次读取一个数据之间,有另一事务对该数据进行了修改,那么,这两次读取的数据是不一样的,即出现了不可重复读。
幻读:当事务不是独立执行时发生的一种现象。例如一个读取,一个插入数据。高并发发生时(即同时实行),有可能读取的不是全部数据(录入的数据还没有来的及读)
3.3 SSM框架,三者结合的配置:
3.3.1 web.xml
在配置了Spring的context-param和listenr查找application.xml的基础上,配置SpringMVC
servlet和servlet-mapping,通过mapping的url,拦截所有对分发器的访问,找到name,在找到servlet的name,再找到其对应的class即分发器。这里需要配置load-on-startup属性,为1,来主动实例化分发器。同时,配置init-param标签,来将SpringMVC.xml配置在src目录下。
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>ContextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
3.3.2 applicationContext-mabatis.xml
这里配置的就是注解的扫描,数据源,SqlSessionFactory工厂,托管mabatis。根据需要添加切面aop和事务tx。 url-pattern的配置把*.do改成/后,是在写requestMapping是简单了,但是把请求的静态资源也给拦截了,在返回客户端,再次请求时,路径已然改变,此时就需要在Springmvc.xml里配置静态资源。
(1)
<beans> <!-- //记住: 在spring配置文件中,只能扫描service层,不要扫描controller --> <context:component-scan base-package="com.bjsxt.service.im pl"></co ntext:component-scan> <context:property-placeholderlocation="classpath:db.properties"/> <!-- 数据源配置 --> <bean id="dataSource" class="org.springframework.jdbc.dataso urce.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 没必要,自动注入 --> <!-- <property name="dataSource" ref="dataSource"></property> --> </bean> spring托管mabatis。 <bean id="mapper" class="org.mybatis.spring.mapper.MapperScanne rConfigurer"> <property name="basePackage" value="com.bjsxt.mapper"></property> <property name="sqlSessionFactoryBeanName" value="factory"></property> </bean>
(2)
<!-- 切面编程,后置通知 --> <!-- 添加角色以及分配权限后置通知 --> <bean id="addRolMenAdvice" class="com.bjsxt.advice.AddRolMenAdvice"> </bean> <!-- 切面编程,后置通知 --> <!-- 删除角色以及该角色的权限,前置通知 --> <bean id="delRolMenAdvice" class="com.bjsxt.advice.DelRolMenAdvice"> </bean> <aop:config> <!-- 针对切点a(方法)增加前、后置通知 --> <aop:pointcut expression="execution(*com.bjsxt.service.impl.RolesServiceImpl.addRoles(..))" id="mypoint"/> <aop:pointcut expression="execution(* com.bjsxt.service.impl.RolesServiceImpl.delR ol(..))" id="mypointDelRM"/> <!-- 后置通知关联 --> <aop:advisor advice-ref="addRolMenAdvice" pointcut-ref="mypoint"/> <aop:advisor advice-ref="delRolMenAdvice" pointcut-ref="mypo intDelRM"/> </aop:config> <!-- 针对切点(方法)增加前、后置通知 --> <bean id="logsAdvice" class="com.bjsxt.advice.LogsAdvice"></bean><!-- 查询客户信息通知实例化 --> <bean id="loginlogsAdvice" class="com.bjsxt.advice.LoginlogsAdvice"> </bean><!-- 记录客户信息通知实例化 --> <bean id="addCusAdvice" class="com.bjsxt.advice.AddCusAdvice"> </bean><!-- 记录客户信息通知实例化 --> <bean id="delCusAdvice" class="com.bjsxt.advice.DelCusAdvice"> </bean><!-- 记录客户信息通知实例化 --> <!-- <aop:aspectj-autoproxy proxy-target-class="true"> </aop:aspectj-autoproxy> --> <aop:config> <!-- 用户查询客户信息 --> <!--<aop:pointcut expression="execution(* com.bjsxt.service.impl.CustomersServiceImpl.selById(..))"id="mypoint1"/> --> <aop:pointcut expression="execution(* com.bjsxt.service.impl.CustomersServiceImpl.selById(..))"id="mypoint2"/> <aop:pointcut expression="execution(* com.bjsxt.service.impl.CustomersServiceImpl.addCus(..))"id="mypoint3"/> <aop:pointcut expression="execution(*com.bjsxt.service.impl.CustomersServiceImpl.delCus(..))" id="mypoint4"/> <!-- <aop:advisor advice-ref="logsAdvice" pointcut-ref="mypoint1"/> --> <aop:advisor advice-ref="loginlogsAdvice" pointcut-ref="mypoint2"/> <aop:advisor advice-ref="addCusAdvice" pointcut-ref="mypoint3"/> <aop:advisor advice-ref="delCusAdvice" pointcut-ref="mypoint4"/> </aop:config> </beans>
3.3.3 springmvc.xml
配置扫描controller的注释的标签,同时还要配置mvc驱动,来支持注解的扫描。
在访问页面的静态资源时,需要配置mvc:resources
其实,基础的配置就是扫描注解和支持扫描注解的驱动。以及访问静态资源的配置。
<!-- 配置访问静态资源 --> <mvc:resources location="/" mapping="/**"></mvc:resources
后面的三个配置根据需要来配置。
自定义解析器:
<!-- 配置自定义解析器 --> <bean id="" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean>
过滤器:
例如在中文乱码:filter-->springmvc已经封装好了过滤器的类。只需要在web.xml中进行配置即可。
其中param-name的值不能随便写。
<init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param>
拦截器:
1、定义:先于客户端访问控制器之前执行,在控制器及jsp执行之后再次执行。
在访问控制器之前做些事儿,在访问控制器之后再做些事儿。
2、基于其特点,与filer相似,与aop相似。
3、拦截器与filter的比较
3.1 filter:可以过滤一切访问资源
3.2 interceptor: 只针对控制器方法有效
4拦截器与aop的比较:
4.1 aop:针对所有切点方法进行关联通知(前置、后置)
4.2 interceptor: 只针对控制器方法有效
5、执行原理:拦截器的实现首先需要实现 HandlerInterceptor 接口,并且实现相关的方法。
6、执行顺序:
- MyInterceptor.preHandle() 先于控制器执行
- DemoController.demo1()执行控制器
- MyInterceptor.postHandle()后于控制器,先于jsp
- MyInterceptor.afterCompletion()后于jsp
7、方法中的参数:
- req:请求对象
- resp:响应对象
- Object:handler 所拦截的控制器方法
<!-- 配置拦截器 --> <!-- 方式一 针对所有方法 --> <!-- <mvc:interceptors> <bean id="mi" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptors> --> <!-- 方式二 针对指定的方法 --> <!-- <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/demo1"/> <bean id="mi" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/demo2"/> <bean id="mi2" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/demo3"/> <bean id="mi3" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> --> <!-- 配置拦截栈 --> <mvc:interceptors> <bean id="mi" class="com.bjsxt.interceptor.MyInterceptor"></bean> <bean id="mi2" class="com.bjsxt.interceptor.MyInterceptor2"></bean> </mvc:interceptors> <!-- 寻找注释实例化的包 --> <context:component-scan base-package="com.bjsxt.controller"></context:component-scan> <!-- 加载MVC注释驱动,使得注释生效 --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 配置自定义解析器 --> <bean id="" class="org.springframework.web.servlet.view.Inter nalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置访问静态资源 --> <mvc:resources location="/" mapping="/**"></mvc:resources> <!-- 配置拦截器 --> <!-- 方式一 针对所有方法 --> <!-- <mvc:interceptors> <bean id="mi" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptors> --> <!-- 方式二 针对指定的方法 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/demo1"/> <bean id="mi" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/demo2"/> <bean id="mi2" class="com.bjsxt.interceptor.MyInterceptor"></bean> </mvc:interceptor><!-- 注意id不能一样 --> </mvc:interceptors> </beans>
1.3.4 第四步:业务逻辑及实现
1.3.4.1 第一:关于aop日志
实现中用到的技术及其安装,配置,使用业务实现之日志:自定义日志,注解实现。
【1】新建annotation,自定义注解@interface,编写方法,default " ";
【2】在配置文件springmvc.xml中添加支持
<!-- 启动对@AspectJ注解的支持 gz--> <aop:aspectj-autoproxy proxy-target-class="true" /> <!--切面--> <bean id="SystemLogAspect" class="org.rentcar.annotation.Syst emLogAspect"></bean>
这个切面方法里面,把所需要记录的写入到数据库。
【3】创建切面方法,并用反射的原理,得到在Controller层注解的自定义注释的内容。
这里定义切点
@Pointcut("execution (* org.rentcar.controller.*.*(..))") public void controllerAspect() { } /** * 后置通知 用于拦截Controller层记录用户的操作 * * @param joinPoint * 切点 joinPoint可以获取连接点的参数列表,方法对象,目标对象,代理对象本身。 */ @After("controllerAspect()") public void doAfter(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); // 读取session中的用户 //待实现 User user = (User) session.getAttribute("user"); // 请求的IP String ip = request.getRemoteAddr(); //得到controller层的方法的描述 public static String getServiceMthodDescription(JoinPoint joinPoint) throws Exception { String targetName = joinPoint.getTarget().getClass(). getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String description = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { description = method.getAnnotation(SystemLog.class).description(); break; } } } return description; }
1.3.4.2 第二:关于RBAC角色权限
思路:涉及到拦截器,访问时先进入拦截器:三个判断:
判断路径是否为控制器中登录的方法:是的话,放行;否则跳到登录页面;
判断用户是否为空,为空,跳到登录页面;else-->
判断user中是否有要访问的路径,有的话跳转,否则跳到登录页面。(这里封装用户的实体类中封装了该用户所拥有的路径的集合)
Stringuri=req.getRequestURI();//sxt/login
RBAC主要难理解的两点:最简单的RBAC-url也需要六张表。还有user,role,url,menu。
(1)数据库表的设计:引进role角色表:
[1]设计role-user:直接给用户分配角色,简单,容易操作。
[2]设计role-url:给角色分配路径,为了访问安全,通过在拦截器中进行判断,防止没有登录的访问程序的页面。
[3]设计role-menu:指定每一个角色对应的菜单。
这样创建好角色,分配好角色的路径,菜单,直接给用户分配角色就可以了。
(2)SQL语句:主要就是表menu的自查询:子菜单父级菜单编号就是父菜单的菜单编号
菜单menu的自查询:编码中用到递归:代码:创建的实体类userMenu:四个属性:id,name,pid(父节点id),List<userMenu>children.
List<UserMenu> list = menuMapper.showMenu(rid, pid); //System.out.println(list.get(0)); for (UserMenu userMenu : list) { List<UserMenu> menu = menuMapper.showMenu(rid, userMenu. getId()); userMenu.setChildren(menu); } return list;
其中rid为角色的id,pid为menu中父节点的id。
select * from menu where idin (select mid from role_menu where rid=#{0}) and pid=#{1}
角色表和菜单表是多对多的关系:查询时,先根据角色id查出该角色对应的菜单id,然后根据菜单id和pid查出此时的所有父级菜单,再利用循环,查出每个父级菜单下的子菜单(是一个集合),然后利用递归,查询每个子菜单的子菜单。
通常就有两级目录。
RBAC在项目中的实际作用:
让不同的用户看到不同的菜单(登陆之后)
不同用户看到不同的网页显示效果:
有些人可以看到增加功能按钮
有些人只能看到显示功能按钮
不同的人员在相同的项目下执行不同的功能 。
2 关于分布式项目及相关技术
(1)单一应用框架:提高数据访问层框架ORM.即应用程序和数据库数据交互的效率。数据存储过程。
(2)垂直应用架构:当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的 Web框架(MVC) 是关键。
(3)分布式服务架构:当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
(4)流动计算架构:当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。 此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。nginx负载均衡,调用集群中压力小的服务器。
2.1 电商项目讲解流程
2.1.1 搭建项目
SOA项目Service-OrientedArchitecture,(即面向服务架构)
服务太多,将具体的服务拆分出来为多个项目。我们拆分的这多个服务部署在多个服务器上,整体分成前台和后台两个大的模块。
通过maven管理项目。前端的框架为Easy-UI。
创建ego-parent。
数据库的设计多少张表,主要并不是我参与设计的。
利用逆向工程创建ego-pojo。
编写过程中创建有ego-commons模块放公共类。
我们用的是dubbo分布式开发。创建ego-service编写服务的接口。ego-dubbo中编写服务的实现类,并把服务发布到注册中心。
其余的模块如ego-manager、ego-portal等均为消费者。
ego项目的分布式开发:
哪里体现了分布式开发?体现了SOA的思想?
答:SOA将整个项目根据具体的功能需求分为多个模块,将服务发布在一个单独的服务器上,体现了SOA的面向服务的架构思想,而将项目即服务的消费者部署在不同的服务器上,显然这是dubbo在管理服务,则体现了分布式开发的思想,而项目可以部署在多个服务器上,但是,这些项目要求是完全相同的,此时就需要利用Nginx来代理服务器,负载均衡,来提高访问效率,降低服务器端的压力,这里体现了集群。
Nginx的负载均衡:打开80端口号,nginx安装目录下/conf/nginx.conf中三连配:
upstream 在server{}标签上 upstream test.ego.com { server 192.168.26.10 weight=2; server 192.168.26.128 weight=3; server 192.168.26.138 weight=2; …… } server{ listen 80; server_name test.ego.com; location /{ proxy_pass http://test.ego.com; } } |
server_name与upstream后的名字一样。proxy_pass后面的是访问路径,upstream后面的是代理路径。
在C:\Windows\System32\drivers\etc的hosts中配置ip和网址映射。第一次做负载均衡的时候,需要更改window的环境变量。 192.168.26.148 test.ego.com前面的是nginx的IP,后面的是访问路径。
Maven构建项目:通过在pom.xml中添加依赖。
服务提供者: ego-parent ego-service ego-dubbo ego-pojo ego-commons 消费者: ego-manager---> 后台管理----8080pom.xml ego-item--->存储商品信息----8081--将商品的详细信息存储到redis ego-portal---> 前台页面----8082 ego-seach-->搜索商品-solr---8083--搭建solr将商品信息存储到solr中 ego-passport--单点登录------8084 ego-cart----购物车--- 8085--存储在redis中,购物车的信息 ego-oder---订单---- 8086 ego--pay-->直接写到ego-oder中 |
2.1.2 具体功能-购物车模块实现
我主要负责的购物车模块,对其他的模块也有一定的了解。
购物车的实现:
怎么实现:
【1】
<1>购物车的添加,删除,修改
当你搜索,查看商品的详情时,就是有一个添加购物车的按钮,没有直接付款的,就跟京东的很像点击添加购物车。就会跳到一个继续购物或是去购物车结算。当点击添加购物车时,已经将商品的信息,封装成一个购物车的实体类对象。
存储到redis,那么,为什么要存储到redis中?
首先,添加,删除等操作,需要不停的跟数据库进行交互,这样,提高放到redis缓存中,提高和数据库的数据的交互效率,提高用户的体验。但是,最重要的确实在客户下次打开购物车时,上次添加的商品仍然是存在的。
我们都逛过淘宝,京东,他这个购物车当你下次打开的时候,你的这个商品肯定还是要在的,以前,做管理系统,它是一个端口,不跨服务。我们通常将数据可以存放在application
session或cookie中,但显然都是不合理的。
【1】添加的实现过程:
我们都知道,redis是非关系数据库,key,value的形式存值,这里,key用用户的id。
首先,我们在单点登录里,已经把用户的信息存储在了redis中,且其id为user:UUID.这个UUID是存在cookie里的(在ego-commons公共类中,存放多个服务都要用到的工具类)。这样,我们就能得到当前用户存在redis中的key,进而得到用户的信息。
通过用户的信息,的到用户的id,然后的到存放购物车的key。然后通过前台传来的itemId得到商品的信息(在查询商品详情时,我们已经把商品的信息存放到redis中了),把购物车需要的商品的属性存到封装购物车的实体类中。此处需要注意的是,redis中封装的可能为多个商品的信息,所以此处从购物车读取出来的是一个购物车中商品的集合。
【2】显示购物车信息
添加购物车完成后,我们再次声明,在购物车页面我们都是没有支付按钮的,此时,只能点击去结算,会跳到一个结算服务?666写一半服了。
2.1.3 单点登录
分布式项目中,因为不同的服务器,需要SSO单点登录,将用户信息存储在redis中,添加拦截器,访问时没有登录,直接跳到登录页面;实现思路:key=user:uuid,将uuid存储在cookie中,传入到后台进行判断。添加:将前台传入的user与数据库比较,如果存在,就创建uuid,并将用户信息存入到redis中,同时跳转到上一个页面。
为什么,多个服务,跨域访问了。用户的信息存在redis中,在别的服务页面添加拦截器,没有登录的用户跳转到登录页面。
注意:涉及到redis的存储,就需要有JedisPool及其实现类来set,get,exist来操作redis。同时在ego-common中添加cookie,json的工具类。
2.1.4关于各种中间件的安装复习
2.1.4.1vsftp--nginx代理
ftp协议:安装,文本传输协议,更安全,上传文件的效率更快,一次上传多个,指定用户,只能看到指定路径的内容,TCP/IP协议,相对缺点是必须要安装FTP服务器,ftp服务器上的文件必须下载才能使用。于http超文本传输协议而言。http协议是Internet最常用的网络协议vsftp可以看做是Linux的一个小的组件。
为什么用FTP服务器做图片的服务器?
在分布式项目中对资源文件的统一管理。确保路径是唯一的!
java FTPClient技术上传图片到vsftp服务上,需要在pom.xml中添加依赖。
Vsftpd 是通过ftp协议访问的!ftp协议的端口号21。Filezila:sftp:默认端口号22。
安装:并用Nginx代理Vsftp的访问路径,用http访问。
安装soeasy:
[root@bogon ~]# yum -y install vsftpd
-y :表示yes、
安装完成之后,有/etc/vsftpd/vsftpd.conf 文件,是vsftpd的配置文件。
/etc/vsftpd/vsftpd.conf关闭匿名访问。
重启ftp服务:service vsftpdrestart
访问:ftp://192.168.26.30/gaoyy.jpg
用nginx代理:正向代理:代理一个路径,访问一个服务器,又客户端决定,例如nginx代理vsftp的图片服务器
反向代理:代理另一个服务器,而该服务其并不由客户端决定,例如网警,电商项目中的负载均衡。
C语言编写的:需要安装gcc环境:yum install gcc-c++ -y
在配置文件nginx.conf中进行配置。代理vsftp时:设置访问权限用户,修改路径location。
修改完配置文件后都需要重启。
2.1.4.2redis集群ruby管理集群
非关系型数据库,c语言编写,key-value存储数据,不要提前定义数据存储词典,是存储在内存中的,吃
内存,访问速度快。缓存。--->有持久化机制:AOF和RDB.
AOF:实时备份,实时追加log日志文件。导致文件过大,恢复时间长
RDB:隔一段时间进行备份,导致数据丢失多。默认的方式。
RedisDesktopManager对redis数据库进行管理。
安装:
单机安装:导入redis压缩包到root目录下并解压-->添加gcc-c++依赖
yum install gcc-c++ automake autoconf
-->创建安装目录mkdir-p /usr/local/redis,并用make进行编译。
-->安装makeinstall PREFIX=/usr/local/redis
将配置文件redis.conf复制到bin目录下并修改配置文件redis.conf中的daemonize为 yes表示后台启动。
然后./redis-server ./redis.conf重启服务。
-->客户端连接服务,要设置防火墙端口,或者直接关闭防火墙。
并设置ip地址:redis.conf配置 bind为:bind*; 其中databases 16表示数据库默认为16个。
redis-cli :表示redis的客户端
redis-server :表示redis的服务器
五种数据类型:String ,Hash,List,Set,sorted Set
实际开发中一般用String和Hash{“”:””, “”:””}键值对形式的数据。
在eclipse中,实现对redis数据库的管理,首先导入jar包,应用JedisPool使用数据库连接池来管理数据源。
集群的实现及管理:
实际中,都是用redis集群来存储数据的。redis集群的安装,并通过ruby来管理redis集群。
单机版集群:
上传ruby脚本到root目录,解压,并在root目录创建redis的配置,日志和数据的目录。
上传6个redis的配置文件和一个公共的配置文件(只需要修改bind(IP)和支持集群,故需要一个公共的配置文件)到新建的配置目录中。并创建./start.sh启动.赋权。启动六个服务。
第三步是通过ruby来管理redis服务,实现集群,执行创建集群命令。创建成功。然后可以通过./redis-cli –c –p6379来测试集群。-c代表集群,-p代表端口。
slots槽16384个,通过crc16算法进行计算出存在哪个端口管理的redis中。
整合进spring:redis整合到项目中,就是把redis交给Spring管理:
(1)配置文件:配置redis集群的IP地址和端口。
(2)管理jedis的dao层和实现类,配置支持注释@Repostory。
(3实现类中能有真正调用redis的set和get方法的Redisclients类。
2.1.4.3solr查询
简介:是一个独立的企业级搜索应用服务器, Apache一个开源的搜索服务器,实际就是一个java项目,部署在tomcat中!。
主要基于 HTTP 和 ApacheLucene 实现。
基于标准的开方的借口,支持http,xml,json查询。可以分页,高亮,可视界面管理。
根据索引全文搜索,需要提前建立索引库。比like效率高,like顺序查询,且没有索引。提高用户体验。
原理:
索引库存放索引字段:solr理解为一个数据库-->document对象理解为表的一行数据-->file属性理解为字段。
在配置文件schema.xml中配置fieldType为中文词条解析器,并配置filedName为查询字段。
在ego项目中,查询商品的详情,需要配置商品的字段,可以根据单个字段查询,同时还可以配置
copyField source属性,在多个字段中进行词条的查询。
Solr会对搜索内容进行词条拆分把拆分后的词条按照已经建立好的索引中进行搜索内容。
单个solr安装:
需要tomcat和jdk。上传并解压solr的压缩包,把其解压后的war包部署在tomcat上,
修改tomcat的./start.sh和./shutdown.sh文件指定特定的tomcat。并修改solr的web.xml文件的索引库
路径。启动tomcat,访问solr出现solr管理页面则安装成功。
这就是solr的安装,索引库配置及查询的原理。
solr的集群需要借助zookeeper。因为tomcat和solr都不具有集群的功能。
一个leader,两个从。每个从管理两个tomcat。
复制tomcat及修改端口号-->修改tomcat的start.sh和shutdown.sh-->修改catalina.sh-->复制并修改
web.xml-->修改solr.xml的端口号与tomcat的对应。测试时一定要关闭防火墙,开启zookeeper。
在ego-search项目中,需要pom.xml中添加solr.jar包的依赖,在配置文件中配置zookeeper集群的IP。
查询的实现过程:在搜索框中输入手机字段,点击搜索,则“手机”作为在solr集群中查询的词条。还
需要从前台获取的有page,即当前页码,作为solr分页的依据。
创建实体类为存放页面需要的商品的信息。在业务层实现类创建该集合,并将查询条件赋给查询的file,
查询出所有符合条件的商品的集合并得到高亮数据;然后遍历该集合,对比高亮字段,得到高亮的集合,
把所有页面需要的属性放入到实体类中,在放到集合里,赋给页面需要的容器里return。
2.1.5 mycat
Mysql通过Mycat集群。
mycat是一个使用java语言编写的数据库中间件.连接应用程序与数据库之间进行数据交互。
mycat的作用就是:当数据量较大时,将一个数据库分成多个逻辑库,相当于数据库的集群。
分库-->一个数据库通过mycat分为多个数据库这些数据库统称为逻辑库,(垂直--根据业务)
分表-->(水平--根据操作)
主要配置:scheme.xml:配置读写分离的主机:mysql主从数据库;定义逻辑库,逻辑表。
server.xml:定义逻辑库,连接数据库的用户名和密码。
rule.xml:定义mycat的分片规则crc32,一种算法。看数据被分到哪个逻辑库中。
主从数据库:
主写数据,从读取数据。
主从数据库。修改配置文件my.cnf,开启日志功能及给定server_id.因为从数据库是通过IO和SQL线程复制及读取日志来实现备份。然后,还需要创建用户,同时告诉从数据库的IP,使从数据库具有备份即访问主数据库的权限。
2.1.6 最后关于项目的发布:dubbo打包
用Maven中的Assembly 插件:(1)pom.xml添加依赖:打包插件信息及assembly打包的描述文件;
(2)项目根目录下创建assembly文件夹,并创建assembly.xml文件,复制配置信息;规定了tar.gz的lib包及目录权限等信息;
(3)Run as --> MavenInstall 后会在target下生成.tar.gz的文件.
(4)传到Linux上./start启动服务。
2.1.7热部署 (远程部署)
(1)热部署:先配置好Linux上的tomcat:vim tomcat-users.xml:
<role rolename="manager-gui"/> <role rolename="manager-script"/> <user username="tomcat" password="tomcat" roles="manager-gui,manager-script"/> |
重启tomcat。
(2)在要部署的war项目的pom.xml文件的
<port>8082</port> <path>/</path>下添加配置。 <username>tomcat</username> <password>tomcat</password> <url>http://192.168.26.150:8080/manager/text</url>--->manager/text写死的,前面的是项目要部署的服务器的IP地址和端口号。 |
接下来开始部署 tomcat7:redeploy 使用maven bulid…
(3)查看是否部署上:找到tomcat目录下webapps下ROOT目录下: cd /WEB-INF/lib查看有没有项目的jar包
(4)访问:在浏览器就可以直接访问http://192.168.26.150:8080/
(5)修改映射关系:配置虚拟映射C:\Windows\System32\drivers\etc\hosts192.168.26.150www.ego.com
如果不写端口默认是80端口。修改linux 服务器中tomcat的端口号80.
需要重启tomcat。通过域名访问即可。
在server.xml修改tomcat的端口号。
tomcat的根目录:Catalina
catlina.policy
catalina.properties
context.xml
logging.properties
server.xml
tomcat-users.xml
web.xml
(6)根据情况,可以用Nginx来代理压力大的服务。(负载均衡)
2.2 常用组件端口号
mysql:3306
oracle:1521
SqlServer:1433
redis: 6379 默认有多少个槽slot(16384)
zookeeper:2181 通信协议端口:20880
tomcat:8080
nginx:80
ftp:21
sftp:22
nuxes:8081(本地仓库)
svn: 3690
mycat:8066
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。