【第二章:Java核心技术解析】第14节:Java框架 - Spring必会知识点


大家好,很高兴我们可以继续学习交流Java高频面试题。本小节主要交流学习Spring框架的相关知识点。Spring是目前流行的一站式框架,包括SpringMVC和SpringBoot都给我们搭建Web系统提供了便利。

做为一名Java开发工程师,在日常的工作中,我们必不可少的会使用到Spring框架。有的同学对Spring的使用比较熟练,但是对其技术原理却不甚了解,而这正是面试中所需要具备的能力。接下来,让我们一起来交流学习Spring相关的技术原理吧~

(1)说一下Spring中的控制反转(IOC)吧。

答:IOC也叫控制反转,将对象间的依赖关系交给Spring容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式,实现解耦合。可以通过注解@Autowired和@Resource来注入对象,被注入的对象必须被下边的四个注解之一标注:

  • @Controller
  • @Service
  • @Repository
  • @Component

在Spring配置文件中配置 <context:annotation-config/>元素开启注解。还有一个概念是DI(依赖注入),和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源(对象等)。

解析:

Spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

Spring的核心模块如下所示:

  • Spring Core:是核心类库,提供IOC服务;
  • Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
  • Spring AOP:提供AOP服务;
  • Spring DAO:对JDBC进行了抽象,简化了数据访问异常等处理;
  • Spring ORM:对现有的ORM持久层框架进行了支持;
  • Spring Web:提供了基本的面向Web的综合特性;
  • Spring MVC:提供面向Web应用的Model-View-Controller实现。

Spring的优点有哪些呢?

  • Spring的依赖注入将对象之间的依赖关系交给了框架来处理,减小了各个组件之间的耦合性;
  • AOP面向切面编程,可以将通用的任务抽取出来,复用性更高;
  • Spring对于其余主流框架都提供了很好的支持,代码的侵入性很低。

简单阐述了Spring框架,并且介绍了其关键技术IOC之后,我们一起来看看Spring的另一个关键技术AOP(面向切面编程)吧~


(2)Spring中的AOP面向切面编程有了解吗?

答: AOP,面向切面编程是指当需要在某一个方法之前或者之后做一些额外的操作,比如说日志记录,权限判断,异常统计等,可以利用AOP将功能代码从业务逻辑代码中分离出来。

AOP中有如下的操作术语:

  • Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
  • Aspect(切面):是切入点和通知(引介)的结合
  • Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或属性
  • Target(目标对象):代(dai)理的目标对象(要增强的类)
  • Weaving(织入):是把增强应用到目标的过程,把advice 应用到 target的过程
  • Proxy(代(dai)理):一个类被AOP织入增强后,就产生一个结果代(dai)理类

我们来简单理解下重要概念。切入点就是在类里边可以有很多方法被增强,比如实际操作中,只是增强了个别方法,则定义实际被增强的某个方法为切入点;通知/增强 就是指增强的逻辑,比如扩展日志功能,这个日志功能称为增强;切面就是把增强应用到具体方法上面的过程称为切面。

Spring中的AOP主要有两种实现方式:

  • 使用JDK动态代(dai)理实现,使用java.lang.reflection.Proxy类来处理
  • 使用cglib来实现

两种实现方式的不同之处:

JDK动态代(dai)理,只能对实现了接口的类生成代(dai)理,而不是针对类,该目标类型实现的接口都将被代(dai)理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代(dai)理。实现步骤大概如下:

  • 定义一个实现接口InvocationHandler的类
  • 通过构造函数,注入被代(dai)理类
  • 实现invoke( Object proxy, Method method, Object[ ] args)方法
  • 在主函数中获得被代(dai)理类的类加载器
  • 使用Proxy.newProxyInstance( )产生一个代(dai)理对象
  • 通过代(dai)理对象调用各种方法

cglib主要针对类实现代(dai)理对是否实现接口无要求。原理是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以被代(dai)理的类或方法不可以声明为final类型。实现步骤大概如下:

  • 定义一个实现了MethodInterceptor接口的类
  • 实现其 intercept()方法,在其中调用proxy.invokeSuper( )

接下来,我们看分别给出JDK动态代(dai)理和cglib实现动态代(dai)理的Demo。

JDK动态代(dai)理Demo:

package com.package1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy {

    public static void main(String[] args){
        //定义一个people作为被代(dai)理的实例
        IPeople ple=new People();
        //定义一个handler
        InvocationHandler handle=new MyHandle(ple);

        //获得类加载器
        ClassLoader cl=ple.getClass().getClassLoader();

        //动态产生一个代(dai)理,下边两种方法均可
//        IPeople p=(IPeople) Proxy.newProxyInstance(cl, new Class[]{IPeople.class}, handle);
        IPeople p=(IPeople) Proxy.newProxyInstance(cl, ple.getClass().getInterfaces(), handle);

        //执行被代(dai)理者的方法。
        p.func();
    }



}

class MyHandle implements InvocationHandler{


    //被代(dai)理的实例
    Object obj=null;

    //我要代(dai)理谁
    public MyHandle(Object obj){
        this.obj=obj;

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result=method.invoke(this.obj, args);
        return result;
    }

}

interface IPeople{

    public void fun();

    public void func();
}

//实际被代(dai)理的类
class People implements IPeople{

