java基础之----spring
概述
为了给企业提供一个完善的解决方案,spring已经做成了一个全家桶,去spring官网看一下,发现有springboot、springcloud、springsecurity、springframework...,spring包含的东西太多,但是我们常说的spring是什么呢?就是springframework,本文主要讨论springframework中一些关键的点。
bean
bean的定义
bean是spring的核心,我们常见的IOC(控制反转),DI(依赖注入)都是依赖于bean的,那bean到底是什么?bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象,这些 bean 是由用容器提供的配置元数据创建的。
如何创建一个bean
- 通过xml配置生成(这种方式比较古老,现在基本一般不会使用这种方式)
- 通过注解生成bean,比如通过@Controller,@Service,@Component
- 通过java类的配置,直接使用@Bean,这个注解不能用在类上,只能用在方法上
前两种方式好理解,第三种方式用的不多,对第三种方式举个例子说明一下:
@Configuration public class BeanTest { @Bean public Dog dog(){ Dog dog=new Dog(); dog.setName("旺财"); return dog; } } class Dog { public String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } class Main { public static void main(String args[]){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanTest.class); Dog dog = (Dog) context.getBean("dog"); System.out.println("小狗的姓名:" + dog.getName()); } }
Spring 中的 bean 的作用域有哪些?
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
- session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
- global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话
Spring 中的单例 bean 的线程不安全怎么解决?
如果bean中不存在变更的变量,那就无所谓线程安全和线程不安全,只有当bean中存在变量,如果多个线程同时修改这个变量才会导致线程不安全,那怎么解决呢?使用prototype模式,也就是每次请求都产生一个新的bean,这样就不存在bean共享导致多线程同时修改的问题了。
spring中的bean存放在什么地方?
上面介绍了很多bean相关的内容,但是有一个核心的问题,这里bean存在哪里呢?又是谁在管理这些bean呢?当我们写程序时需要生成一个bean时,往往都是直接使用类似于@Component这样的注解直接生成的,之后我们就不管了,其实这个bean就是被ioc容器存储起来了,所谓的ioc容器就是采用key-value的方式存储的。
到底什么是IOC?
ioc,英文是Inversion of Control,翻译过来就是控制反转,作为一个java程序员,面试时往往会有下面的事情发生。
面试官-某地中海中年男人:谈谈什么是IOC?
我-某意气风发少年:IOC是spring的核心,就是控制反转
面试官-某地中海中年男人:什么是控制反转,怎么就反转了?
我-某意气风发少年:就是之前由程序员自己控制的对象,交给了由IOC容器控制,就叫做控制反转
面试官-某地中海中年男人:谈谈这么做有什么好处?
我-某意气风发少年:如果由开发人员自己new 一个对象,会增大程序的耦合性,而通过IOC管理相当于解耦了程序之间的依赖关系,使所有对象都依赖于IOC容器(腹语:我自己都不知道自己在说什么,为什么这样就解耦了,使用注解不还是需要把bean注入进来,和new的区别又是什么呢?)
面试官-某地中海中年男人:表情凝重,来咱们在谈谈AOP。。。
我-某意气风发少年:表情呆滞(腹语:搞飞机,净问些老掉牙的问题)
以上的对话是我瞎编的,各位看客莫笑,其实为了搞明白什么是IOC,我查了很多资料,因为IOC本身只是一种编程思想,并不是一个具体的实现,所以网上很多解释这个思想的,有人把不使用IOC的程序比作一个齿轮系统,一个齿轮有问题,整个系统就瘫痪了,所以得出一个结论就是这个系统的耦合性太高,这篇文章就是这么讲的:Spring学习之——控制反转(IoC)与依赖注入(DI),这么说对么?我自己的思考是这么说并不对,原因在上面的对话的腹语中已经说了,可能我理解错了,有知道望指教。在文末我会给出一个git地址,是我根据网上的一个教程写的一个简易spring,里面有实现IOC容器,当然这个spring不能和官方的比,但是基本的功能还是都有的。
什么是DI?
相比于IOC,关于DI,我倒是没有太多疑问,看下面的一段程序。
@Service public class AutoService { private static Logger logger = LoggerFactory.getLogger(AutoService.class); @Autowired private AutoMapper autoMapper; }
这段程序我们基本都会这样写。其中@Autowired这个操作就是DI,就是将AutoMapper这个bean和AutoService建立联系,这个过程是这样的,首先AutoMapper这个bean在IOC中可能是存在的(为什么是可能?因为有些bean是不存在的,因为@Aoutowired还有个参数是required = false,为false的意思就是如果这个bean存在就注入,如果不存在就不注入),而AutoService中直接通过@Autowired注入时,并不知道这个AutoMaper是什么,他会向IOC容器中寻找(小注释:正常情况下我们的理解是只有我们添加了注解,才会生成bean,IOC容器中才会存在,其实并不是这样的,在初始化的时候spring会把很多的对象放到IOC容器中,比如我们常见的HttpServletRequest这个类,spring就会自动把他放到容器中,可以直接注入),当找到这个bean之后呢,他会为这个autoMapper赋值,那怎么赋值呢?大家仔细看,其实这个private AutoMapper autoMapper;就是AutoService 中的一个字段,也就是说是类中一个字段而已,其实就是给类中的字段赋值,那怎么给类中的字段赋值,通过反射的机制,我的另一篇博客有写这个:java基础之----反射
什么是AOP?
aop,我们大家都知道,就是面向切面编程,是一种编程思想。那问题来了,什么是面向切面,为什么要面向切面?大家知道java中有封装、继承、多态,这是面向对象的精髓,为什么要搞这些,其实就是为了使编程更简便,减少代码的冗余,继承解决了什么问题呢?继承解决了纵向拉伸的问题,什么叫纵向拉伸,就是有很多的对象有公共的地方,那把公共的地方提取出来,作为这些对象的父类,然后这些对象再继承这个父类,通过这种方式可以减少很多冗余的代码。那aop其实就是横向拉伸,什么叫横向拉伸,就是很多对象本身没有公共的地方,但是现在我要对这些对象都做一个操作,比如我要在执行这些对象之前做些操作,在操作完这些对象之后再做一些操作,这个就不能通过继承来完成。可以把这些对象当做一个横切面,我在这个切面前后可以做点其他事情。这就是面向切面编程。
最常见的aop操作是什么呢?
这个我相信大家并不陌生,大家常见的过滤器,拦截器就是最常见的aop操作。
aop的底层原理是什么呢?
底层其实就是动态代理,在了解动态代理之前,先了解一下什么是java中的中设计模式-代理模式,参考这篇博客:23种设计模式--代理模式
上面那篇博客中其实已经讲了动态代理和静态代理的区别,这篇博客感觉讲的更全面:动态代理与静态代理的区别
aop示例?
下面是我在github上找到的一个使用拦截器来实现日志记录功能demo,大致介绍一下这个demo,程序中的日志功能,其实大家都懂,在需要打印日志的地方,我们直接使用类似于log.info(),这样就可以实现,但是如果需要记录用户访问记录怎么办?比如张三今天访问了系统的登陆页,之后是首页,又访问了某某列表页,类似于这种访问日志,如果每个地方我们都自己去记录,然后保存到数据库,需要写很多的冗余代码,而且使代码看上去非常不简洁,这个demo就是做这样一件事,使用拦截器来实现访问日志记录的功能。但是有点遗憾的是这个demo有点老了,使用的是spring mvc,不是springboot,不过基本思想是没有任何问题的。
aop demo地址:https://github.com/MusicXi/demo-aop-log.git
自己手写spring示例
这个demo是一个简易的spring,实现了IOC,DI等,我这个demo是参考这篇博客写的,博客地址为:纯手写Spring,麻雀虽小,五脏俱全-咕泡学院Tom老师
上面这篇博客基本把源代码贴出来了,但是都是使用的截图,没有可以copy的代码,所以我动手重新敲了一篇,原来的代码我在本地跑的过程中发现有些bug,已经修复了,基本下载下来就可以运行。
spring demo地址:https://gitee.com/jjj200/spring
番外话:最近在思考一件事情,就是事物的本来面貌,比如我们都喜欢自居自己是什么什么人?比如我们都喜欢自居自己是个善良、勤奋、有正义感的人,但往往在遇到事情的时候,我们会有很多和以上完全相反的想法和行为,当我们发现自己是这样的时候,往往我们很自责,其实这就是不尊重事实的表现,因为其实自己就是一个自私、懒惰、为了点好处就媚上的人,只有接受自己本来面目,才有改正的可能,而不是虚伪活在谎言里。