复习宝典之Spring

查看更多宝典,请点击《金三银四,你的专属面试宝典》

第六章:Spring

Spring容器是Spring的核心,一切Spring bean都存储在Spring容器内,并由其通过IoC技术管理。Spring容器也就是一个bean工厂(BeanFactory)。应用中bean的实例化,获取,销毁等都是由这个bean工厂管理的。

org.springframework.context.ApplicationContext接口用于完成容器的配置,初始化,管理bean。一个Spring容器就是某个实现了ApplicationContext接口的类的实例。也就是说,从代码层面,Spring容器其实就是一个ApplicationContext。

在普通的JAVA工程中,我们可以通过代码显式new一个ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext来初始化一个Spring容器。

在Web工程中,我们一般是通过配置web.xml的方式来初始化Spring容器。

 

1)框架特征

轻量--从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

控制反转--Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反--不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

面向切面--Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的--完成业务逻辑--仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

容器--Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建--基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例--以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

框架--Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

MVC--Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

 

2)不仅仅是容器

spring容器工厂:spring对所有对象都可以进行管理,然后通过单例模式创建一个全局唯一的一个实例对象,通过工厂模式在调用注入的接口时实例化对象。

Spring模式包含:单例模式和工厂模式

单例模式:

a.单例模式限制了类实例的创建,但采用这种模式设计的类,可以保证仅有一个实例,并可提供访问该实例的全局访问点。

b.J2EE应用的大量组件,都需要保证一个类只有一个实例,比如数据库引擎访问只有一个。

c.但是更多时候,为了提高性能,程序应尽量减少Java 对象的创建和销毁时的开销。使用单例模式可避免Java 类被多次实例化。

d. 为了防止单态模式的类被多次实例化,应将类的构造器设成私有,这样就保证了只能通过静态方法获得类实例。

 

工厂模式:

a.工厂模式是根据调用数据返回某个类的一个实例,此类可以是多个类的某一个类。

b.通常,这些类满足共同的规则(接口)或父类。调用者只关心工厂生产的实例是否满足某种规范,即实现的某个接口是否可供自己正常调用(调用者仅仅使用)。

c.该模式给对象之间作出了清晰的角色划分,降低程序的耦合。

d.接口产生的全部实例通常用于实现相同接口,接口里定义了全部实例共同拥有的方法,这些方法在不同的实现类中实现的方式不同。

f.从而使程序调用者无须关心方法的具体实现,降低了系统异构的代价。

 

3)IOC(控制反转)与 DI(依赖注入)

IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一,好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系

Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

DI(Dependency Injection),即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

 

4)AOP(面向切面编程)

AOP编程思想:横向重复代码,纵向抽取;就是说多个地方重复的代码可以抽取出来公用(过滤器等可以体现)
动态代理:动态代理可以体现AOP思想;对目标方法进行增强
SpringAOP开发:封装了动态代理代码(包括cglib代理),可以对任何类进行代理增强

AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。

Spring 的 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他 Bean 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。

纵观 AOP 编程,其中需要程序员参与的只有 3 个部分:

  • 定义普通业务组件。

  • 定义切入点,一个切入点可能横切多个业务组件。

  • 定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。

上面 3 个部分的第一个部分是最平常不过的事情,无须额外说明。那么进行 AOP 编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP 框架将会自动生成 AOP 代理

 

5)spring基础知识

spring的一些基本概念:

<?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.xsd">
  
      <!-- spring:管理对象的容器 -->
      <!-- spring IOC:控制反转,通过spring创建对象(无需手动创建) -->
      <!-- DI:依赖注入;就是给对象的某些属性赋值 -->
  
     <!-- 将User对象交给spring管理 -->
     <!-- bean元素:描述需要spring管理的对象;name属性:给对象取个别名,获取对象时需要;
         class属性:被管理对象的完整类名;id属性:与name属性一模一样,不可重复,不能使用特殊字符(因此要name)-->
     <!-- 创建方式一:spring调用无参构造创建对象 -->
     <!-- scope:singleton:表示单例对象,在spring容器中只会存在一个实例 -->
     <!-- scope:prototype:表示多例对象,每次获取都会创建一个新的对象;整合struts2时必须为多例 -->
     <!-- 生命周期方法配置init,destroy -->
     <bean name="user" class="com.bean.User" scope="singleton" init-method="init" destroy-method="destroy"></bean>
 
     <!-- 创建方式二:通过静态工厂的某个方法创建一个user对象 -->
     <bean name="user1" class="com.bean.UserFactory" factory-method="createUser"></bean>
 
     <!-- 创建方式三:实例工厂创建对象 -->
     <bean name="user2" factory-bean="userFactory" factory-method="createUser2"></bean>
     <bean name="userFactory" class="com.bean.UserFactory"></bean>
 
     <!-- 模块化配置,可以导入其他spring配置文件 -->
     <!--<import resource="applicationContext.xml"/>-->
 </beans>

 


spring依赖注入方式:

