一:前言

Spring 有XML配置和注解两种版本,我个人非常喜欢使用注解,相当热衷Spring boot!

对于Spring,核心就是IOC容器,这个容器说白了就是把你放在里面的对象(Bean)进行统一管理,你不用考虑对象如何创建如何销毁,从这方面来说,所谓的控制反转就是获取对象的方式被反转了。

既然你都把对象交给人家Spring管理了,那你需要的时候不得给人家要呀。这就是依赖注入(DI)!再想下,我们在传入一个参数的时候除了在构造方法中就是在setter方法中,换个好听的名字就是构造注入设值注入

至于AOP(面向切面),这玩意我举个例子说下,比如你写了个方法用来做一些事情,但这个事情要求登录用户才能做,你就可以在这个方法执行前验证一下,执行后记录下操作日志,把前后的这些与业务逻辑无关的代码抽取出来放一个类里,这个类就是切面(Aspect),这个被环绕的方法就是切点(Pointcut),你所做的执行前执行后的这些方法统一叫做增强处理(Advice)。

二:配置

推荐使用IDEA快速构建Spring项目!

抛弃配置第一步,快速定义application.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启自动扫描-->
    <context:component-scan base-package="cn.zyzpp"/>

</beans>

作用

  1. 默认扫描Spring提供的@Component, @Repository, @Service,@Controller、@RestController、@ControllerAdvice和@Configuration等注解的类。
  2. 默认开启annotation注解配置,激活@Required,@Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext, @PersistenceUnit组件类中的注释。

三:依赖注入

搜索Bean类

Spring提供如下几个Annotation来标注Spring Bean:

  • @Component: 标注一个普通的Spring Bean类
  • @Controller: 标注一个控制器组件类
  • @Service: 标注一个业务逻辑组件类
  • @Repository: 标注一个DAO组件类

使用@Resource配置依赖

使用@Resource<property.../>元素的ref属性有相同的效果。

@Resource不仅可以修饰setter方法,也可以直接修饰实例变量,如果使用@Resource修饰实例变量将会更加简单,此时Spring将会直接使用JavaEE规范的Field注入,此时连setter方法都可以不要。

@Resource位于javax.annotation包下,是来自JavaEE规范的一个Annotation

@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

使用@Autowired配置依赖

Spring提供了@Autowired注解来指定自动装配,@Autowired可以修饰setter方法、普通方法、实例变量和构造器等。当使用@Autowired标注setter方法时,默认采用byType自动装配策略。

默认情况下@Autowired(required = true),意思是要求依赖对象必须存在。

搭配@Qualifier指定BeanId

在这种策略下,符合自动装配类型的候选Bean实例常常有多个,这个时候就可能引起异常,为了实现精确的自动装配,Spring提供了@Qualifier注解,通过使用@Qualifier,允许根据Bean的id来执行自动装配。

    @Autowired
    @Qualifier(value = "user")
    User user;

四:定义Bean

@Configuration配置

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

 @Configuration
 public class AppConfig {

     @Autowired
     Environment env;

     @Bean
     public MyBean myBean() {
         MyBean myBean = new MyBean();
         myBean.setName(env.getProperty("bean.name"));
         return myBean;
     }
 }

@Bean定义

@Bean 与 Component 的区别是用在方法上 ,而不是类上。类似于XML中 <bean/> 。默认Bean名称为方法名。

如果需要显式命名,可以使用name属性(或value属性)。还要注意,name接受字符串数组,允许对单个bean使用多个名称(即主bean名称加上一个或多个别名)。

可以定义Bean的初始化方法与关闭应用程序时调用的方法。

     @Bean({"b1", "b2"},initMethod = "",destroyMethod = "") // 有b1,b2 bean,但没有myBean
     public MyBean myBean() {
         return new MyBean;
     }

@Scope作用域

@Scope可以搭配@Component等或者@Bean注解定义Bean的作用域。默认作用域 Singleton。

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
global-session 一般用于Portlet应用环境,改作用于仅适用于WebApplicationContext环境

示例

@Component
@Scope(value = "singleton")
public class HelloWord {

@Lazy懒加载

@Lazy搭配@Component或@Bean使用,作用是延迟初始化 bean,告诉IOC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。

@Component
@Lazy
public class HelloWord {

@PostConstruct和@PreDestroy定制生命周期行为

@PostConstruct@PreDestroy同样位于javax.annotation包下,也是来自JavaEE规范的两个Annotation,Spring直接借鉴了它们,用于定制Spring容器中Bean的生命周期行为。

它们都用于修饰方法,无须任何属性。

其中前者修饰的方法时Bean的初始化方法;而后者修饰的方法时Bean销毁之前的方法。

五:使用AOP

AOP(Aspect Orient Programming)也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。

AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。

配置开启AOP

  <!--启动@AspectJ支持-->
  <aop:aspectj-autoproxy/>

