开发框架:Spring/SpringBoot+SpringMVC+MyBatis

【Spring+SpringBoot】

【问】Spring?Spring Boot和Spring的区别?

Spring:是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。

Spring Boot:快速开发的脚手架,在Spring的基础上进一步简化配置和开发。

【问】Spring Boot、Spring Cloud、Spring Cloud Data Flow?

【问】Spring Boot和Spring和Spring MVC的区别?

Spring Boot是一个在Spring的基础上搭建的一个全新微框架,目的是简化Spring的搭建和开发过程,是原有Spring的一个扩展。

Spring MVC是Spring整合的框架之一,而Spring相当于中央引擎。

【问】Spring Boot如何在Spring基础上进一步进行简化的?

(1)起步依赖:

spring-boot-starter-包名,一个包顶原有的spring很多个包,简化了包的收集和导入。

(2)自动配置:

启动注解@SpringBootApplication点进去,有个自动配置注解@EnableAutoConfiguration,再点进去有个@Import注解。主启动类被这个注解修饰,那么SpringApplication.run(…)的内部就会执行selectImports()方法,寻找 META-INF/spring.factories文件,里面写有所有配置类的路径,根据路径将自动配置类加载到Spring IOC容器中。

   

(3)配置文件更加简约:

a、application.properties:

按照约定的写法在全局配置文件application.properties中配置参数或开关。(全局配置文件application.properties@ConfigurationProperties修饰的类绑定。)

b、yml映射文件:

更方便,修改SpringBoot默认参数,如端口号。

(4)简化配置:

JavaConfig  自定义配置类,纯注解,无需xml文件。

【问】快速搭建一个SpringBoot程序?

最方便的是用IDEA创建:

1.创建一个新项目,选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现。

2.选择初始化的组件,初学者选择WEB即可

3.选择项目路径,创建一个空文件夹springboot-01-helloworld,选择这个文件夹。

4.运行启动类(@SpringBootApplication注解下的类)

 

【问】IOC?

一、先解释IOC理念:

IOC就是控制反转的意思,是一种设计思想,意思是把创建对象的控制权交给外部环境(IOC容器/spring框架),达到解耦的目的。

比如,Service层A接口有个A类,Dao层B接口有个B1类,A类想使用B1类的功能。

(1)那么在不使用IOC编程的情况下:

必须由程序员在A类内部主动new一个B1类的对象来使用,当业务有了新的逻辑,Dao层的B1类需要更换为B2类,那么还需要在A类当中重新new一个B2类对象。在这种情况下,Dao层代码变更影响了Service层的代码,这个就叫做程序间的耦合。

(2)有了IoC容器后:

不需要new,只需要A类中声明一个B接口类型的对象,通过IOC容器注入的方式创建B1对象(比如用@Repository注解修饰B1类+用@autoWired注解修饰A类)。新业务逻辑产生后,只需要把新B2对象注入即可(比如用@Repository+@primary注解修饰B2类)。此时Dao层代码变动没有改变Service层的代码,所以达到了解耦的目的。

二、再讲Spring中IOC的实现:

依赖注入:先装配Bean到IOC容器中,然后从IOC容器中获取Bean。有以下的组合搭配:

SpringBoot使用:注解声明+配置类配置+注解注入/上下文获取(Spring Boot当中就是这种,不用xml)

(1)注解声明Bean

@Component

(2)配置类

@Configuration注解+@Bean注解+配置类,替代xml文件。

每写一个@Bean,就相当于之前我们写的一个<bean>标签;方法名相当于id属性;返回值相当于class属性。

(3)Bean的获取

a、使用AnnotationConfigApplicationContext,传入配置类的class对象获取上下文,然后getBean(方法名)。

b、或者@AutoWired自动注入

三、Spring IOC的底层实现机制:

Spring 中的 IoC 的实现原理就是工厂模式加反射机制。(通过class对象(反射对象)来获取Bean)

【问】Spring三种注入方式:

  1. 构造器注入;
  2. set方式注入;
  3. 注解方式注入。

