周六了,又是摸鱼的一天,今天还有点不在状态,脑瓜子迷迷糊糊的,昨晚出去喝可乐桶喝的脑子到现在都不是很正常(奉劝各位可以自己小酌:450ml威士忌+1L多一点可乐刚刚好,可能是我酒量不好),正好没啥事就想整理一下自己的文件夹,发现了很久之前整理的一个spring基础的思维导图,如下:

今天,就以这份思维导图为基础,讲解一下spring基础的内容,好了,我们来看一下**文字和代码**的详细解析吧

**需要这份思维导图的,可以关注公众号:Java架构师联盟,后台回复Java即可**

# 什么是Spring

spring是一个轻量级的控制反转(ioc)和面向切面编程(AOP)的容器框架。

- 轻量:从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的jar文件里发布;并且Spring所需的处理开销也是微不足道的。
- 非入侵:在应用中,一般不需要引用springjar包里的类
- 控制反转:Spring的核心机制,通过控制反转技术促进了松耦合。简单来讲,就是把对象创建的权力交给了容器,让容器来管理对象。
- 面向切面:允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。AOP是基于代理的,通俗一点就是把 核心业务 和 系统服务(日志系统、权限管理等) 分开。

# Spring的核心配置和类

1. **applicationContext.xml**:核心配置文件。作用:用于配置所有的类,这些类可以称为springbean
2. **BeanFactory**:容器的工厂类(接口)。作用:用于创建或获取springbean,即spring管理的对象。
3. **ApplicationContext**:应用上下文(接口)他是BeanFactory的子类 作用:用于创建或获取springbean。功能比BeanFactory强大。**BeanFactory**和**ApplicationContext**的区别: *BeanFactory*:懒加载 需要某个对象再去加载 *ApplicationContext*:非懒加载 一次性加载所有的对象

# Spring IOC

**控制反转**:把对象的创建、销毁的权利交给容器框架,由容器来管理对象的生命周期。ioc不是新的技术,只是一种思想或理念,实现松耦合。

IOC包括**依赖注入**(**DI**,核心) 和 依赖查找。

**DI**:依赖注入,简单来讲就是在spring实例化对象的时候,由容器来设置这些对象的属性。

# spring的注入方式

# 属性的注入(set方法注入)

**前提要有对应的setter方法**

以下为spring配置文件代码 java bean忽略。

```
<bean id="person" class="com.xx.Person">
<property name = "name" value = "xzy"/>
<property name = "coll">
<list>
<value>list</value>
<value>list2</value>
</list>
</property>
<property name = "map">
<map>
<entry key = "age" value = "21" />
</map>
</property>
<property name = "arr">
<array>
<value>java</value>
<value>C++</value>
</array>
</property>
</bean>
```

# 通过构造器注入

需要构造方法,javaBean代码:

```
public class User {
private String name;
private Integer age;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
```

配置文件代码:

```
<!--通过构造器的方式-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value = "Jay" ></constructor-arg>
<constructor-arg value = "21" />
</bean>
<!--指定下标的方式-->
<bean id="user" class="cn.pojo.User">
<constructor-arg value="44" index="1"/>
<constructor-arg value="Jack" index="0"/></bean>
<!--指定在构造中的参数名称-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value="44" name="age" />
<constructor-arg value="xxx" name = "name" />
</bean>
```

# 注入其他类

```
<!--通过构造器注入-->
<bean id = "user" class = "com.xx.User">
<constructor-arg value="Jack"></constructor-arg>
<constructor-arg value="44"></constructor-arg>
<!--引用的方式 设置引用id-->
<property name = "car" ref = "car"></property>
</bean>
<bean id = "car" class = "com.xx.Car">
<property name = "type" value = "BWM"></property>
</bean>

<!--内部声明,用这种方式声明,别的bean不能引用了-->
<property name = "car">
<bean class = "cn.xx.Car">
<property name = "type" value = "红旗"/>
</bean>
</property>
```

