Spring基础考点复习

Spring是一个轻量级、低侵入的框架

一、Spring之IOC(控制反转)

IOC:Inversion of Control控制反转:将对象的创建、销毁、初始化等一系列生命周期的过程交给Spring容器来管理;

    Spring容器:ApplicationContext;

    Spring的主配置文件为:ApplicationContext.xml;

1、启动一个基本的Spring容器就是依靠上面的两个东西,代码如下:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

基本的配置文件applicationContext.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  
  <!-- bean:将一个类的创建过程交给Spring容器管理
      id:唯一标识符
      class:指定类的路径
  -->
  <bean id="dog" class="com.jiguiquan.www.ioc.Dog"></bean>
</beans>

2、Spring中单例和多例

image.png

scope常用值其实有4个:

singleton:单例(默认为单例)

prototype:多例

request:寿命为一次请求

session:寿命为一次会话

3、Spring中的懒加载与非懒加载

image.png

image.png

懒加载和非懒加载的方式,各有各的优缺点:

懒加载:对象使用的时候再去创建,节约资源;但是不利于提前发现项目错误;

非懒加载:容器启动时立马创建对象,消耗资源;但是有利于提前发现项目错误;

4、Spring中对象的初始化和销毁

image.png

对象的初始化方法:比较容易理解,它发生在对象创建完成之后,即先执行构造方法,在执行初始化方法

对象的销毁方法:它发生在spring容器关闭的时候,通过将context强转为ClassPathXmlApplicationContext对象后就可以调用.close()方法关闭spring容器了;

image.png

补充:

a、已经通过Spring创建的对象如何获取? 使用:

Dog dog1 = (Dog)context.getBean("dog");

b、scope为singleton单例模式创建的bean:默认为非懒加载;

    scope为prototype多例模式创建的bean:默认为懒加载;(想想也是,多例的话,将会会出现很多对象,容器启动的时候无法知道)


二、Spring之DI(依赖注入)

DI:Dependency Injection依赖注入:给对象的属性赋值,共有两种方法;

  • 通过构造方法赋值;

  • 通过get/set方法赋值;

1、通过构造方法给对象的基本属性赋值:

image.png

2、通过get/set方法给对象的基本属性赋值:

image.png

3、通过DI中的get/set方法给对象的List/Set/Map/properties等复杂属性赋值:

新建一个复杂对象Person:

public class Person {
    private String name;

    private Panda pet;

    private List list;

    private Set set;

    private Map map;// key value

    private Properties props;// .properties
    
    ......
}

给Person对象中的list、set、map、properties类型的复杂属性赋值:

  <bean id="person" class="cn.java.di1.Person">
     <property name="name" value="王二麻子"></property>   ——给普通属性赋值,name/value
     <property name="pet" ref="smallPanda"></property>    ——给自定义对象属性赋值,name/ref
     <property name="list">                               ——给list类型属性赋值,name/list/value,其中遇到自定义对象还是用ref
      <list>
       <value>list1</value>
       <value>中国</value>
       <ref bean="smallPanda"/>
      </list>
     </property>
     <property name="set">                                ——给set类型属性赋值,name/set/value,其中遇到自定义对象还是用ref
      <set>
       <value>set1</value>
       <value>china</value>
       <ref bean="smallPanda"/>
      </set>
     </property>
     <property name="map">                                ——给map类型属性赋值,name/map/entry/key/value
      <map>
       <entry key="name" value="李四"></entry>
       <entry key="age" value="10"></entry>
      </map>
     </property>
     <property name="props">                              ——给配置文件类型operties属性赋值,name/props/prop/key
      <props>
       <prop key="driver">com.mysql.jdbc.Driver</prop>    ——一个prop就是一条配置记录
       <prop key="url">jdbc:oracle:@thin:localhost:1521:orcl</prop>
      </props>
     </property>
 </bean>

三、Spring之AOP

