随堂笔记第三阶段

一、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访问此方法需要授权

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值