# bean元素中的属性

- id:Bean的唯一标识符
- name:通过name对Bean进行管理和配置 name可以多个每个以逗号隔开。
- class:指定了Bean的具体实现类,必须是完整的类名 实用类的全限定名
- scope:设定Bean实例的作用域,其属性有singleton(单例)、prototype(原型)、request、session、和global
Session,默认值为singleton.
- constructor-arg:的子元素,可以传入构造参数实例化 该元素index属性指定构造参数的序号(从0开始).
- property:的子元素,通过调用Bean实例中的setter方法完成属性赋值.
- ref:property、constructor-arg等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用;
- value:property、constructor-arg等元素的子元素,用来直接指定一个常量值;
- list:用于封装List或数组类型的依赖注入。
- set:用于封装Set或数组类型的依赖注入。
- map:用于封装Map或数组类型的依赖注入。
- entry:map元素的子元素 用于设定一个键值对。

# Bean的实例化

# 构造器实例化

Spring容器通过Bean对应的默认构造函数来实例化Bean。

# 静态工厂方式实例化

通过创建静态工厂的方式来创建Bean的实例

```
public class BeanFactory {
public static Bean createBean(){
return new Bean();
}
}
<!--factory-method-->
<bean id = "bean" class = "com.xx.BeanFactory" factory-method = "createBean"></bean>
```

# 实例工厂化方式实例化

不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式.

```
public class BeanFactory {
public BeanFactory(){
System.err.println("BeanFactory 工厂实例化")
}
public static Bean createBean(){
return new Bean();
}
}

<!--配置工厂-->
<bean id = "beanFactory" class = "com.xx.BeanFactory" /><!--factory-bean 属性指向配置的实例工厂;factory-method属性确定使用工厂的哪个方法-->
<bean id = "bean" factory-bean="beanFactory" factory-method="createBean"/>
```

# Bean的作用域

# singleton:单例模式

Spring IOC容器中只会存在**一个共享的Bean实例**,无论有多少个Bean引用它,始终指向同一对象。

```
配置文件:
<bean id ="dog" class = "com.bean.Dog" scope="singleton"></bean>

java代码:
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Dog dog1 = (Dog) ctx.getBean("dog");
System.err.println(dog1);
Dog dog2 = (Dog) ctx.getBean("dog");
System.err.println(dog2);
}

// 输出的结果一致,表明为单例模式。
```

# prototype:原型模式

每次通过Spring容器获取prototype定义的bean时,容器都将创建一个**新的Bean实例**,每个Bean实例都有自己的**属性和状态**。

# request

在一次Http请求中,容器会返回该Bean的**同一实例**。而对不同的Http请求则会产生新的Bean,而且该bean**仅在当前HttpRequest内有效**。
针对每一次Http请求,Spring容器根据该bean的定义创建一个全新的实例,且该实例仅在当前Http请求内有效,而其它请求**无法看到当前请求中状态的变化**,当当前Http请求结束,该bean实例也将会被销毁。

# session

在一次Http Session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例**仅在当前Session内有效**。
同Http请求相同,每一次session请求创建新的实例,而不同的实例之间不共享属性,且实例**仅在自己的session请求内有效**,请求结束,则实例将被销毁。

# Bean的装配方式

# 基于XML的装配

两种装配方式:setter注入和构造器注入。

设置注入的两个要求:

- Bean类必须提供一个默认的午餐构造方法
- Bean类必须为需要注入的属性提供对应的setter方法

# 基于注解(annotation)

常见注解:

- @Component 是所有受Spring管理组件的通用形式。
- @Repository 持久层使用,dao层使用。
- @Service 业务类,表示业务层组件,Service层使用。
- @Controller 控制器,表示web层组件
- @Autowired 按照类型来装配
- @Resource 根据名字去装配

# 自动装配