    @Override
    public void fun() {
        System.out.println("这是fun方法");

    }

    @Override
    public void func() {
        System.out.println("这是func方法");

    }

}

cglib实现动态代(dai)理Demo:

package com.pak;

import net.sf.cglib.proxy.*;

import java.lang.reflect.Method;

public class TestCglib {
    public static void main(String[] args) {

        // 定义一个回调接口的数组
        Callback[] callbacks = new Callback[] {
                new MyApiInterceptor(), new MyApiInterceptorForPlay()
        };

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class); // 设置要代(dai)理的父类
        enhancer.setCallbacks(callbacks); // 设置回调的拦(lan)截器数组
        enhancer.setCallbackFilter(new CallbackFilterImpl()); // 设置回调选择器

        Person person = (Person) enhancer.create(); // 创建代(dai)理对象

        person.eat();
        System.out.println("--------------------");
        person.play();
    }
}

class MyApiInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("吃完饭我会先休息会儿" );  // 方法调用之后也可以进行一些操作
     

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java开发岗高频面试题全解析 文章被收录于专栏

<p> Java开发岗高频面试题全解析,专刊正文共计31节,已经全部更新完毕。专刊分9个模块来对Java岗位面试中的知识点进行解析,包括通用面试技能,Java基础,Java进阶,网络协议,常见框架以及算法,设计模式等。专刊串点成面的解析每个面试题背后的技术原理,由浅入深,循序渐进,力争让大家掌握面试题目的背后的技术原理,摒弃背题模式的陋习。 专刊详细信息,请查阅专刊大纲和开篇词的介绍。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p> <p> <br /> </p>

全部评论
楼主您好,有时间的话可以总结下springboot自动配置的实现原理以及项目的启动流程吗?谢谢
5 回复 分享
发布于 2020-03-06 17:27
楼主您好,有时间的话可以总结下springboot自动配置的实现原理以及项目的启动流程吗?谢谢
4 回复 分享
发布于 2020-03-30 10:30
大家好,***两字触发了牛客的敏感字,所以打不出来,目前使用代(dai)理来显示,后续应该会修复该问题。
4 回复 分享
发布于 2019-12-15 16:21
轻量级和重量级概念的划分      主要看它使用了多少服务,使用的服务越多,容器为普通的java对象做的工作就越多,必然会影响到应用的发布时间,或者运行性能。      对于spring容器,他提供了很多服务,但默认是关闭的,应用需要某种服务,需要指明使用服务,如果服务很少,如只使用了spring核心服务,就认为此时应用属于轻量级的,如果使用了大部分的服务,这时就属于重量级的。      目前EJB容器就重量级。因为他默认为应用提供了Ejb规范中的所有功能,所以属于重量级。 原文链接:https://blog.csdn.net/myk_666888/java/article/details/5740809
1 回复 分享
发布于 2020-06-26 17:42
感谢,在网上东找西找,你这总结的非常好
1 回复 分享
发布于 2020-05-14 01:39
你好,Bean的lazy-init那里是不是说反了?
1 回复 分享
发布于 2020-01-17 03:32
打卡
1 回复 分享
发布于 2020-01-04 21:56
这个Spring MVC示意图是我见过的最好的,非常好理解
点赞 回复 分享
发布于 2021-05-09 19:37
“IOC容器的初始化过程就是对Bean定义资源的定位、载入和注册,此时容器对Bean的依赖注入并没有发生。接下来,我们看下依赖注入的发生时刻吧。”这句话是不是意味着IOC容器的初始化和启动是两个不一样的过程?
点赞 回复 分享
发布于 2021-05-07 22:25
老师您好,“大部分的Spring Bean并没有可变的状态”,啥叫有状态的Bean,啥叫无状态的Bean?
点赞 回复 分享
发布于 2021-03-18 21:09
Spring相关权威数据推荐?
点赞 回复 分享
发布于 2021-03-07 23:55
打卡
点赞 回复 分享
发布于 2021-02-24 20:09
你好,方便总结一下bean加载过程吗
点赞 回复 分享
发布于 2020-09-21 22:00
什么是Spring Boot 无代码生成
点赞 回复 分享
发布于 2020-09-19 19:20
打卡 一刷。
点赞 回复 分享
发布于 2020-09-11 09:19
spring解决循环依赖的方式是采用三级缓存
点赞 回复 分享
发布于 2020-08-24 23:49
二刷打卡
点赞 回复 分享
发布于 2020-08-22 11:13
说实话循环依赖那里很容易误导人,如果不说的详细点建议删除
点赞 回复 分享
发布于 2020-06-24 09:53
你好,请问一下SpringBoot和Kafaka在校招中考察的深度怎么样
点赞 回复 分享
发布于 2020-05-13 09:37
Spring事务传播方式,没用过,根本记不住🤣
点赞 回复 分享
发布于 2020-03-31 22:06

相关推荐

不愿透露姓名的神秘牛友
03-08 19:53
已编辑
AAA不喝拿铁:海投吧,感觉项目写的可以了,能cover住提问就行。我根据真实面经整理得到的最全(高/中/低频)面试题,适合面试前短期突击&长期提高补充,需要的牛u可以关注一手我的专栏,祝好运
点赞 评论 收藏
分享
评论
6
1
分享

创作者周榜

更多
牛客网
牛客企业服务