简单易懂的Spring 快速上手

重新回顾一遍Spring,希望能够温故知新,有新的见解。

学习Spring的好处

  1. 方便解耦,简化开发
    通过Spring提供的IoC容器,我们可以讲对象之间的依赖关系交由Spring进行控制,避免硬编码锁造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  2. AOP编程的支持
    通过Spring提供的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。
  3. 声明式事务的支持
    在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务管理。
  4. 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作,Spring对Junit4的支撑,可以通过注解方便地进行测试。
  5. 方便继承各种优秀框架
    Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate,Hessian,Quartz等)的支撑。
  6. 降低Java EE API 的使用难度
    Spring对很多难用的Java EE API 提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API 的使用难度大幅下降。
  7. Java源码是学习的典范
    Spring源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。

关键字: IoC,DI,AOP,Spring容器

  1. IoC:控制反转(Inversion of Control),创建对象的控制权不是由用户创建,而是反转给Spring框架来管理。是一种设计思想,不是技术,满足这种思想的技术可以有很多种,Spring IoC是其中一种。

  2. DI:依赖注入(Dependency Injecetion),IoC是指将对象的创建权反转给了Spring容器,而DI则是Spring框架负责创建Bean对象是,动态地将依赖对象注入到Bean组件中。DI和IoC是相辅相成,缺一不可。

  3. AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器中Bean的功能。

  4. Spring容器:IoC容器,存储Spring生成的Bean对象,底层也是一个BeanFactory。

Spring的两大特性之一:IoC / DI

1. Spring IoC/DI 基于xml方式的应用

IoC配置

创建ApplicationContext是通过读取applicationContext.xml文件,然后通过解析xml标签获取响应的信息创建applicationContext对象的。同理,在applicationContext.xml中通过读取bean标签,也可以创建响应的对象,从而交给Spring管理。

    <bean id="person" class="com.pillow.domain.Person"
          init-method="doInit" destroy-method="doDestroy" scope="prototype">
        
    </bean>
  • bean标签的作用:
    用于配置被Spring容器管理的bean的信息
    默认情况下它调用的是类中的 无参构造器。如果没有无参构造器则不能创建成功。
  • bean标签的属性:
    • id:给对象在容器中提供一个唯一标识,用于获取对象。
    • class:指定类的全限定名。用于反射创建对象。默认情况下调用 无参构造器。
    • init-method:指定类中的初始化方法,在创建对象后马上执行。
    • destroy-method:指定类的销毁方法名称,在销毁对象之前执行。比如DataSource的配置中一般需要指定destroy-method='close'。
    • scope:指定对象的作用范围:
      • singleton:默认值,单例(在整个容器中只有一个对象)
      • prototype:多例的,每次访问对象时,都会重新创建对象实例。
DI配置

依赖注入可通过构造函数注入,setter方法注入。
构造函数注入,该方式需要满足指定参数的构造 :

    <bean id="person" class="com.pillow.domain.Person"
          init-method="doInit" destroy-method="doDestroy" scope="prototype">

        <constructor-arg name="id" value="2"/>
        <constructor-arg name="name" value="pillow"/>

    </bean>

setter方式注入一:手动装配(xml方式):
须配置bean标签的子标签property,bean对象中需存在setter方法。

    <bean id="person" class="com.pillow.domain.Person"
          init-method="doInit" destroy-method="doDestroy" scope="prototype">

        <property name="name" value="张飞"/>
    </bean>

2. Spring IoC/DI 基于xml和注解混合的使用

IoC配置

第一步:在spring配置文件中配置context:component-scan标签,指定扫描的范围。带有第二步注解的类都能被扫描到。

<context:component-scan base-package="com.pillow.service"></context:component-scan>

第二步:在类上加上注解@Component,或者它衍生的注解@Controller,@Service,@Repository。这四个注解的作用都是一样的,只是编码时用于区分层次。

@Component 注解

这个注解的作用就是把该类交给Spring管理,相当于在xml中配置了一个bean。
属性value:指定bean的id,如果不指定value属性,默认bean的id是当前类的类名,首字母小写。‘’

DI配置

setter方式注入二: 自动装配(注解方式)

  • @Autowired:从Spring容器中根据Bean的类型(Class)获取实例,将找到的实例装配给另一个实例的属性。注意:一个Java类型(Class)在同一个Spring容器中只能有一个实例。
  • @Resource:从Spring容器中根据Bean的名称(name)获取实例,然后进行赋值
  • @Inject:根据类型进行自动装配,如果需要根据名称则需要配合@named。可以用作在变量、setter方法、构造函数上。

3. Spring IoC/DI 基于纯注解的使用

Spring 纯注解的使用和SpringBoot是无缝衔接的,有些在Springboot中经常用到的注解,实际上是Spring的,比如@Configration,@ComponentScan等。

