Spring 理论与用例实践

本文深入介绍了Spring框架的核心概念,包括XML、注解等多种配置方式,详细阐述了控制反转IOC和依赖注入DI的原理与实现。此外,还探讨了Spring的AOP切面编程,包括前置、后置、异常和环绕通知,以及如何通过注解和Schema形式实现AOP。通过本文,读者将能够掌握Spring的基础知识并为后续的Spring生态技术学习奠定基础。
摘要由CSDN通过智能技术生成

Spring 是目前各个企业必备的技术,也是各个框架技术需要整合的组件。Spring 生态技术目前已经是互联网开发中必不可少的一个技术栈。

相信读者能通过本文的学习,打下一定的 Spring 技能基础,为后面 Spring 生态的扩展技术做充足的准备。

本文大致包含了以下几方面的讲解:

Spring 多种开发方式:XML 方式、注解方式、Schema 方式、P 命名空间方式等;
Spring 核心基石:控制反转 IoC/依赖注入 DI ;
Spring 切面编程:AOP。
Spring 起源于 2003 年,Spring 技术栈两大基石控制反转 IOC(依赖注入 DI)和面向方面编程 AOP。 开发 spring 应用必须使用的 jar 包: Spring-aop.jar 开发 Spring AOP 特性; Spring-beans.jar 处理 bean; Spring-context.jar 处理上下文; Spring-core.jar Spring 核心包; Spring-expression.jar 表达式 EL/jtsl; 以及三方提供的日志 jar 包,Commons-logging.jar。 1、Spring 核心基石:控制反转 IOC/依赖注入 DI ; Spring IOC 发展史:IOC 控制反转也可以称之为 DI 依赖注入。IOC 将创建对象、属性值的方式进行了反转,从通过 new setXxx()方式反转为从 Spring IOC 容器通过 getBean()方式;依赖注入 DI,将属性值注入给属性,将属性注入给 bean,将 bean 注入给 IOC 容器。 控制反转 IOC 和依赖注入 DI 要获取对象都可以直接从 Spring IOC 容器中获取,而不需要自己操作。 控制反转 IOC 分为两步,第一步先给 Spring IOC 容器存放对象并赋值,第二步从 Spring IOC 容器中获取对象。 控制反转 IOC 容器赋值,如果简单数据类型(8 个基本数据类型+String 类型),用 value 赋值;如果是对象类型用 ref=”需要引用的 id 值”赋值,因此实现了对象与对象之间的依赖关系。 依赖注入的三种方式: 1)set 方式的依赖注入,赋值默认使用的是 set 方法。依赖注入底层是通过反射实现的,如下例所示:

<bean id="teacher" class="entity.Teacher">
     <property name="name" value="liwen"></property>
     <property name="age" value="35"></property>
</bean>
<bean id="course" class="entity.Course" autowire="byName">
     <property name="cName" value="java"></property>
     <property name="teacher" ref="teacher"></property>
</bean>

2)构造器注入,通过构造方法赋值,如下例所示:


需要注意如果的顺序与构造方法参数的顺序不一致,则需通过 type、name、index 指定。 3)P 命名空间注入,如下例所示:xmlns:p=http://www.springframework.org/schema/p 简单类型:p:属性名=“属性值”; 引用类型(除了 String 外):p:属性名-ref=“引用的 ID”。

注入各种集合类型:包括 list、map、set、array、properties; set、list、array 各自都有自己的标签、、,如下例所示:

<bean id="collectionDemo" class="entity.AllCollection">    
      <property name="list">
        <list>
           <value>足球</value>
           <value>篮球</value>
        </list>
      </property>
      <property name="array">
         <list>
           <value>足球 1</value>
           <value>篮球 1</value>
        </list>
      </property> 
      <property name="set">
        <set>
           <value>足球 2</value>
           <value>篮球 2</value>
        </set>
      </property>
      <property name="map">
        <map>
           <entry>
             <key>
                <value>foot3</value>
             </key>
             <value>足球 3</value>
           </entry>
           <entry>
             <key>
                <value>basket3</value>
             </key>
             <value>篮球 3</value>
           </entry>
        </map>
      </property>
      <property name="props">
          <props>
             <prop key="foot4">足球 4</prop>
             <prop key="basket4">篮球 4</prop>
          </props>
      </property>
</bean>

通过构造器方式注入,在 IOC 容器定义 bean 的前提,该 bean 的类必须提供了无参构造。赋值:



自动装配(只适用引用类型 ref):约定优于配置: (byName 本质是 byID) byName:课程 Course 类中有一个 ref 属性 teacher(属性名),并且该 IOC 容器恰好有一个 bean 的 ID 值等于该 Course 类的属性名 teacher,如下例所示:

byType:其他 bean 的类型,是否与该 Course 类 ref 属性类型一致,此种方式必须满足,当前 IOC 容器中只能有一个 bean 满足条件。 constructor:其他 bean 的类型,是否与该 Course 类的构造方法参数类型一致.此种方式必须满足,当前 IOC 容器中只能有一个 bean 满足条件。 可以在头文件中,一次性将该 IOC 容器所有 bean 统一设置为自动装配:
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd" default-autowire="byName">

自动装配虽然可以减少代码量,但是会降低程序可读性,使用时需要谨慎。 使用注解定义 bean:通过注解的形式,将 bean 以及属性值放入 IOC 容器中。

<context:component-scan base-package=“org.dao”></context:component-scan>
Spring 在启动的时候会根据扫描的 base-package="org.dao"在该包中扫描所有类,查找这些类是否有注解@Component(“studentDao”),如果有就将该类加入 IOC 容器。 dao 层注解用@Repository; service 层注解用@Service; 控制器层注解用@Controller。

2、SpringAOP 切面编程 AOP 相关的一些名词:切面(Aspect):一个横切功能的模块化,这个功能可能会横切多个对象(业务)。例如:aMethod()方法就是一个“切面”,它横切到多个业务之中。 切入点(Pointcut):可以插入“横切逻辑(如 aMethod())”的方法。例如,“调用 add()”就是一个切点。 通知(Advice): 前置通知(Before Advice),在切入点 add()方法执行之前,插入通知。 后置通知(After Returning Advice),在切入点 add()方法执行完毕之后,插入通知。 异常通知(After Throwing Advice),当切入点 add()方法抛出异常时,插入通知。 最终通知(After Finally Advice),当切入点 add()方法执行完毕时,插入通知。(不论是正常返回还是异常退出)。 环绕通知(Around Advice),可以贯穿切入点 add()方法执行的整个过程。 把一个普通的类变成一个具有特定功能的类,一种方法继承,第二种方法实现接口,第三种方法加注解,第四种方法通过配置。 把类变成通知,通过实现接口的方式: xml 方式的通知类型: 1)前置通知 需要实现的接口:org.springframework.aop.MethodBeforeAdvice 接口中的方法:before() 执行时机:目标方法执行前 2)后置通知 需要实现的接口:org.springframework.aop.AfterReturningAdvice 接口中的方法:afterReturning() 执行时机:目标方法执行后 3)异常通知 需要实现的接口:org.springframework.aop.ThrowsAdvice 接口中的方法:无 执行时机:目标方法发生异常时 4)环绕通知 需要实现的接口:org.aopalliance.intercept.MethodInterceptor 接口中的方法:invoke() 执行时机:拦截对目标方法调用,即调用目标方法的整个过程

execution 表达式和前置通知: 需要导入 jar 包:aopalliance-1.0.jar、aspectjweaver.jar 表达式 expression 常见事例: 1)public boolean addStudent(org.entity.Student) 所有返回类型为 boolean,参数类型为 org.entity.Student 的 addStudent()的方法 2)public boolean org.service.StudentService.addStudent(org.entity.Student) org.service.StudentService 类或接口中的 addStudent()的方法,并且返回类型为参数类型为 boolean,参数类型为 org.entity.Student 3)public * addStudent(org.entity.Student) ""代表任意返回类型 4)public void (org.entity.Student) ""代表任意方法名 5)public void addStudent(…) "…"代表任意参数列表 6) org.service.StudentService..(…) org.service.StudentService包中,包含的所有方法(不包含子包中的方法) 7)* org.service.StudentService….(…) org.service.StudentService包中,包含的所有方法(包含子包中的方法) 后置通知、异常通知: 1)后置通知类实现接口

package org.aop;
import org.springframework.aop.AfterReturningAdvice;
public class LogAfter implements AfterReturningAdvice{
      @Override
      public  void afterReturning(java.lang.Object arg0, java.lang.reflect.Method arg1, java.lang.Object[] arg2, java.lang.Object arg3) throws java.lang.Throwable{
          System.out.println("后置通知");
    }
}

2)业务类中实现业务方法

