spring详解

165 阅读8分钟

1.简介

一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。

原生Spring框架的弊端:配置太多,配置地狱。

2.IOC

IOC(控制反转)是一种设计思想。在没有控制反转的程序中,使用面向对象编程,对象与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制。Spring中控制反转是通过IOC容器创建,创建权交由第三方来管理,将对象的创建权反转了。

控制反转是通过描述(xml和注解)并通过第三方生产或获取对象的方式。在Spring中控制反转是通过IOC容器,其实现方式是DI(依赖注入)

DI(依赖注入)

DI(依赖注入)是实现IOC的方式。例子:类 A 依赖于类 B。注入:所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。

创建对象方式

XML和注解,采用XML方式配置Bean的时候,Bean的定义信息和实现是分离的,而注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

  1. 构造器创建对象

    1>使用无参构造创建对象,默认。

    <bean id="" class=""></bean>
    

    2>使用有参构造创建对象。(下标,类型,参数名)

    <bean id="" class=''>
       <constuctor-arg index="" value=""></constuctor-arg>
    </bean>
    

    总结:在配置文件加载的时候,容器中管理的对象就已经初始化了。

  2. 调用静态工厂方法创建Bean对象

    静态工厂方法创建bean对象的核心是: class + factory-method

    创建一个工厂类,然后在类中写一个返回值为 new 其他对象的方法,在xml中调用,用factory-method指定该方法。

    public class StaticFactoryBean { 
    	public static Integer createRandom() { 
    		return new Integer(new Random().nextInt());} 
    }
    
    <!-- 调用静态工厂方法创建bean对象 -->
    <bean id="random"  class="com.beans.factory.StaticFactoryBean"  factory-method="createRandom">
    </bean>
    
    public static void main(String[] args) {
    //调用getBean()时,返回随机数.如果没有指定factory-method,会返回StaticFactoryBean的实例,即返回工厂Bean的实例       
    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml"));       
    System.out.println("我是IT学习者创建的实例:"+factory.getBean("random").toString());
    }
    
  3. 调用实例工厂方法创建Bean对象

    先实例化工厂类的bean

    在引用工厂Bean来配置其他Bean

    <!-- 先配置工厂Bean,class指定该工厂的实现类,该Bean负责产生其他Bean实例 -->
    <bean id="userFactory" class="com.beans.factory.UserFactory"/>
    <!-- 再引用工厂Bean来配置 其他Bean -->
    <bean id="user"  factory-bean="userFactory"  factory-method="createPerson">
    <constructor-arg name="id" value="666"></constructor-arg>
    <constructor-arg name="name" value="Leorizon"></constructor-arg>
    </bean>
    

Spring涉及到的设计模式

  1. 工厂模式:参照静态工厂创建Bean对象。

  2. 单例模式:分为懒汉、饿汉模式。

    懒汉:延迟加载,第一次调用才初始化,避免内存浪费。可以加synchronized实现线程安全。

    饿汉:类加载时就初始化。没加锁,执行效率高。通过classloader机制避免线程同步问题。

    public class Singleton {  
        private static Singleton instance = new Singleton();  
        private Singleton (){}  
        public static Singleton getInstance() {  
        return instance;  
        }  
    }
    
  3. 适配器模式

  4. 代理模式:代理类与被代理类实现同一个接口,代理类中包装被代理类,外部调用只调用代理类。代理模式 | 菜鸟教程 (runoob.com) (AOP)

  5. 模板模式:定义一个算法的骨架(抽象类),将一些步骤延迟到子类中(实现类)。通过多态进行调用。(jdbcTemplate)

  6. 策略模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。(ApplicationListener)

Bean作用域

作用域释义
singleton在SpringIOC容器中只存在一个实例Bean(默认)
prototype每次getBean都会创建一个新对象
request每次http请求都会创建一个新对象
session每个session中共享同一个Bean
global-session一般用于Portlet应用环境

自动装配

相当于<xml中的ref>

如果一个类中的属性是另一个类,SpringIOC创建Bean时,没有自动装配的情况,需要手动在Bean.xml去ref引用这个类定义。

如有自动装配,会根据自动装配的类型,去springIOC上下文查找符合该条件的Bean,自动引入,不需要ref引入。

ByName Resource配置,根据属性名字自动查找装配

ByType autowired配置,根据属性类型自动查找配置

construtor 类似ByType ,但该类型适用于构造函数参数类型

注解自动装配

相当于<将xml中的ref注解,写在了后台代码中>