属性值说明语法default 默认值由的default-autowire属性值确定default-autowire=“default”byName根据属性名称自动装配byType根据属性的数据类型自动装配constructor根据构造函数参数的数据类型自动装配no不适用自动装配,必须通过ref元素定义

# Spring AOP

实现**核心业务**和**系统服务**代码之间的分开 通过一种特殊的技术手段来实现核心业务运行时能够实现系统服务的功能;
***aop的本质是代理*** 通过对方法进行拦截、增强来实现的。

# AOP的基本概念

采用**横向抽取机制**,把分散在各个方法中的**相同的代码抽取出来**,然后在编译器或者是运行时再把这些代码应用到所需要执行的地方。

**通知(Advice)**:aop在切点上执行的增强处理。

通知的类型:

- 前通知(methodBeforeAdvice):方法执行前做增强
- 后通知(methodAfterAdvice):方法执行后做增强
- 环绕通知(MethodInterceptor):方法执行前和后做增强
- 返回通知(AfterReturningAdvice): 成功返回后 进行增强
- 异常通知(ThrowsAdvice): 抛出异常后 进行通知

**切点(Pointcut)**:就是带有通知的连接点,就是对那些类 哪些方法做增强。

切点的类型:

基于正则表达式 JdkRegexpMethodPointcut

基于AspectJ的表达式 AspectJExpressionPointcut

**切面(Aspect)**:通常上就是一个类,里面定义了 **通知** 和 **切点**

***AOP = 通知 + 切点***

# AOP案例 java实现

```
// com.bean.User
public class User {
public String login(String name){
return "name is "+ name ;
}
public String login(String name , int pwd){
return "name is "+name+", pwd is "+pwd ;
}
}

// 新建一个环绕通知
public class MyAroundAdivice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable{
System.err.println("方法执行前:"+arg0.getMethod().getName()+","+arg0.getArguments()[0]);
Object obj = arg0.proceed();
System.err.println("执行完成后...");
return obj;
}
}

// 新建基于AspectJ切点的测试类
@Test
public void test(){
// 1. 声明通知
Advice advice = new MyAroundAdivice();
//2、基于AspectJ声明切点对象
AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
//3、设置表达式
/*返回类型为String类型 com.bean.User的login方法 参数为String类型*/
//cut.setExpression("execution (String com.bean.User.login(String))");
//任意放回类型 com包下包括com子包下 任意方法 任意的参数0~n
cut.setExpression("execution(* com..*(..))");
//4、切点+通知 =AOP
Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
//5、声明代理bean
ProxyFactory proxy = new ProxyFactory(new User());
//6、设置aop
proxy.addAdvisor(advisor);
//7、从代理中获取代理的对象
User user = (User) proxy.getProxy();
user.login("rose");
System.err.println("---------------");
user.login("jack",123);
}
```

AspectJ语法

*这个目录下的,或是类上的所有…任意的多个。0~N个execution (* com.bean.User.login(String,int))对login方法,必须要接收两个参数,且参数的类型必须是Strig,int的,且返回值无限制。且这个方法必须是User这个类的Execution (* com.*.*(…))返回所有的类型 所有在com包下的类,不包含子包 类的所有方法 接收任意多个参数 0~NExecution (* com…*.*(…))在com包下的,包含子包下的所有类的所有方法,所有参数都切入execution(* com…*.*(String)) || execution(* com…*.*(*,int))|| 逻辑或

# AOP案例 基于XML声明式AspectJ