【问】AOP

一、什么是AOP?

                                

当我们要写一个类时,这个类中很多方法的相同位置出现了重复的代码,比如“打印日志、事务控制、权限验证”等等,这部分重复的代码,统称为 “横切冗余代码”,横切冗余代码的存在,造成了程序代码臃肿,不变维护。

AOP叫做面向切面编程,就是解决项目中切面代码冗余的。AOP编程就将切面逻辑代码和业务逻辑代码分离(比如SpringBoot使用@Aspect声明切面类,并用@Before、@After注解声明要注入哪些类的哪些方法,一个切面类织入多个目标类的方式),这样就降低横切逻辑代码的重复编写、也易于维护。

二、SpringBoot实现AOP?

SpringBoot使用注解实现AOP:

(1)目标类:

(2)编写一个注解实现的切面类,注解@Aspect声明切面,然后用@Before、@After等注解注入到类的所有方法前。

(3)注册切面类的bean,并增加注解支持(或者用配置类也可以)

三、Spring实现AOP的底层原理---动态代理?

Spring AOP是基于动态代理的原理来实现的。

默认使用jdk动态代理织入增强,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

(1)代理模式:

目标类(业务),代理类(业务+切面)

(静态代理的时候,代理类是写好的,不是动态生成的。)

(2)动态代理:动态的方式生成代理类

相对于静态来说,动态代理的代理类是动态生成的,不是直接写好的。动态代理类可以生成一类业务相同的代理实例,不用再去一个个的手写。

a、JDK动态代理(基于接口)(基于反射实现):

步骤

1.创建一个实现接口InvocationHandler的类,这个类时用于生成代理类的类。在这个类中private target;   setTarget(); 用于传入目标类。

2.它必须重写invoke()方法,在invoke方法中插入横切逻辑代码,再运用反射method.invoke一下。

3.用Proxy类的静态方法newProxyInstance()获取代理类,写在getProxy()方法里

4.最后使用的时候,用这个类的对象,setTarget()方法传入目标类,通过getProxy()动态获取代理对象。当代理对象调用目标对象方法比如rent()时,会自动跳转到的关联的invoke方法进行调用。从而动态地将横切代码和业务代码编织在一起。

代理角色类:

//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Object rent;

    public void setRent(Host rent){
        this.rent=rent;
    }

    //代理类
    public Object getProxy(){
        //1.代理类加载器 2.被代理类的接口 3.代理类
        //生成代理类实例
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    @Override
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        Object result=method.invoke(rent,args);
        return result;
    }

    public void seeHouse(){
        System.out.println("代理类带你看房子!");
    }
}

调用:

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host=new Host();
        //代理角色:现在没有
        ProxyInvocationHandler pih=new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象!
        pih.setRent(host);
        Rent proxy= (Rent) pih.getProxy();
        proxy.rent();
    }
}

b、cglib动态代理(基于类,继承)。

原理:继承;

适用于:没有实现接口的类;

不适用于:final修饰的类。

【问】Bean的作用域?单例模式?多例模式?

单例模式:从bean的设定角度是singleton,只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。

【问】Bean的生命周期?

  1. 实例化bean对象,并设置属性,检查Aware接口设置依赖
  2. 前置处理,检查方法,后置处理,注册回调接口
  3. 使用中
  4. 最后销毁

 

【问】什么是循环依赖?Spring如何解决循环依赖?

(1)

假设有两个Bean,当程序调用Bean A时,Bean A中依赖Bean B,在Bean A中调用Bean B时,Bean B中又依赖了Bean A,这样就形成了循环依赖。

(2)

三级缓存解决了Bean之间的循环依赖。

实例化对象时,Spring一级一级向下寻找,找出了前面提到的三级缓存,也就是三个Map集合类:

  1. singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;
  2. earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;
  3. singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。

找到后,一个Bean创建成功,另一个也就成功了。

ps:基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。

 