  <!--指定自动搜索Bean组件、自动搜索切面类-->
  <context:component-scan base-package="cn.zyzpp">
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
  </context:component-scan>

@Aspect定义切面

@Aspect是不能被扫描到的,所以需要配合@Component注解。@Aspect标识自己是一个切面,将自己从自动代理中删除。

@Aspect
public class AspectModule {
}

@Pointcut定义切点

注意切点通过一个普通方法来定义,返回类型必须为void。支持execution表达式,within表达式,例如:

@Pointcut("execution(* cn.zyzpp.demo.*Word.*(..))")
private void businessService() {}  // signature

第一个*表示任意返回类型,以.号进行划分,接下来是包名,类名,类名后是方法名,方法名跟括号,括号内是参数,两个点..意思是任意参数。

其它用法参考 AOP表达式用法
AspectJ语法详解:execution,within,this,@Aspect

Advice增强处理

注解 解释
@Before 前置通知:目标方法执行之前执行以下方法体的内容
@After 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
@AfterReturning 返回通知:目标方法正常执行完毕时执行以下代码
@AfterThrowing 异常通知:目标方法发生异常的时候执行以下代码
@Around 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码

执行顺序

JoinPoint 获取目标方法

访问目标方法最简单的做法是定义增强处理方法时,将第一个参数定义为JoinPoint类型,当该增强处理方法被调用时,该JoinPoint参数就代表了织入增强处理的连接点。JoinPoint里包含了如下几个常用的方法:

  • Object[] getArgs:返回目标方法的参数
  • Signature getSignature:返回目标方法的签名
  • Object getTarget:返回被织入增强处理的目标对象
  • Object getThis:返回AOP框架为目标对象生成的代理对象

自定义注解向Advice传参

@Retention定义自定义注解的生命周期

  1. RetentionPolicy.SOURCE:注解只作用在Java源文件(.java文件) ,不会被编译为Class字节码文件。
  2. RetentionPolicy.CLASS:注解保留到Class文件,在JVM加载Class文件时候被遗弃,为默认生命周期
  3. RetentionPolicy.RUNTIME:注解在源文件与Class中存在,可被JVM加载,在运行时动态获取。

@Target定义注解的作用位置

  • ElementType.TYPE //接口、类、枚举、注解
  • ElementType.FIELD //字段、枚举的常量
  • ElementType.METHOD //方法
  • ElementType.PARAMETER //方法参数
  • ElementType.CONSTRUCTOR //构造函数
  • ElementType.LOCAL_VARIABLE //局部变量
  • ElementType.ANNOTATION_TYPE //注解
  • ElementType.PACKAGE ///包

@Documented,@Inherited 忽略即可!

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Annota {
    String value();
}

在目标方法使用此注解

    @Annota("Mark")
    public void say(String name){
        ....
    }

完整AOP示例

@Component
@Aspect
public class MyAspect {
    //声明切点
    @Pointcut("execution(* cn.zyzpp.demo.*Word.*(..))")
    public void pointcut(){}

    //声明切点
    @Pointcut("within(cn.zyzpp.demo.*)")
    public void bizPointcut(){}

    //前置通知:目标方法执行之前执行以下方法体的内容
    @Before("pointcut()")
    public void before(){
        System.out.println("    before");
    }

    //前置通知:获取注解,给Advice传递参数
    @Before("pointcut() && @annotation(annota)")
    public void beforeWithAnnotaion(Annota annota) {
        System.out.println("BeforeWithAnnotation: " + annota.value());
    }

    //前置通知:获取参数
    @Before("pointcut()")
    public void beforeArgs(JoinPoint joinPoint){
        for (Object object:joinPoint.getArgs()){
            System.out.println("    before args: "+object.toString());
        }
    }

    //前置通知:获取对象
    @Before("pointcut() && args(arg)")
    public void beforeWithPar(Object arg){
        System.out.println("    before obj: "+arg.toString());
    }

    //后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
    @After("pointcut()")
    public void after() {
        System.out.println("    After");
    }

    //返回通知::目标方法正常执行完毕时执行以下代码
    @AfterReturning(pointcut="bizPointcut()" ,returning="retrunValue")
    public void afterReturning(Object retrunValue){
        System.out.println("AfterReturning: "+retrunValue);
    }

    //异常通知:目标方法发生异常的时候执行以下代码
    @AfterThrowing(pointcut="pointcut()", throwing="e")
    public void afterThrowing(RuntimeException e) {
        System.out.println(" AfterThrowing: " + e.getMessage());
    }

    //环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("  Around: start");
        Object obj = pjp.proceed();
        System.out.println("  Around: end, return: "+obj);
        return obj;
    }

}

六:事物管理

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID

  • 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
  • 一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
  • 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
  • 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

编程式 vs. 声明式

Spring 支持两种类型的事务管理:

  • 编程式事务管理:这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
  • 声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。

声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。

基于注解的方式

1.开启事务