@Configration 注解

相当于Spring的xml配置文件,可替换掉使用xml方式的配置文件。
配置类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean对象,初始化Spring容器。

@Configuration 
public class SpringConfiguration {
    
    // Spring容器初始化时,会调起配置类的无参构造器
    public SpringConfiguration() {
        System.out.println("spring容器初始化....");
    }
}

@Bean 注解

相当于<bean>标签,作用是注册bean对象,用来配置非自定义的bean,比如DruidDataSource,SqlSessionFactory,实际也可以用于自定义bean。
@Bean标注在方法上,将该方法返回的实例交给Spring管理

    @Bean(name = "common")
    @Scope("prototype")
    public CommonProperties constantsProperties() {
        return new CommonProperties();
    }

属性 name:给当前@Bean注解方法创建的对象指定一个名称,注入时可根据该名称注入。如果不指定,默认与注解的方法名相同。
@Bean注解默认作用域为单例singleton,可通过@Scope("prototype")设置为多例。

@ComponentScan 注解

相当于<context:component-scan>标签,用于扫描@Component、@Controller等注解的类。
该注解贴在类上面,一般是和@Configration注解一起使用。

@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
public class SpringConfiguration {

    // Spring容器初始化时,会调起配置类的无参构造器
    public SpringConfiguration() {
        System.out.println("spring容器初始化....");
    }

}

属性:
basePackages:用于指定要扫描的包。如果不指定,则默认扫同包名下的注解。
value:和basePackages一样。

@PropertySource 注解

相当于<context:property-placeholder>注解,编写类上,作用是加载properties配置文件。

@Configuration
@PropertySource("classpath:config.properties")
public class ConfigProperties {
    @Value("${baseUrl}")
    private String baseUrl;
}

属性 value[]:用于指定properties文件路径,如果是在类路径下,需要指定classpath。

@Import 注解

相当于spring配置文件中的<import>标签,用于组合多个配置类,可以不用再写@Configration注解。实际上写上也没关系,Spring可以同时加载多个@Configration注解。

@Configuration
@ComponentScan(basePackages = "com.xmotor.gzyz")
@Import({JdbcConfiguration.class})
public class SpringConfiguration {
    // Spring容器初始化时,会调起配置类的无参构造器
    public SpringConfiguration() {
        System.out.println("spring容器初始化....");
    }
}

@PropertySource("classpath:config.properties")
public class JdbcConfiguration {
}

属性 value:用来指定其他配置类的字节码文件,可以指定多个。

创建纯注解方式上下文容器
AnnotationConfigApplicationContext applicationContext = 
                new AnnotationConfigApplicationContext(SpringConfiguration.class);

        ConfigProperties configProperties = applicationContext.getBean(ConfigProperties.class);

Spring的两大特性之二: AOP

AOP是一种编程思想,跟IoC类型,并不是一种技术,Spring AOP是实现了这种思想的一种技术。
最大作用就是将系统逻辑(权限验证,事务管理,记录操作日志等)和业务逻辑隔离开,提高了程序的可重用性,降低耦合度。在Spring AOP中,它甚至将事务逻辑都帮你写好了。

AOP术语:Joinpoint,Pointcut,Advice,Aspect

Joinpoint:连接点,被拦截到需要被增强的方法。
Pointcut:切入点,多个连接点的集合,一般指哪些包中的哪些类。
Advice:通知/增强,拦截到Joinpoint之后要做的事情,可以分为前置增强,后置增强,异常增强,最终增强,环绕增强,就是在执行Joinpoint的前后左右添加新的增强功能。
Aspect:切面:Pointcut + Advice,哪些地方,在什么时候,做什么增强。
Target:目标对象,代理的目标对象
Weaving:织入,是指把增强应用到目标对象来创建新的代理对象的过程。
Proxy:代理,一个类被AOP织入增强后,产生的代理类。
总结:Joinpoint,某一个方法,多个Joinpoint集合成Pointcut。Pointcut + Advice 组合成Aspect。把Advice 织入(Weaving)到Target中,得出Proxy对象。

静态代理和动态代理

静态代理 :在Spring容器启动的时候就已经确定好了代理类,只能服务于一种类型的对象。
动态代理 :在程序运行期间由JVM通过反射产生,不存在字节码文件。代理类和委托类的关系是在程序运行时确定。

1. Spring 基于AspectJ的AOP使用之xml方式

首先要编写增强类,也就是要做什么增强。在一个增强类里面可以写多个增强方法,但是增强的时候只会选择其中一个执行。

public class MyAdvice {
    public void log(){
        System.out.println("记录日志....");
    }
}

其次配置增强,将增强类交给Spring容器管理

    <bean name="myAdvice" class="com.pillow.advice.MyAdvice"></bean>