```
<bean id = "user" class = "com.bean.User">
<!-- 定义一个切面 -->
<bean id="myBeforeAdvice" class="com.demo03.MyAdvice"></bean>
<aop:config>
<!-- 配置切入点 -->
<!-- 表达式(用来表示方法) -->
<!-- execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>),返回值,方法名,参数不能少 -->
<!-- *代表:任意值 方法名:全类名.方法名 参数中的..:任意个数,任意类型 -->
<aop:pointcut expression="execution(* com..*(..))" id="myPointcut" />
<!-- 切面配置 -->
<aop:aspect ref="myBeforeAdvice">
<!-- 配置前通知 -->
<aop:before method="doBefore" pointcut-ref="myPointcut" />
<!-- 配置后通知 -->
<aop:after method="doAfter" pointcut-ref="myPointcut" />
<!-- 配置返回通知 -->
<aop:after-returning method="doReturnAfter"
pointcut-ref="myPointcut" />
<!-- 配置环绕通知 -->
<aop:around method="doAround" pointcut-ref="myPointcut" />
<!-- 异常通知 -->
<aop:after-throwing method="doThrowing"
pointcut-ref="myPointcut" throwing="e" />
</aop:aspect>
</aop:config>
</bean>
```

# 基于注解的声明式AspectJ (常用)

注解功能@Aspect注解在类上,声明这是一个切面@Pointcut注解在方法上声明是一个切点,且要声明aspectj的切点表达式@Before前通知@After @AfterRetuning –正确执行成功返回值 @AfterThrow – 错误的执行,抛出异常后通知@Around环绕通知

第一步:新建注解的切面类

```
@Aspect
@Component
public class MyAspect {
//定义一个切入点表达式 使用一个返回值为void,方法体为空的方法来命名切点
@Pointcut("execution(* com..*(..))")
public void myPointCut(){}

//前置通知
@Before("myPointCut()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("前置通知,方法开始..."+":目标类是"+joinPoint.getTarget()+",被植入的增强方法是:"+joinPoint.getSignature().getName());
}
//后置通知
@After("myPointCut()")
public void doAfter()
{
System.out.println("后置通知,方法结束...");
}
//返回通知
@AfterReturning("myPointCut()")
public void doReturnAfter()
{
System.out.println("返回通知,方法最终结束...");
}
/**
* 环绕通知 ProceedingJoinPoint用来调用被增强的方法
* 必须是Object的返回类型
* 必须接受一个参数,类型为ProceedingJoinPoint
* 必须throws Throwable
*/
@Around("myPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("环绕通知:begin...");
//执行被增强的方法
Object obj = joinPoint.proceed();
System.out.println("环绕通知:end.....");
return obj;
}
@AfterThrowing(value="myPointCut()",throwing="e")
public void doThrowing(Throwable e){
System.out.println("异常通知......."+e.getMessage());
}
}
```

第二步:xml配置文件

```
<bean id="user" class="com.bean.User"></bean>
<context:component-scan base-package="com.xxx"></context:component-scan>
<!-- 启动基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
```

第三步:测试方法

```
@Test
public void test1() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/xxx/applicationContext.xml");
User user = ctx.getBean("user",User.class);
user.say();
//user.run();
}
```

> 如有问题 请指出