【问】主启动类?为什么会开启一个服务器?

主启动注解@SpringBootApplication下面的一个主启动类:SpringbootApplication:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

【问】JSR303校验?

Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

我们这里来写个注解让我们的name只能支持Email格式;

使用数据校验,可以保证数据的正确性;

【问】SpringSecurity?Shiro?

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。

安全性的东西。市面上存在比较有名的:Shiro,Spring Security !

 

 

【Spring MVC】

【问】SpringBoot如何开发SpringMVC?

@ReponseBody注解:

在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,它的效果等同于通过response对象输出指定格式的数据。

不用的话只会返回ModelAndView对象,前台解析不到。

@Controller
@RequestMapping("/alpha")
public class AlphaController {

    @Autowired
    private AlphaService alphaService;

    //(1)POST请求提交表单,需要写一个提交表单的静态页面student.html,放在static/html目录下
    @RequestMapping(path = "/student", method = RequestMethod.POST)
    @ResponseBody
    public String saveStudent(String name, int age) {
        System.out.println(name);
        System.out.println(age);
        return "success";
    }

    // (2)响应HTML数据,/teacher是真实URL,/demo/view是thymeleaf的位置
    @RequestMapping(path = "/teacher", method = RequestMethod.GET)
    public ModelAndView getTeacher() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("name", "张三");
        mav.addObject("age", 30);
        mav.setViewName("/demo/view");
        return mav;
    }

    //(3)响应JSON数据(异步请求:当前网页不刷新,但悄悄的访问了服务器)
    // Java对象 -> JSON字符串 -> JS对象
    @RequestMapping(path = "/emp", method = RequestMethod.GET)
    @ResponseBody
    public Map<String, Object> getEmp() {
        Map<String, Object> emp = new HashMap<>();
        emp.put("name", "张三");
        emp.put("age", 23);
        emp.put("salary", 8000.00);
        return emp;
    }
}

 

【问】Spring MVC的执行原理流程?

自己写一个controller就执行了,绿色部分都是spring MVC框架帮我们做的。

1、SpringMVC有个中央处理器DispatcherServlet

2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

3、 DispatcherServlet调用HandlerAdapter处理器适配器。HandlerAdapter经过适配调用具体的处理器(Controller)。Controller执行完成返回ModelAndView。HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

4、DispatcherServlet将ModelAndView传给ViewReslover视图解析器。ViewReslover解析后返回具体View。DispatcherServlet根据View进行渲染视图,最终实现响应用户。

 

【问】@RequestMapping注解?

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

(1)只注解方法

(2)同时注解类和方法

【问】RestFul风格?

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

【问】拦截器?登录判断验证?

 

 

【MyBatis】

【问】SpringBoot如何使用MyBatis?

在spring boot中,一般不用@Repository,通常用@Mapper。

(1)导入jar包

(2)application.properties完成配置:端口号、用户名、密码、连接池等。

(3)编写实体类(一个表对应一个实体类)(private+get、set、构造等)

public class User {

    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;

    //外加get、set、toString等
}

(4)编写UserMapper接口:

@Mapper
public interface UserMapper {

    User selectById(int id);

    User selectByName(String username);

    User selectByEmail(String email);

    int insertUser(User user);

    int updateStatus(int id, int status);

    int updateHeader(int id, String headerUrl);

    int updatePassword(int id, String password);

}

(5)配置映射文件:user-mapper.xml,搭配Mapper接口自动生成一个实现类。

namespace对应接口名,id对应接口内方法名。

<mapper namespace="com.nowcoder.community.dao.UserMapper">

    <sql id="insertFields">
        username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>

    <sql id="selectFields">
        id, username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>

    <select id="selectById" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where id = #{id}
    </select>

    <select id="selectByName" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where username = #{username}
    </select>

    <select id="selectByEmail" resultType="User">
        select <include refid="selectFields"></include>
        from user
        where email = #{email}
    </select>

    <insert id="insertUser" parameterType="User" keyProperty="id">
        insert into user (<include refid="insertFields"></include>)
        values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
    </insert>

    <update id="updateStatus">
        update user set status = #{status} where id = #{id}
    </update>

    <update id="updateHeader">
        update user set header_url = #{headerUrl} where id = #{id}
    </update>

    <update id="updatePassword">
        update user set password = #{password} where id = #{id}
    </update>