@Autowired ByType 放在属性上,该属性就不需要set方法了

@Resource ByName

@Qualifier(value="xxx")

@Autowired 和 @Resource的区别

都是用来自动装配的,都可以放在属性字段上;

@Autowired是通过ByType实现,而且必须要求这个对象存在!<常用>

@Resource是通过ByName实现,如果找不到名字,就用ByType实现,如果找不到,则报错。

注解装配Bean

相当于xml中的

@Component

@Controller @Service @Repository 三个注解都是一样的,这三个区分为MVC三层,都是继承@Component

@Scope(“singleton”) 设置作用域

**@Value()**实现注入<相当于xml中的property>

这些注解生效的前提是扫描包

完全不用xml配置的情况

@Configuration 用于定义配置类,启动springIOC容器,可以替换xml文件,被注解的类内部可以包含一个或多个@Bean的方法

@Configuration 的类相当于 配置了一个beans.xml

**@ComponentScan()**扫描包,指定注解生效

@Bean

用于方法上,该方法返回一个实例对象告诉Spring,然后在Spring容器中注册成一个bean,通常方法体中包含了最终产生bean实例的逻辑。

主要用于第三方库中的类需要装配到Spring容器,因为无法在第三方库中加@Component注解,只能通过@Bean来实现。

@Component

用于类上,将该类注册成一个组件类(bean),并告知Spring要为这个类创建bean。与@Controller、@Service、@Repository的意思一样。需要通过类路径扫描来自动装配到Spring容器中。@Bean不需要通过类路径扫描。

@Bean和@Component异同

目的一样,都是想当与在xml配置了一个

@Bean 的注解相当于在beans.xml中设置了一个

@Bean 的默认作用域为singleton 可以设置为prototype

@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,子默认与标注的方法名相同

3.AOP

简介

Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。

通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

  • Aspect(切面):通常是一个类,里面可以定义切入点和通知
  • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
  • Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
  • pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
  • AOP代理:AOP框架创建的对象,代理就是目标对象的加强。JDK动态代理,CGlib
    • Target(目标对象):织入 Advice 的目标对象.。
    • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
JDK动态代理和CGlib动态代理的区别:

JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在具体调用方法前调用InvokeHandler来处理。

CGlib是针对类实现代理,对指定的类生成子类,覆盖其中的方法。利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

注解:

  • @Before(前置通知):目标方法执行前通知
  • @After(后置通知):目标方法执行后通知,不管有没有异常
  • @AfterReturning(返回通知):目标方法正常返回值后运行
  • @AfterThrowing(异常通知):目标方法出现异常后通知
  • @Around(环绕通知):动态代理,需要手动执行joinPoint.procced(),相当于在目标方法执行前是前置通知,执行后,是后置通知

切入点表达式:

execution(* com.sample.service.impl..*.*(..))

整个表达式分为5部分:

  1. execution(): 表达式主体。
  2. 第一个*号:表示返回类型,*号表示所有的类型。
  3. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法
  4. 第二个*号:表示类名,*号表示所有的类。
  5. *(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
//注册一个Bean
@Component
定义此类为一个切面类
@Aspect
public class TestAOP{
   //定义一个切入点,execution指定一个切入执行方法的位置
   //Pointcut定义分为两部分:Pointcut表示式和Pointcut签名(signature)
   @Pointcut("execution(* com.sample.service.impl..*.*(..))")
   //Pointcut签名,目的是可以有多个签名,后面切入的时候,可以选取相应的签名
   public void executionTest(){
   }
   //前置通知,选择Pointcut签名
   @Before("executionTest()")
   //具体切入的执行逻辑
   //AspectJ中的切入点匹配的执行点
   public Object getTest(JoinPoint jp){
	}
}

涉及的类:

  1. JoinPoint
    • 获取入参:.getArgs();
    • 获取切入的方法名:.getSignature().getName();
  2. ProceedingJoinPoint(继承自JoinPoint)
    • .proceed()。执行前,是前置,执行后是后置,返回结果为出参。

两个类区别:

  • ProceedingJoinPoint 继承了 JoinPoint 。
  • ProceedingJoinPoint 是在JoinPoint的基础上暴露出 proceed 这个方法。
  • proceed方法很重要,这个是aop代理链执行的方法。暴露出这个方法,就能支持 aop:around 这种切面,也就是 Proceedingjoinpoint 才支持环绕通知(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关), 它能决定是否走代理链还是走自己拦截的其他逻辑。