zoukankan      html  css  js  c++  java
  • 抛弃配置后的Spring终极教程

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

    一:前言

    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增强处理

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

    注解 解释
    @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.开启事务<tx:annotation-driven transaction-Manager="" />

    	<!-- 初始化 数据源 -->
        <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~~

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

    版权声明

    【本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究。若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!】

  • 相关阅读:
    javascript获取xml节点的最大值
    iis 不能浏览aspx页面
    批量替换文件夹里面的文本文件的指定字符
    select update delete
    SQL IAM的理解
    数据库的页构成
    sqltype IsDBNull
    MSSQL优化教程之1.4 其他几种类型的页面
    SqlDataAdapter
    行状态,行版本
  • 原文地址:https://www.cnblogs.com/onblog/p/13035763.html
Copyright © 2011-2022 走看看