最后配置aop切面

    <aop:config>
        <!-- ref: 配置通知、增强 -->
        <aop:aspect ref="myAdvice">
            <aop:before method="log" pointcut="execution(void com.pillow.service.PersonServiceImpl.save(*))"></aop:before>
        </aop:aspect>
    </aop:config>

切入点表达式:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
返回值类型:可以用 * 通配符
包名:可以用 * 代替一级包的通配符,可以用 .. 省略中间包名
类名:可以用 * 通配符,也可以写成 *ServiceImpl
方法名:可以用 * 通配符,也可以写成 *SaveImpl
参数:可以用 * 通配符,多个参数,可以使用 .. 代替
通知:同一个方法可以配置多个通知

通知类型:前置通知,后置通知,最终通知,环绕通知,异常抛出通知

前置通知
执行时机:目标对象方法之前执行
配置文件:<aop:before ...>

后置通知
执行时机:目标对象方法之后之后,抛出异常则不会执行
配置文件:<aop:after-returning ...>

最终通知
执行时机:目标对象方法之后执行通知,有没有异常都会执行,顺序在后置通知之后
配置文件:<aop:after ...>

环绕通知
执行时机:目标对象方法之前和之后都会执行,包含before和after-returning
配置文件:<aop:around ...>

异常抛出通知
执行时机:在抛出异常时通知
配置文件:<aop:after-throwing ...>

2. Spring 基于AspectJ的AOP使用之xml和注解混合方式

编写切面类,注意不是通知类,是切面类,包含了通知和切入点。

@Component
@Aspect
public class MyAspect {
    @Before(value = "execution(* *..*.*save(*))")
    public void beforce() {
        System.out.println("前置通知。。。");
    }
    @After(value = "execution(* *..*.*save(*))")
    public void after() {
        System.out.println("最终通知。。。");
    }
    @AfterReturning(value = "execution(* *..*.*save(*))")
    public void afterReturn() {
        System.out.println("后置通知。。。");
    }
}

@Component 注解 再配置组件扫描,将该切面类交给Spring管理。

    <context:component-scan base-package="com.pillow.*"></context:component-scan>

最后开启AOP自动代理

    <!-- AOP基于注解的配置,开启自动代理 -->
    <aop:aspectj-autoproxy/>
2. Spring 基于AspectJ的AOP使用之纯注解方式

纯注解方式重点在于转换混合方式配置组件扫描和开启自动代理两种xml配置。

@Configuration
@ComponentScan(basePackages = "com.pillow")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}

@EnableAspectJAutoProxy 开启自动代理

Spring声明式事务

事务,真正的实现是由底层的数据库进行实现的,而Spring是对数据库底层进行一些简单的设置。配置好了以后,Spring会读取配置,传递给数据库,使数据库进行一些响应的改变。

需要引入依赖包 spring-tx

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.3.14.RELEASE</version>
        </dependency>

事务的四大特性:一致性,原子性,隔离性,持久性

隔离性将会导致一系列并发问题
脏读:事务A读取到了事务B未提交的数据,事务B回滚后,事务A的数据就是错的。
不可重复度:事务A第一次读取数据时,事务B未开始。事务B执行了update操作,并提交的事务,A再读取时,发现两次读取的数据不一致。
幻读:事务A第一次读取时,事务B未开始。事务B执行了insert,delete操作,并提交的事务,A再读取时,发现两次读取的数据不一致。

针对不同的并发问题制定了不同的隔离级别
read uncommitted:读未提交,不能解决任何问题
read committed:读已提交,可以解决脏读。Oracle默认级别。
repeatable read:可重复度,可以解决脏读和不可重复读。Mysql默认级别,Mysql中幻读也不会出现。
Serializable:串行化,事务提交了一个才进行下一个,可以解决所有问题,性能也最差。

1. Spring声明式事务 基于xml方式的应用
    <!-- 配置事务三步骤:1. 配置平台事务管理器,注入数据源 -->
    <bean id="txManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置事务三步骤:2. 配置通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="list*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="select*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- 配置事务三步骤:3. 配置切面 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.pillow.service.*ServiceImpl.*(..))" id="txPoint" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" />
    </aop:config>
2. Spring声明式事务 基于xml和注解混合的使用

在service类上或者方法上加注解:@Transactional
@Transactional
加在类上,表明该类所有方法都被事务管理
加在方法上,表明仅仅该方法被事务管理

    <!-- 配置事务 -->
    <bean id="txManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 开启事务注解 -->
    <tx:annotation-driven transaction-manager="txManager"/>
3. Spring声明式事务 基于纯注解的使用

纯注解方式在于解决混合用法的开启事务注解的xml处理。
@EnableTransactionManagement 贴在配置类上即可。

@ComponentScan(basePackages = "com.pillow")
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfiguration {
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335