package org.service.impl;
    import org.dao.StudentDao;
    import org.dao.impl.StudentDaoImpl;
    import org.service.StudentService;
    import entity.Student;
    public class StudentServiceImpl implements StudentService{
        StudentDao studentDao=new StudentDaoImpl();
        public StudentDao getStudentDao() {
            return studentDao;
        }
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        public void addStudent(Student student) {
            studentDao.addStudent(student);
        }
    }

3)配置

<!-- 将业务类 addStudent()方法所在类纳入 SpingIOC 容器 -->
<bean id="studentService" class="org.service.StudentServiceImpl">
  <property name="studentDao" ref="studentDao"></property>
</bean>
 <!-- 将后置通知类纳入 SpingIOC 容器-->
<bean id="logAfter" class="org.aop.LogAfter"></bean> 
<aop:config>
  <!-- 配置切入点 -->
  <aop:pointcut expression="execution(public void org.service.impl.StudentServiceImpl.addStudent(entity.Student))" id="pointcut2"/> 
<!-- advisor 相当于连接切入点和切面的线 -->
  <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut2"/>
</aop:config>

4)异常通知 根据异常通知接口的定义可以发现,异常通知的实现类,必须编写以下方法:public void afterThrowing([Method, args, target], ThrowableSubclass) 5) 环绕通知 在目标方法的前后、异常发生时、最终等各个地方都可以进行的通知,最强大的一个通知;可以获取目标方法的全部控制权(目标方法是否执行、执行之前、执行之后、参数、返回值等) 在使用环绕通知时,目标方法的一切信息都可 invocation 参数获取到,环绕通知底层通过拦截器实现。

基于注解形式的 AOP 实现:

 <!-- 开启注解对 AOP 的支持 -->
  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  <!-- 类包 org.aop 放入扫描器 -->
 <context:component-scan base-package="org.aop"></context:component-scan>
package org.aop;
            import org.aspectj.lang.annotation.AfterReturning;
            import org.aspectj.lang.annotation.Aspect;
            import org.aspectj.lang.annotation.Before;
            import org.springframework.stereotype.Component;

            @Component("logAspectAnnotation") //将该类纳入 SpringIOC 容器
            @Aspect //声明该类是一个通知
            public class LogAspectAnnotation {
                //定义切点
                @Before("execution(public * addStudent(..))")
                public void myBefore(){
                    System.out.println("注解形式前置通知");
                }
                @AfterReturning("execution(public * addStudent(..))")
                public void myAfter(){
                    System.out.println("注解形式后置通知");
                }
            }

扫描器会将指定包中的@Component、@Service、@Respository、@Controller 修饰的类产生的对象,增加到 IOC 容器中。 @Aspect 不需要加入扫描器,只需要开启即可,如下例所示:

aop:aspectj-autoproxy</aop:aspectj-autoproxy>
通过注解形式实现的 AOP,如果想获取目标对象的参数,则需要使用对象:JoinPoint 注解形式返回值: 1)声明返回值的参数名

@AfterReturning(pointcut=“execution(public * addStudent(…))”,returning=“returningValue”)
public void myAfter(JoinPoint jp,Object returningValue){
System.out.println(“注解形式后置通知。目标对象:”+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+Arrays.toString(jp.getArgs())+",返回值:"+returningValue);
}
基于 Schema 形式的 AOP 实现: 1)类似于实现接口的方式 2)编写一个普通类

public class LogBeforce
将该类通过配置转为一个通知,如果要获取目标对象信息:JoinPoint 适用于注解和 Schema。

package org.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class LogSchema {
     //JoinPoint 适用于注解和 Schema
    public  void afterReturning(JoinPoint jp,Object returnValue) throws java.lang.Throwable{
          System.out.println("Schema 后置通知");
    }
     public  void before(){
          System.out.println("Schema 前置通知");
    }
     public  void myException(JoinPoint jp,NullPointerException e){
          System.out.println("Schema 异常通知");
    }
     //环绕通知返回目标方法的返回值
     public  Object myAround(ProceedingJoinPoint jp){
         Object result=null;
        //方法执行之前 前置通知
            System.out.println("Schema 方法执行之前 环绕前置通知");
            try {
                //方法执行时
                result=jp.proceed();
                //方法执行后 后置通知
                System.out.println("Schema 方法执行后 环绕后置通知");
            }catch(Throwable e) {
                //发生异常时 异常通知
                System.out.println("Schema 发生异常时 环绕异常通知");
            }
            return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草原印象

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值