AOP:Aspect Oriented Programming 面向切面编程:OOP的延续。将系统中非核心的业务提取出来,进行单独处理。比如事务,日志等;

AOP可以实现:对功能的扩展不需要通过修改源码的方式实现;

大白话来谈谈AOP和OOM的对比:

  • OOP面向对象解决了层次的问题,比如下级继承上级来增强上级等等;

  • AOP面向切面解决了前后左右级别的问题,比如分别给一个对象的不同方法,加入不同的额外功能,特点是通过代理方式对目标类在运行期间织入功能

以下是在网上另一处找到的文字,似乎更容易理解一点:

AOP是什么?

AOP,面向切面编程,是对OOP的补充。从网上看到的一句话:这种在运行时,动态的将代码切入到类的指定方法或者指定位置上的编程思想,就是面向切面的编程这是其中的一种方式,在运行时动态添加。还有另外一种是在编译代码的时候,将代码切入到指定的方法或者位置上去,这是静态添加的方式。

使用

我们在实际的业务中都会有一些公共逻辑,比如日志的记录,事务的管理等等,而如果每次都把日志和事务的代码手动写到业务逻辑前后,重复代码就相当可怕,而如果这些额外代码有修改,必须要每个都修改,这是相当不明智的。AOP可以帮我们解决这些问题。

实现

其实AOP本身并不能帮我们解决那些问题,AOP就是一种思想,而帮我们解决的是具体的AOP的实现,比如aspectj,jboss AOP,以及我们最这里要讲的Spring AOP;

Spring AOP在Spring1.0的时候是自己实现的AOP框架,在2.0之后就开始集成了aspectj。现在我们所说的Spring AOP就是Spring加Aspectj这种方式。

1、实际生活中的例子:——手机银行App

image.png

通过此实际应用,引入AOP中的几个基本概念:

1、切面:a(Securty)、c(Rizhi)、d(Clean),这几个类,上面的四个模块想象成4张纸,除了主业务模块外,一个切面就是一张纸,执行顺序纸上打洞(从上往下);

2、通知:切面中的方法 isSecurity()、log()、clearResource()就是通知

        a) 前置通知:在核心类的前面的叫前置通知;

        b) 后置通知:在核心类后面的叫后置通知;

        c) 环绕通知:

        d) 异常通知:

3、连接点:[BankServiceImpl]核心业务类中的目标方法,连接点用来区分前置和后置的,没有连接点,就无法区分前置通知和后置通知;

4、织入:将目标方法和通知整合起来的过程就叫做织入;