<?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:p="http://www.springframework.org/schema/p"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
      <!-- set方式注入 -->
      <bean name="user" class="com.bean.User">
          <!-- 值类型注入 -->
         <property name="name" value="xdzy"/>
         <property name="age" value="18"/>
         <!-- 引用类型注入 -->
         <property name="car" ref="car"/>
     </bean>
 
     <!-- 将car对象配置到spring容器 -->
     <bean name="car" class="com.bean.Car">
         <property name="name" value="兰博基尼"/>
         <property name="color" value="红色"/>
     </bean>
 
     <!-- 构造函数注入 -->
     <bean name="user1" class="com.bean.User">
         <!-- 当构造方法参数位置不同,可以通过index确定参数位置 -->
         <!-- 当构造方法参数类型不同,可以通过type确定参数类型 -->
         <constructor-arg name="name" value="xdzy" index="0" type="java.lang.String"/>
         <constructor-arg name="car" ref="car"/>
     </bean>
 
     <!-- p属性注入 -->
     <!-- 需加入xmlns:p="http://www.springframework.org/schema/p" -->
     <bean name="user2" class="com.bean.User" p:name="xdzy" p:age="18" p:car-ref="car"/>
 
     <!-- spel表达式注入;可以实现动态注入 -->
     <bean name="user3" class="com.bean.User">
         <property name="name" value="#{user.name}"/>
         <property name="age" value="#{user2.age}"/>
         <property name="car" ref="car"/>
     </bean>
 
     <!-- 数组注入 -->
     <bean name="cb" class="com.bean.CollectionBean">
         <!-- 只有一个值时 -->
         <!--<property name="arr" value="tom"/>-->
         <!-- 多值多元素注入 -->
         <property name="arr">
             <array>
                 <value>xdzy</value>
                 <value>jady</value>
                 <ref bean="user3"/>
             </array>
         </property>
     </bean>
 
     <!-- 集合注入 -->
     <bean name="cb2" class="com.bean.CollectionBean">
         <!-- 只有一个值时 -->
         <!--<property name="list" value="tom"/>-->
         <!-- 多值多元素注入 -->
         <property name="list">
             <list>
                 <value>xdzy</value>
                 <value>jady</value>
                 <ref bean="user3"/>
             </list>
         </property>
     </bean>
 
     <!-- map注入 -->
     <bean name="cb3" class="com.bean.CollectionBean">
         <property name="map">
             <map>
                 <entry key="name" value="xdzy"/>
                 <entry key="user" value-ref="user3"/>
                 <entry key-ref="user1" value-ref="user2"/>
             </map>
         </property>
     </bean>
 
     <!-- 资源注入 -->
     <bean name="cb4" class="com.bean.CollectionBean">
         <property name="properties">
             <props>
                 <prop key="driverClass">com.jdbc.mysql.Driver</prop>
                 <prop key="userName">admin</prop>
                 <prop key="password">123</prop>
             </props>
         </property>
     </bean>
 </beans>

 


spring的通知类型:

package com.aspect;
  
 import org.aspectj.lang.ProceedingJoinPoint;
 /**
  * @author: 肖德子裕
  * @date: 2018/9/7 08:23
  * @description: 通知
  * 前置通知:目标方法运行之前
  * 后置通知(如果出现异常不会调用):之后
  * 环绕通知:之前之后
  * 异常拦截通知:出现异常调用
  * 后置通知(无论是否出现异常都会调用):之后
 */
 public class MyAdvice {
     //前置通知:目标方法运行之前
     public void before(){
         System.out.println("前置通知");
     }
     //后置通知(如果出现异常不会调用):之后
     public void afterReturning(){
         System.out.println("后置通知(如果出现异常不会调用)");
     }
     //环绕通知:之前之后
     public Object around(ProceedingJoinPoint pjp) throws Throwable{
         System.out.println("环绕通知之前部分");
         //调用目标方法
         Object proceed=pjp.proceed();
         System.out.println("环绕通知之后部分");
         return proceed;
     }
     //异常拦截通知:出现异常调用
     public void afterException(){
         System.out.println("出现异常调用");
     }
     //后置通知(无论是否出现异常都会调用):之后
     public void after(){
         System.out.println("后置通知(无论是否出现异常都会调用)");
     }
 }

 


6)JDK动态代理与CGLIB动态代理

JDK动态代理:

 package com.service;
  
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  
 /**
  * @author: 肖德子裕
  * @date: 2018/9/6 20:25
  * @description: 动态代理
  * 被代理对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理技术
  * 动态代理可对方法进行增强,如增加事务的打开与提交
  * 个人理解:它是对service实现类里所有的方法进行了增强;
  * 在不破坏原有结构的情况下,生成动态代理对象,对原有方法进行增强
  */
 public class UserServiceProxyFactory implements InvocationHandler {
     private UserService us;
 
     public UserServiceProxyFactory(UserService us) {
         this.us = us;
     }
 
     public UserService getUserServiceProxy(){
         //生成动态代理
         UserService userProxy=(UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
                 UserServiceImpl.class.getInterfaces(),
                 this);
         //返回一个动态代理对象
         return userProxy;
     }
 
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("打开事务");
         Object invoke=method.invoke(us,args);
         System.out.println("提交事务");
         return invoke;
     }
 }

 


cglib动态代理:

package com.service;
 
 import org.springframework.cglib.proxy.Enhancer;
 import org.springframework.cglib.proxy.MethodInterceptor;
 import org.springframework.cglib.proxy.MethodProxy;
 
 import java.lang.reflect.Method;
 /**
  * @author: 肖德子裕
  * @date: 2018/9/6 20:25
  * @description: cglib代理
  * 可以对任何类生成代理,对目标对象进行继承代理
  */
 public class UserServiceProxyFactory2 implements MethodInterceptor {
     public UserService getUserServiceProxy(){
         //生成代理对象
         Enhancer en=new Enhancer();
         //对谁进行代理
         en.setSuperclass(UserServiceImpl.class);
         //代理要做什么
         en.setCallback(this);
         //创建代理对象
         UserService us=(UserService) en.create();
         return us;
     }
 
     @Override
     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
         //打开事务
         System.out.println("打开事务");
         //调用原有方法
         Object returnValue=methodProxy.invokeSuper(o,objects);
         //提交事务
         System.out.println("提交事务");
         return returnValue;
     }
 }

 


原文地址:https://www.cnblogs.com/xdzy/p/10485447.html