    <!-- 初始化 数据源 -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/TEST" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
    </bean>
    <!-- 事务管理器配置 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 使用annotation注解定义事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

2.service类@Transactional(name=value)

@Transactional参数

参数名称 功能描述
readOnly 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)
rollbackFor 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})
noRollbackForClassName 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")指定多个异常类名称:@Transactional(noRollbackForClassName={"RuntimeException","Exception"})
propagation 该属性用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout 该属性用于设置事务的超时秒数,默认值为-1表示永不超时

尾声

本文辛苦码字半天,顺带复习下Spring的知识,流程图用的word~~

本文已授权微信公众号“后端技术精选”独家发布

抛弃配置后的Spring终极教程的更多相关文章

  1. Spring应用教程-3 依赖关系配置

    注:组件与组件之间的耦合,采用依赖注入管理,但普通的JavaBean属性值,应直接在代码中设置. 1. 注入其他Bean的属性值 我们分析一下,Bean_A的一个属性要依赖Bean_B的一个属性值.这 ...

  2. 2017.3.31 spring mvc教程(二)核心流程及配置详解

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  3. 【Spring Framework】Spring入门教程(二)基于xml配置对象容器

    基于xml配置对象容器--xml 标签说明 alias标签 作用:为已配置的bean设置别名 --applicationContext.xml配置文件 <?xml version="1 ...

  4. spring boot 教程(三)配置详解

    在大部分情况下,我们不需要做太多的配置就能够让spring boot正常运行.在一些特殊的情况下,我们需要做修改一些配置,或者需要有自己的配置属性. Spring Boot 支持多种外部配置方式 这些 ...

  5. Spring Boot教程(三十二)多数据源配置与使用

    之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源.在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.propertie ...

  6. Spring Boot教程(三十二)多数据源配置与使用(1)

    之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源.在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.propertie ...

  7. 【Spring Framework】Spring入门教程(三)使用注解配置

    本文主要介绍四个方面: (1) 注解版本IOC和DI (2) Spring纯注解 (3) Spring测试 (4) SpringJDBC - Spring对数据库的操作 使用注解配置Spring入门 ...

  8. Spring MVC 教程,快速入门,深入分析

    http://elf8848.iteye.com/blog/875830/ Spring MVC 教程,快速入门,深入分析 博客分类: SPRING Spring MVC 教程快速入门  资源下载: ...

  9. Spring Security教程系列(一)基础篇-1

    第 1 章 一个简单的HelloWorld 第 1 章 一个简单的HelloWorld Spring Security中可以使用Acegi-1.x时代的普通配置方式,也可以使用从2.0时代才出现的命名 ...

随机推荐

  1. 关于最新笔记本机型预装win8如何更换为win7的解决办法

    关于最新笔记本机型预装win8如何更换为win7的解决办法 目前新出的很多机型出厂自带的都是win8系统,可能有些人用不习惯,想更换为win7系统,但是由于这些机型主板都采用UEFI这种接口(硬盘分区 ...

  2. Microsoft Teams 集成 (协作, 沟通 和 行为)

    Microsoft Teams 集成 (协作, 沟通 和 行为) 概述 Microsoft Teams是在Office 365中以chat为中心的工作空间.软件开发团队可以快速获得在一个专门的团队协作 ...

  3. java一个数分解的质因数java

    import java.util.Scanner; /** * Created by Admin on 2017/3/18. */ public class Test01 { public stati ...

  4. webApi添加视图出现/Index.cshtml”处的视图必须派生自 WebViewPage 或 WebViewPage<TModel>。

    是因为webApi Views文件夹下缺乏web.config文件,从mvc项目相同目录拷贝一个web.Config文件放入 删除多余的namespaces 即可 web.config配置如下: &l ...

  5. oracle 分组函数执行分析

    先上例了: select job as "JOB1", avg(sal) as "avg sal" from scott.emp group by " ...

  6. LeetCode算法题-Excel Sheet Column Title(Java实现)

    这是悦乐书的第180次更新,第182篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第39题(顺位题号是168).给定正整数,返回Excel工作表中显示的相应列标题.例如: ...

  7. LeetCode算法题-Intersection of Two Linked Lists(Java实现)

    这是悦乐书的第178次更新,第180篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第37题(顺位题号是160).编写程序以找到两个单链表交叉的节点.例如: 以下两个链表: ...

  8. February 11th, 2018 Week 7th Sunday

    Grasp all, lose all. 欲尽得,必尽失. Not to be greedy and not to try to get everything. Our time, energy an ...

  9. vue原理简介

    写vue也有一段时间了,对vue的底层原理虽然有一些了解,这里总结一下. vue.js中有两个核心功能:响应式数据绑定,组件系统.主流的mvc框架都实现了单向数据绑定,而双向绑定无非是在单向绑定基础上 ...

  10. Java教程01-基础语法

    目录 1. 基本概念 1.1. 环境变量 Path环境变量的作用->寻找命令 classpath变量的作用->寻找类文件 1.2. JDK里面有什么? 1.3. 什么是JRE? 2. Ja ...