</mapper>

(6)测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MapperTests {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DiscussPostMapper discussPostMapper;

    @Test
    public void testSelectUser() {
        User user = userMapper.selectById(101);
        System.out.println(user);

        user = userMapper.selectByName("liubei");
        System.out.println(user);

        user = userMapper.selectByEmail("nowcoder101@sina.com");
        System.out.println(user);
    }

    @Test
    public void testInsertUser() {
        User user = new User();
        user.setUsername("test");
        user.setPassword("123456");
        user.setSalt("abc");
        user.setEmail("test@qq.com");
        user.setHeaderUrl("http://www.nowcoder.com/101.png");
        user.setCreateTime(new Date());

        int rows = userMapper.insertUser(user);
        System.out.println(rows);
        System.out.println(user.getId());
    }

    @Test
    public void updateUser() {
        int rows = userMapper.updateStatus(150, 1);
        System.out.println(rows);

        rows = userMapper.updateHeader(150, "http://www.nowcoder.com/102.png");
        System.out.println(rows);

        rows = userMapper.updatePassword(150, "hello");
        System.out.println(rows);
    }

    @Test
    public void testSelectPosts() {
        List<DiscussPost> list = discussPostMapper.selectDiscussPosts(149, 0, 10);
        for(DiscussPost post : list) {
            System.out.println(post);
        }

        int rows = discussPostMapper.selectDiscussPostRows(149);
        System.out.println(rows);
    }

}

【问】动态SQL?

接口方法里传入一个参数blog,然后使用blog的属性来在mapper.xml中生成动态SQL。

 

【问】怎么用MyBatis操作事务?

Spring操作事务有如下两种方法:

我在项目中使用的是声明式事务管理(底层是aop实现的)。

声明式事务有两种(我在项目中用的是基于注解的形式):

(1)基于xml文件:事务标签

(2)基于注解:@Transactional,并设置“隔离级别+事务的传播机制(事务1内调用了事务2时)”

以下示例为Service层调用dao层的mapper,并使用事务:

    // REQUIRED: 支持当前事务(外部事务),如果不存在则创建新事务.
    // REQUIRES_NEW: 创建一个新事务,并且暂停当前事务(外部事务).
    // NESTED: 如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会和REQUIRED一样.
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public Object save1() {
        // 新增用户
        User user = new User();
        user.setUsername("alpha");
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
        user.setEmail("alpha@qq.com");
        user.setHeaderUrl("http://image.nowcoder.com/head/99t.png");
        user.setCreateTime(new Date());
        userMapper.insertUser(user);

        // 新增帖子
        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle("Hello");
        post.setContent("新人报道!");
        post.setCreateTime(new Date());
        discussPostMapper.insertDiscussPost(post);

        Integer.valueOf("abc");

        return "ok";
    }

 

【问】MyBatis有几种分页方式?

一、数组分页

不改变mapper.xml里的sql语句,而是在service层获取数据并且进行分页实现

二、RowBounds实现分页

RowBounds分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。

存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。

适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。

举例: select _ from student ,拦截 sql 后重写为: select t._ from (select \*from student)t limit 0,10

三、拦截器分页

四、借助Sql语句进行分页

 

【问】MyBatis的缓存?MyBatis缓存原理?自定义缓存Ehcache?

(1)

一级缓存:

默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!一级缓存就是一个Map。

二级缓存:

也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。只要开启了二级缓存,在同一个Mapper下就有效。

所有的数据都会先放在一级缓存中,只有当会话提交,或者关闭的时候,才会提交到二级缓冲中!

(2)

(3)

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存。

要在程序中使用ehcache,先要导包!

 

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值