喝完可乐桶后程序员回归本源,开源Spring基础内容的更多相关文章

  1. 80后程序员降薪6K,预感中年危机来袭,准备跳槽却碰壁

    一提及程序员,很多人想到的都是“工资高”“技术好”诸如此类的,可见程序员是个非常赚钱的职业,所以每年都会有很多毕业生来选择这个行业. 但是社会是公平的,不要只看程序员表面上的光鲜亮丽,其背后也有很多的 ...

  2. 好程序员web前端分享HTML基础篇

    好程序员web前端分享HTML基础篇,最近遇到很多新手,都会问,如果要学web前端开发,需要学什么?难不难学啊?多久能入门之类的问题?那么今天好程序员就先来给大家分享一下web前端学习路线:HTML基 ...

  3. 一位90后程序员的自述:如何从年薪3w到30w!

    初入职场之时,大多数人都应该考虑过这样的一个问题,如何找到一种实用,简化web流程的方法,在工作之中能有所提升和突破. 学好哪些?基础必须精通! 九层之塔,起于垒土;千里之行,始于足下.入门之前,这些 ...

  4. 月薪25K的90后程序员,他们都经历了什么?

    如果说薪资是检验一家公司对程序员认可的标准,那么年纪轻轻就能达到月薪 25K,一定程度上说明了公司对他创造的价值的认可. 深访10+ 名月薪25K的程序员,发现他们最常见的三种成长途径是…… 在公司发 ...

  5. Java程序员职业生涯规划完整版:从程序员到CTO( 摘)

    在技巧方面无论我们怎么学习,总感觉需要晋升自已不知道自己处于什么水平了.但如果有清晰的指示图供参考还是非常不错的,这样我们清楚的知道我们大概处于那个阶段和水平. Java程序员 高等特性 反射.泛型. ...

  6. Java程序员面试宝典1 ---Java基础部分(该博文为原创,转载请注明出处)

    (该博文为原创,转载请注明出处   http://www.cnblogs.com/luyijoy/  by白手伊凡) 1.    基本概念 1)         Java为解释性语言,运行过程:程序源 ...

  7. IT观察】网络通信、图片显示、数据库操作……Android程序员如何利用开源框架

    每个Android 程序员都不是Android应用开发之路上孤军奋战的一个人,GitHub上浩如烟海的开源框架或类库就是前人为我们发明的轮子,有的轮子能提高软件性能,而有的轮子似乎是以牺牲性能为代价换 ...

  8. 程序员找工作必备 PHP 基础面试题

    1.优化 MYSQL 数据库的方法 (1) 选取最适用的字段属性,尽可能减少定义字段长度,尽量把字段设置 NOT NULL, 例如’省份,性别’, 最好设置为 ENUM (2) 使用连接(JOIN)来 ...

  9. 程序员书单_java学习基础编程篇

    Java程序设计语言.(美国)阿诺德.清晰版 http://download.csdn.net/detail/shenzhq1980/9076093 JAVA2核心技术第1卷.基础知识7th.part ...

随机推荐

  1. input 与 button 的问题 (空隙/不等高/对不齐)及 解决办法

    1. input 与 button 为什么有空隙? - 要明白为什么,需要了解一下几点基础知识(耐心看完,你会发现竟如此简单)     1. input 与 button 都属于行级块元素,都具有文本 ...

  2. VUE自定义(有限)库存日历插件

    开发过程中遇到一个令人发指的,一个element-ui无法满足的日历需求, 改造其日历插件的代价太大,于是索性自己手写一个,需求如下: 1. 根据开始.结束时间计算时间覆盖的月份,渲染有限的可选择日期 ...

  3. Java安全之JNDI注入

    Java安全之JNDI注入 文章首发:Java安全之JNDI注入 0x00 前言 续上篇文内容,接着来学习JNDI注入相关知识.JNDI注入是Fastjson反序列化漏洞中的攻击手法之一. 0x01 ...

  4. Java 中数组转换为 List

    目录 1 - int 型数组转换为 List 2 - List 转换为 int 型数组 3 - String 型数组转换为 List 4 - List 转换为 String 型数组 版权声明 开发中经 ...

  5. @requestBody 与@requestparam详解

    @RequestParam注解接收的参数是来自于requestHeader中,即请求头.都是用来获取请求路径url 中的动态参数,格式为xxx?username=123&password=45 ...

  6. python3:文件读写+with open as语句(转)

    读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘, ...

  7. Spring源码之循环依赖

    https://www.cnblogs.com/longy2012/articles/12834762.html https://www.bilibili.com/video/BV1iD4y1o7pM ...

  8. simple-rpc

    RPC的实现原理 正如上一讲所说,RPC主要是为了解决的两个问题: 解决分布式系统中,服务之间的调用问题. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑. 还是以计算器Calc ...

  9. linux绑定盘符

    [root@centos6 ~]# udevadm info -q path -n /dev/sdb [root@centos6 ~]# udevadm info -q path -n /dev/sd ...

  10. uniapp开发小程序

    uniapp开发小程序 uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条 ...