一、SpringMVC常见问题
1.404错误 是请求的url不存在
地址都是小写
2.500错误 请求的url未绑定参数
解决办法:把方法的参数改为引用类型(包含包装类),就算不传参,也没有异常,使用了默认值null
3.400错误 url中的参数类型和服务器需要的参数类型不同
二、关于MyBatis
持久层框架,利用ORM思想实现数据库持久化操作;半自动化ORM映射框架
ORM:以对象的方式操作数据库
对象映射表
属性映射字段
导入jar包
<!--mybatis依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--jdbc依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
关于Mybatis参数封装说明:
1.mybatis中只支持单值传参
2.单值可以是数字,字符串,对象
3.多值转化为单值,首选Map集合
4.@Param("minAge") int minAge 将参数封装为map
解析:Map = {minAge=100,maxAge=1000}
List<User> findParam(@Param("minAge") int minAge @Param("maxAge") int maxAge);
创建类封装数据--->创建接口--->编辑对应的映射文件Mapper.xml--->编辑核心配置文件mybatis-config.xml--->编辑测试类
demo test:
private SqlSessionFactor sqlSessionFactory;
@Beforeach
//@Beforeach表示当每次执行@Test注解方法时都会优先执行该方法
public void init() throws IOException{
//定义源是核心配置文件全路径
String resource = "mybatis/mybatis-config.xml";
//根据源读取数据,即输入流
InputStream inputStream = Resources.getResourcesAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@test
public void testCar(){
SqlSession sqlSession = sqlSessionFactory.openSession();
CarMapper carMapper = sqlSession.getMapper(CarMapper.class);
List<Car> carList = carMapper.findAll();
System.out.println(carList);
sqlSession.close();
}
三、各特殊sql语句的写法
1) 集合操作
<select id="findIn" resultMap="carRM">
select * from demo_car where id in
(<foreach collection="array" separator="," item="id">
id=#{id} // 取值写法,获取属性值
</foreach>)
<resultMap id="carRM type="com.jt.pojo.Car">
<id column="car_id" property="carIdid"/>
//下列的可以自动映射
<result column="car_name" property="carName"/>
<result column="car_price" property="carPrice"/>
<result column="car_color" property="carColor"/>
</resultMap>
</select>
2)动态sql语句:根据不为null的属性查询
<select id="findSqlWhere" resultMap="carRM">
select * from demo_car
<where>//此标签可以去掉多余的and
<if test="car_id!=null">car_ id=#{car_id}></if>
<if test="car_name!=null">and car_name=#{car_name}></if>
<if test="car_price!=null">and car_price=#{car_price}></if>
<if test="car_color!=null">and car_color=#{car_color}></if>
</where>
<resultMap id="carRM type="com.jt.pojo.Car">
<id column="car_id" property="carIdid"/>
<result column="car_name" property="carName"/>
<result column="car_price" property="carPrice"/>
<result column="car_color" property="carColor"/>
</resultMap>
</select>
3) <update id="findSqlSet">
update demo_user
<set>//此标签可以去掉多余的逗号
<if test="car_name != null">car_name =#{car_name},</if>
<if test="car_price != null">car_price = #{car_price}, </if>
<if test="car_color!= null">car_color =#{car_color}, </if>
</set>
where id = #{id}
</update>
4)分支结构 v-if\v-else-if 判断为true,则显示标签
<select id="findSqlChoose" resultMap="carRM">
select * from demo_car
<where>
<choose>
<when test="car_name!=null">car_name=#{car_name}</when>
<when test="car_price!=null">car_price=#{car_price}</when>
<oterwise>car_color=#{car_color}</oterwise>
</choose>
</when>
<resultMap id="carRM type="com.jt.pojo.Car">
<id column="car_id" property="carIdid"/>
<result column="car_name" property="carName"/>
<result column="car_price" property="carPrice"/>
<result column="car_color" property="carColor"/>
</resultMap>
</select>
四、注解总结
@RestController(springmvc)=@Controller(spring)+@ResponseBody(springmvc)
@RequestMapping("接收请求的方法名")
@Component注解
表示将User对象交给Spring容器管理
@Configuration 写在类上
表示该类是一个配置类,可以在此类中编辑自定义对象
@Bean 写在类里方法外 map<方法名,返回值对象>
是Spring专门为管理自定义对象的注解 在配置类文件中使用
@SpringBootTest 测试类上
在该类中执行@Test测试方法,就会启动spring容器
@Test 测试方法外
测试方法不能有返回值,不能有参数
@Param(key1) 类型 value1, @Param(key2) 类型 value2
@MapperScan是Spring管理Mybatis接口的注解,不是管理框架本身.
@Service注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService"
@Autowired注入依赖
@RequestBody 响应体 是用户传入的json串格式,需要转换为对象
页面参数说明:
用户在js中写的是js对象,经过浏览器解析发起Ajax请求,js对象被解析为json串传给后端服务器的控制层
交互的媒介: HTTP协议 协议中要求传递字符串
打印日志:logging:level:com.jt:debug
前端传递的是JSON,后端不可以直接使用User对象接收.
解决方案:
* 1.对象可以转化为JSON串 @ResponseBody
* 2.JSON串转化为对象 @RequestBody
@Transactional类上 默认只处理运行时异常
readOnly=true 只读,用户不能修改,比如公告、合同
@TableField是用来标识属性与字段的映射关系,是MP中的核心注解
五、关于跨域
三要素:协议、域名、端口号 三者一致时即是同域
若有一个不一致则是跨域 需要在控制层的controller类上添加注解@CrossOrigin
ajax请求地址:"http://localhost:8080/findAll"
浏览器地址:http://127.0.0.1:8848/vuejs/1.入门案例/3-Axios入门案例.html
六、小知识点
双向数据绑定
规则: 看到input文本输入框,首先想到使用双向数据绑定.
用户输入框 双向数据绑定: v-model=""
指定公共前缀:
axios.defaults.baseURL = "http://localhost:8080"
async addUserBtn(){
let {data: result,status: status} = await axios.get("/axios/findAll")
this.userList = result
}
要求用户访问页面的时候, 初始化的时候 就要求访问后台服务器.,发起Ajax请求. 实现用户列表页面的渲染.
解决方案: 使用mounted 函数
关于增删改查注意的点:
@ResponseBody 将List集合转化为json
参数接收:单个参数就只传参数类型 参数名
如果是多个参数,并且参数名称与属性名称一致,就可以用对象来接
@RequestMapping("findUserByAS")
public List<User> findUserByAS(User user){
return userService.findUserByAS(user);
}
RestFul风格的参数传递:地址里面直接是值
http://localhost:8090/updateById/1/黑熊精/3000/男
参数接收:1.单个参数使用@PathVariable Integer id接收;
2.如果是多个参数就使用对象
@PutMapping("/updateById/{id}/{name}/{age}/{sex}")
public String updateById(User user){
userService.updateById(user);//无返回值
return "修改成功"
}
@Update("update demo_user set name=#{name},age=#{age},sex=#{sex} where id=#{id}
void updateById(User user);
七、关于秘钥说明
当用户登录之后,可以跳转到系统的首页. 到了系统首页之后,用户可以进行其它的业务操作. 系统如何判断正在操作业务的用户 已经登录?
业务说明: 一般在登录认证系统中,都会返回秘钥信息.来作为用户登录的凭证.
秘钥特点: 最好独一无二.
动态生成秘钥: UUID
用户登录操作
public class UserController{
@Autowired
private UserService userService;
@postMapping("login")
public SysResult login(@RequestBody User user){
String token = userService.login(user);
if(token == null){
return SysResult.fail();
}
return SysResult.success(token);
}
}
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper usermapper;
@Override
public String login(User user){
//1.先将密码进行加密处理
//获取密码值
String password = user.getPassword();
//利用nd5加密算法进行密码加密
String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
user.setPassword(md5Pass);
//2.查询数据
User userDB = userMapper.findAllByUp(user);
if ( userDB == null){
return null;
}
String uuid = UUID.randomUUID().toString().replace("-","");
return uuid;
}
}
public interface userMapper{
@Select("select * from user where username=#{username} and password=#{password}")
User findAllByUp(User user);
}
八、关于Session和Cookie说明
1 业务需求说明
用户的请求是一次请求,一次响应. 当响应结束时,服务器返回的数据 也会销毁.
问题: 如果销毁了token 则认为用户没有登录.需要重复登录. 如何解决该问题: 应该持久化token信息.
2 Session
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。
Session对象存储特定用户会话所需的属性及配置信息。
这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。
Session 对象最常见的一个用法就是存储用户的首选项。
例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。
有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持cookie的浏览器中保留。
特点: Session总结
Session 称之为 会话控制 技术
Session生命周期, 会话结束 对象销毁.
Session的数据存储在内存中.
Session只可以临时存储数据.不能永久存储.
3.Cookie总结
Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,
进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。
特点: 1. 类型: 小型文本文件.
2. 文件通常是加密的.
3. cookie 可以临时或者永久存储.
九、前端相关说明
node.js 作用Node.js 是一个基于 Chrome V8 引擎 的 JavaScript 运行时环境。
VUE脚手架 是前端开发模拟后端设计的一种开发框架体系 xxx.vue
VUE.js 是将javascript封装的高级渐进式的JS的框架
node.js:相当于js的运行环境
vue脚手架xxx.vue:js的开发框架体系
vue.js:运行js脚本的框架
ElementUI组件声明步骤:
1.导入需要的组件:import{ Button } from 'element-ui'
2.对外声明组件:Vue.use(Button)
模拟浏览器
public class Browser{
public static void main(String[ ] args) throws IOException{
Socket socket = new Socket("127.0.0.1",9000);
InputStream in = socket.getInputStream();
byte[ ] buf = new byte[1024];
int len = in.read(buf);
System.out.println("server say:"+new String(buf,0,len));
socket.close();
}
}
路由导航守卫:拦截跳转路径
编辑index.js文件:
router.beforeEach((to,from,next)=>{
if(to.path==='/login') return next()
let token = window.sessionStorage.getItem('token')
if(token===null || token===" "){//校验是否有token
return next("/login")
}
next()
})
ajax:局部刷新,异步访问 ajax引擎
定义嵌套路由对象:
const router = new VueRouter({
routers: [
{path:'/shopping',component:shopping,
children:[
{path:'/shopping/shoe',component:shoe},
{path:'/shopping/phone',component:phone}
]}
]
)}
利用钩子函数实现数据查询:
在页面刷新的时候就执行此函数
mounted(){
this.getUserList()
}
定义初始化函数:
created(){ //页面加载时默认获取商品分类列表数据 this.findItemCatList() }
作用域插槽:scope.row.status 动态获取当前行对象
<!-- 图片上传的JS
1. action: 代表图片上传的地址url
2. file-list: 图片列表数据的集合[{name:"xx",url:"xxx"},{}]
3. 钩子函数: 满足某些条件时触发.
4. on-preview 当点击图片时触发
5. on-remove 当移除列表中的图片时触发
5.on-success 图片上传成功时触发
6.multiple 可以支持多张图片上传
7.drag 是否允许拖拽
-->
十、增删改查
1.查询
编辑UserMapper.xml映射文件:
根据ID查询用户数据
<select id="findUserById" parameterType="Integer" resultType="User">
select * from demo_user(id,name,age,sex) where id=#{id}
</select>
@Test
public void testUserById(){
int id = 1;
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(id);//返回值为User对象
System.out.println(user);
sqlSession.close();
}
User findUserById(Integer id);
2.新增操作
需要传参且参数为整个对象,同时不需要返回值,只是自动的返回影响的行数rows
<insert id = "saveUser">//无resultType
insert into demo_user(id,name,age,sex) value(null,#{name},#{age},#{sex})
</insert>
Integer saveUser(User user);
User user = new User(null,"xxx","xxx","xxx");//封装对象
int rows = userMapper.saveUser(user);
3.修改操作
不会改变id的值,可作为条件,也不需要返回值,传入的参数可以用对象接
<update id = "updateUser">
update demo_user set name=#{name},age=#{age} where id=#{id}
</update>
将需要的值在测试类封装对象时赋值
4.删除操作
一般来说只需要一个name参数
String name = "xxx";
int rows = UserMapper.deleteUserByName(name);
int deleteUserByName(String name);
<delete id = "deleteUserByName">
delete from demo_user where name=#{name}
</delete>
5.模糊查询
模糊查询的结果为集合,参数是条件属性
List<User> findUserByLike(String name);
<select id = findUserByLike resultType="User">
select * from demo_user where name like "%"#{name}"%"
</select>
简化Sql语句:
<select id = "findUser" resultType="User">
select <include refid="user_cloumn"/> from demo_user
</select>
#将字段提取,避免大量字段的书写
<sql id = "user_cloumn">
id,name,age,sex
</sql>
十一、关联查询
mybatis遇到集合参数操作时,需要将集合进行遍历:
foreach标签表示循环遍历集合
collection属性表示集合的类型:array数组int[ ]、List集合、Map集合
item属性表示循环遍历的数据的变量
1. array集合操作
<select id = "findIn" resultType="User">
select * from demo_user where id in (
<foreach collection="array" separator="," item="id">
#{id}
</foreach>)
</select>
2.List集合操作
数组转化时,需要使用包装类型.
基本类型没有get/set方法 包装类型是对象 对象中有方法
//将数组转化为list集合
Integer[] ids = {1,2,4,5,7};
List list = Arrays.asList(ids);
List<User> userList = userMapper.findInList(list);
3. Map集合操作
<select id="findInMap" resultType="User">
select * from demo_user where id in (
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
and sex = #{sex}
</select>
多值封装为Map集合
List<User> findInMap(@param("ids") int[ ] ids, @param("sex") String sex);
Sql:
select * from demo_user where id in (1,3....)
and sex = "男";
关联查询
一对一封装:
<resultMap="empRM" type="Emp">
<id property="empId" column="emp_id"/>//主字段
<result property="empName" column-"emp_name"/>//其他字段
<association property="dept" javaType="Dept">
<id property="deptId" column="dept_id"/>
<result property="deptIName" column="dept_name"/>
</association>
</resultMap>
一对多:
private List<Emp> emps;
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
</collection>
<!--在配置文件中配置settings信息-->
<settings>
<!--开启驼峰映射规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
映射文件需要配置的属性是autoMapping="true",主键还是得标识
缓存
一级缓存SqlSession默认开启
二级缓存SqlSessionFactory,创建不同的SqlSession,需在映射文件中添加缓存的标签<cache/>
如果需要使用一级/二级缓存,则POJO对象必须实现序列化接口implements Serializable. 否则数据不可以被缓存.
十二、Spring控制事务策略
1. Spring中的事务注解@Transactional 默认条件下只处理运行时异常.如果遇到检查异常(编译异 常)事务控制没有效果.
2. 注解的属性控制
rollbackFor = IOException.class , 遇到某种异常实现事务的回滚
noRollbackFor = 遇到某种异常事务不回滚.
readOnly = true 该操作是只读的,不能修改. 公告/合同等
全局异常处理机制:AOP
捕获异常是为了按照特定的要求 返回用户可以识别的有效数据.
@RestControllerAdvice//异常拦截器,用于异常统一处理
public class SystemException{//运行时异常
@ExceptionHandler(value = RuntimeException)//异常类名
public SystemResult fail(Exception e){
e.printStackTrace();
return SysResult.fail();
}
}
十三、Mybatis-Plus
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
MP工作原理
实质: MP动态生成Sql语句.
铺垫: insert into 表名(字段名…) value (属性值…)
工作原理:
1. 用户执行userMapper.insert(user); 操作
2. 根据UserMapper的接口找到父级BaseMapper.根据BaseMapper的接口查找泛型对象User.
3. 根据User.class 获取注解@TableName(“demo_user”),获取表名 ,@TableId(type = IdType.AUTO)//主键自增
4. 根据User.class 获取所有的属性,根据属性获取指定的注解@TableField(value =“name”),获取字段名称
5. 根据属性获取属性的值.之后动态拼接成Sql语句
6. 将生成的Sql交给Mybatis执行入库操作.
MP使用特点: 根据其中不为null的属性进行业务操作!!!
Spring与MP的整合
配置文件:
管理数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver //驱动
url: jdbc:mysql://127.0.0.1:3306/jt/serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: root
业务代码
1.分页查询业务层代码:
@Service
public class ItemServiceImpl implements ItemService{
@Autowired
private ItemMapper itemMapper;
/**
* 要求: 3+2(总记录数,分页结果)
* 关于selectPage(参数说明)
* 参数1: page MP提供的分页对象
* 参数2: 条件构造器
* @param pageResult
* @return
*/
@Override
public PageResult getItemList(PageResult pageResult) {
//1.根据已知参数先封装构建分页对象 参数1: 第几页 参数2: 多少条
Page<Item> page = new Page<>
(pageResult.getPageNum(),pageResult.getPageSize());
//2.准备条件构造器 构建模糊查询,获取queryWrapper值
QueryWrapper queryWrapper = new QueryWrapper();
String query = pageResult.getQuery();//已知
boolean flag = StringUtils.hasLength(query);
queryWrapper.like(flag,"title",query); //query搜索关键字title like %query%
//3.根据1、2条MP查询 实现分页数据的自动封装
page = itemMapper.selectPage(page,queryWrapper);
//4.获取数据,返回分页对象
long total = page.getTotal();
//获取分页结果
List<Item> rows = page.getRecords();
return pageResult.setTotal(total).setRows(rows);
}
}
2.MP框架提供:分页配置类
@Configuration //这是配置类
public class MybatisConfig {
//需要通过配置文件 指定数据库类型.
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));
return interceptor;
}
}
3.使用ItemVO对象接收
/**主键自动回显原理:
* 问题: id是主键自增. 入库之后才有主键所以
* 应该让主键动态回显
* 1.Mybatis 动态实现回显
* <insert id="xxxx" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
* insertinto xxxx
* </insert>
* 2.MP是mybatis的增强版本.所以可以实现自动的主键回显!!!
* @param itemVO
*/
@Override
@Transactional
public void saveItem(ItemVO itemVO) {
Item item = itemVO.getItem();
//设定状态
item.setStatus(true);
//获取商品基本信息
itemMapper.insert(item);
//获取商品详情
ItemDesc itemDesc = itemVO.getItemDesc();
itemDesc.setId(item.getId());
itemDescMapper.insert(itemDesc);
}
4.图片上传及回显
参数:virtualPath图片的虚拟路径、urlPath图片服务路径、fileNam文件名称(UUID.type)
/**
* 业务说明: 实现图片上传
* URL: http://localhost:8091/file/upload
* 类型: post
* 参数: file 字节信息
* 返回值: SysResult.success()
* 扩展:
* 一般情况下:
* 一般前端向后端服务器发送字节信息.由外到内实现数据传输.
* 采用输入流信息. InputStream file
* 使用字节流的弊端: 1.必须手动关闭, 2.代码操作繁琐
* 底层代码的实现.
* SpringMVC高级API MultipartFile 专门处理IO流操作
* 文件上传步骤:
* 1.获取文件名称
* 2.准备文件上传的目录
* 3.判断目录是否存在 存在目录: 实现上传 没有目录:创建目录
* 4.利用工具API方法,实现文件上传.
* 注意事项: MultipartFile 默认支持1M的数据
*/
@PostMapping("/upload")
public SysResult upload(MultipartFile file) throws IOException {
//1.获取文件名称
String fileName = file.getOriginalFilename();
//2.准备磁盘地址
String dirPath = "E:/project3/images/";
//3.将这个文件目录 封装为File对象
File dirFile = new File(dirPath);
//4.判断对象是否存在
if(!dirFile.exists()){
//如果文件目录不存在,则创建目录
dirFile.mkdirs(); //表示多级目录上传.
}
//5.封装文件全路径 E:xxx/xxx/a.jpg
String path = dirPath + fileName;
File allFile = new File(path);
//6.实现文件上传 将IO流按照指定的对象格式进行输出.
file.transferTo(allFile);
return SysResult.success();
}
/**正则表达式:[a-zA-Z0-9-_]
* 业务说明: 实现图片上传
* URL: http://localhost:8091/file/upload
* 类型: post
* 参数: MultipartFile file 字节信息
* 返回值: SysResult.success()
* 问题思考:
* 1.完成图片类型校验 jpg|png|gif....
* 2.防止恶意程序 a.exe.jpg
* 3.将图片分目录存储
* 3.1.按照类型分 理论可以但是得多分配几个
* 3.2.按照时间划分. yyyy/MM/dd
* 4.自定义文件名称. 利用UUID充当图片名称.
*/
fileName.toLowerCase();//转化为小写字母
//1.3正则校验是否为图片类型
if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
//图片类型 不匹配 程序应该终止
return null;
}
//校验是否为恶意程序 怎么判断就是一张图 高度和宽度
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
int height = bufferedImage.getHeight();
int width = bufferedImage.getWidth();
if(height == 0 || width == 0){
return null;
}
//3.将图片分目录存储 yyyy/MM/dd
String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
.format(new Date());
String dateDirPath = localDirPath + dateDir;
File dirFile = new File(dateDirPath);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//4.防止文件重名 动态生成UUID.类型
//4.1 动态生成UUID
String uuid = UUID.randomUUID().toString()
.replace("-","");
//4.2 获取图片类型 abc.jpg .jpg
String fileType = fileName.substring(fileName.lastIndexOf("."));
//全新的文件名称
String newFileName = uuid + fileType;
属性动态赋值,方便后期维护
@Service
@PropertySource("classpath:/image.properties")//配置文件的路径
public class FileServiceImpl implements FileService{
@value("${image.localDirPath}")
private String localDirPath;
@value("${image.preUrl}")
private String preUrl;
}
5.负载均衡配置
1.轮询策略: 根据配置文件中的服务器的顺序,依次访问服务器
#定义tomcat服务器集群
upstream tomcats {
server localhost:8091;
server localhost:8092;
server localhost:8093;
}
#配置后端服务器代理 manage.jt.com localhost:8091
server {
listen 80;
server_name manage.jt.com;
location / {
#代理请求协议
#proxy_pass http://localhost:8091;
proxy_pass http://tomcats;
}
}
2.权重策略:根据服务器的性能,灵活的进行访问的配比
upstream tomcats {
server localhost:8091 weight=6;
server localhost:8092 weight=3;
server localhost:8093 weight=1;
}
3.IPHASH策略
将用户数据存储到后端服务器的session中,并且保证每次用户访问都访问到同一个服务器
upstream tomcats {
ip_hash;
server localhost:8091 weight=6;
server localhost:8092 weight=3;
server localhost:8093 weight=1;
}
前端访问后端,是通过ajax的方式动态访问. 需要将Ajax请求改为后端域名访问.
dist就是编译之后的前端项目.
#配置前端服务器代理 www.jt.com dist/index.html
server {
listen 80;
server_name www.jt.com;
location / {
root dist;
index index.html;
}
}
项目说明:
1. 前端项目 是静态资源文件 部署在nginx内部
2. 后端项目 多个tomcat服务器 8091/8092/8093
3. 用户通过www.jt.com 反向代理到前端服务
4. 前端获取数据通过manage.jt.com 访问后端服务器集群. 通过nginx实现负载均衡,实现图片的回显
6.AOP
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率。
Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.
/**
* 知识回顾: AOP利用动态代理扩展目标方法.
* 公式: 切面 = 切入点表达式 + 通知方法
* 切入点表达式: 如果目标对象满足切入点表达式的判断(if),则spring自动为其创建代理对象
* 通知方法: 对目标方法进行扩展的封装方法.
* 目标对象的bean的ID: userServiceImpl
* 切入点表达式:
* 1. bean("bean的ID")
* AOP规则: 如果目标对象满足切入点表达式,则执行通知方法
*/
当我们在项目中定义了AOP切面以后,系统启动时,会对有@Aspect注解描述的类进行加载分析,
基于切入点的描述为目标类型对象,创建代理对象,并在代理对象内部创建一个执行链,
这个执行链中包含拦截器(封装了切入点信息),通知(Around,…),目标对象等,
我们请求目标对象资源时,会直接按执行链的顺序对资源进行调用。
7.ajax
ajax请求的入口对象是xmlhttprequest对象
网关服务器在哪里做了负载均衡:lb://
采用AOP的设计获取用户行为日志
反射技术的应用:bean对象的创建、依赖注入、方法对象的获取、方法上注解的获取等等
@PreAuthorize访问此方法需要授权