5、代理:完成“织入”工作的主体就叫做代理(proxy)——这里就体现出了AOP中使用的设计模式(经典 代理模式

2、AOP面向切面的具体配置过程(Spring的厉害之处就在于能将AOP思想通过配置的形式实现出来):

第一步:导入jar包(3个):

第二步:配置aop提示模板:

第三步:配置aop模板头信息:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- <bean/> definitions here -->
</beans>

第四步:创建切面bean(首先要将4个类对象交给Spring去管理):

<!-- 配置安全切面bean -->
<bean id="security" class="cn.java.aop.Security"></bean>

<!-- 配置核心业务类bean -->
<bean id="bankServiceImpl" class="cn.java.aop.BankServiceImpl"></bean>

<!-- 配置日志类bean -->
<bean id="rizhi" class="cn.java.aop.Rizhi"></bean>

<!-- 配置清除缓存类bean -->
<bean id="clean" class="cn.java.aop.Clean"></bean>

第五步:配置aop:(切入点+前置通知+后置通知等):

<!-- 配置aop(切面是指类层,通知和连接点,切入点都是指方法层) -->
<aop:config>
    <!-- 配置切入点,".*"代表这个类下面的“所有方法”都当做切入点,“(..)”代表这些切入点可能带参数,也可能不带; -->
    <aop:pointcut expression="execution (* cn.java.aop.BankServiceImpl.*(..))" id="aaa"/>
    
    <!-- 配置切面 -->
    <aop:aspect ref="security">
        <!-- aop:before代表前置通知,method代表通知的方法名,pointcut代表前置于哪个切入点前面 -->
        <aop:before method="isSecurity" pointcut-ref="aaa"/>
    </aop:aspect>
    
    <aop:aspect ref="rizhi">
        <aop:after method="log" pointcut-ref="aaa"/>
    </aop:aspect>
    
    <aop:aspect ref="clean">
        <aop:after method="clearResouce" pointcut-ref="aaa"/>
    </aop:aspect>
</aop:config>

备注:1、配置通知时候,想越先执行的通知,离切入点越近;(方法一)

        2、配置aop切面时候,使用order属性,数值越大,权重越高,越先被执行;

(如果以后想去掉日志模块的功能,代码一行都不用修改,只需要注释掉rizhi的aop:aspect配置)——搭积木——低侵入

增加一个功能,也同样,写一个新的模块——>增加一个切面bean——>再增加一个aop:aspect切面配置

3、带返回值的切入点和带参数的切面通知:

image.png

<aop:aspect ref="receive" order="3">
	<!-- ruturning代表aaa这个切入点会有个返回值,这个返回值将被printMoney这个通知方法里面的money这个形参接收 -->
	<aop:after-returning method="printMoney" pointcut-ref="aaa" returning="money"/>
</aop:aspect>
        BankServiceImpl bsi = (BankServiceImpl)context.getBean("bankServiceImpl");
	Windows窗口中输出结果:
	bsi.getMoney();  //getMoney方法有返回值,会被Receive切面中的后置通知方法printMoney中的形参money接收到;
	bsi.zhuanMoney();  //zhuanMoney没有返回值,直接不调用Receive切面中的方法,因为此方法需要有输入值(good);
备注:为了解决多个切入点的返回值不一样的问题,在切面通知里面接收参数时候可以统一使用Object作为参数类型;
为了解决业务类的各连接点可能会返回不同的数据类型,所以我们可以将 int m修改为 Object m,这样就比较通用了!

4、环绕通知(重要应用——权限)

我们给上面的例子,增加一个新的需求(环绕通知),只有环绕通知通过后,才可以进行后面的所有切面的执行

判断用户名和密码是否正确(权限过滤器)——————>环绕通知:

|

a)    检测手机系统环境是否安全     Securty—->isSecurity()

    |

     建行手机APP

b) 1、查询余额      2、 转账      3、投资理财   BankServiceImpl核心类(目标类–>目标方法)

        |

c)    记录用户操作日志 Rizhi—->log()

        |

d)      清空缓存记录 Clean—->clearResource()

验证类的代码:Auth.java

public class Auth {
	private String username = "admin";
	private String password = "123";
				
	public void isUserExist(ProceedingJoinPoint pjp) throws Throwable {
		if ("admin".equals(username)&&"123".equals(password)) {  //通过则放行
			System.out.println("登录成功!");
			pjp.proceed();   //有这行代码,代表放行
		}else {  //用户名或密码错误
			System.out.println("亲,用户名或密码错误,请重试");
		}
	}
}

配置实现aop.xml:

<!-- 配置环绕切面通知 -->
<aop:aspect ref="auth">
	<aop:around method="isUserExist" pointcut-ref="aaa"/>
</aop:aspect>

问:为什么使用“环绕通知”,而不使用前置通知实现?

答:之所以用环绕通知,而不用前置通知是因为,前置通知只前置,却不阻拦,前置完,后面的代码依旧执行,环绕通知通过“pjp.proceed()”决定后面的代码是否放行;

5、异常通知(aop:throwing):

  • 专门用来处理异常情况,比如记录异常日志、跳转到指定页面等;

  • 只有方法发生异常的时候,才会去请求这个切面异常通知,正常时候是不会请求这个通知的;

验证的代码Global.java:

public class Global {
	public void handExpt(Throwable ex) {
		System.out.println("哎呀,我出错了");
		//		System.out.println(ex.getMessage());  /// by zero
		//		System.out.println(ex.getClass());   //class java.lang.ArithmeticException
		//		System.out.println(ex.getCause());	//null
		System.out.println(ex.toString());  //java.lang.ArithmeticException: / by zero
		//实际生产过程中的处理是“记录异常错误日志”+“跳转到指定页面”;
	}
}

配置文件(aop.xml):

<!-- 配置异常处理切面通知 -->
<aop:aspect ref="globalExpt">
	<!-- 如果aaa切入点执行时候发生错误,统一去找globalExpt切面里面的handExpt方法,并把抛出的异常对象传给形参ex; -->
	<aop:after-throwing method="handExpt" pointcut-ref="aaa" throwing="ex"/>
</aop:aspect>

四、Spring考点补充

1、Spring框架中涉及到的设计模式有哪些?简单列举几个?

第一种:简单工厂模式:

简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

第二种:单例模式:

Spring IOC模式就是单例模式,如果想使用多例模式可以使用scope="prototype"来创建多例对象;

第三种:代理模式

Spring AOP就是通过代理模式(proxy)将各个切面中的各个通知与目标方法整合起来(这个过程叫织入);

第四种:观察者模式(Observer)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

spring中Observer模式常用的地方是listener监听器的实现。如ApplicationListener。 

2、简单讲一下设计模式?

设计模式共有23中(GOF23),其中:创建:结构:行为 = 5:7:11

创建型(5种)

1. Factory Method(工厂方法)

2. Abstract Factory(抽象工厂)

3. Builder(建造者)

4. Prototype(原型)

5. Singleton(单例)

结构型(7中)

6. Adapter Class/Object(适配器)

7. Bridge(桥接)

8. Composite(组合)

9. Decorator(装饰)

10. Facade(外观)

11. Flyweight(享元)

12. Proxy(代理)

行为型(11种)

13. Interpreter(解释器)

14. Template Method(模板方法)

15. Chain of Responsibility(责任链)

16. Command(命令)

17. Iterator(迭代器)

18. Mediator(中介者)

19. Memento(备忘录)

20. Observer(观察者)

21. State(状态)

22. Strategy(策略)

23. Visitor(访问者)

3、讲一讲Spring中Bean的生命周期:

1.png

通过这张图能大致看懂spring的生命周期,详解:

1、instantiate bean对象实例化

2、populate properties 封装属性

        3、如果Bean实现BeanNameAware执行setBeanName

        4、如果Bean实现BeanFactoryAwar或ApplicationContextAwar设置工厂setBeanFactory或上下文对象setApplicationContext

        5、如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforeInitialization

        6、如果Bean实现InitializingBean执行afterPropertiesSet

7、调用自定义的init-method方法

        8、如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterInitialization

9、执行业务处理

        10、如果Bean实现DisposableBean执行destroy

11、调用自定义的destroy-method

第一步 就是对实例化bean,调用构造函数来创建实例,第二步 是根据配置,进行相应属性的设置,依赖注入就是在这一步完成的。

第三步 和 第四步 是让spring去了解咱们的spring容器,第五步 和 第八步 可以针对指定的Bean进行功能增强,这时一般是采用的动态代理,(两种动态代理方式:jdk动态代理和cglib动态代理)。

第六步 和 第十步 是通过实现指定的接口来完成init(初始化)和destory(销毁)操作。但是我们在通常情况下不会使用这两步,

因为我们可以通过 第七步 和 第十一步,在配置文件中设置相应的初始化和销毁方法。

       总结:

    对于springbean的生命周期,我们需要关注的主要有两个方法:

      1、增强bean的功能可以使用后处理Bean,BeanPostProcessor

      2、如果需要初始化或销毁操作,我们可以使用init-method方法和destory-method方法。

  同时还需要注意一点:destory-method方法是只针对于scope=singleton的时候才有效果!

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