SpringIOC和AOP简介以及基本使用

一、Spring 体系结构

Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入。下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍。

Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。

1、核心容器

核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:

  • spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。

  • spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。

  • context模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。

  • spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。

它们的完整依赖关系如下图所示:

SpringIOC和AOP简介以及基本使用_第1张图片

2、数据访问/集成

数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:

(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)

  • JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。

  • ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。

  • OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。

  • JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。

  • 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)

3、Web

Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:

  • Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。。

  • Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。

  • Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。

  • Web-Portlet 模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。

4、其他

还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:

  • AOP 模块提供了面向方面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。

  • Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。

  • Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。

  • Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

  • 测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。

Spring的核心是控制反转(IOC)和面向切面(AOP)

一、控制反转(IOC)

控制反转(IOCInversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的装配和管理。

IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖注入,应用广泛。

依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA 对 classB 有依赖。

IOC 的实现:

  • 依赖查找:DL ( Dependency Lookup ), 容器提供回调接口和上下文环境给组件。
  • 依赖注入:DI (Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。

依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持 POJO 之间依赖关系的管理。

依赖注入是目前最优秀的解耦方式。依赖注入让 Spring Bean 之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。

二、第一个Spring案例实现

1、添加依赖

 4.3.9.RELEASE
    2.3
 


    
      org.springframework
      spring-beans
      ${spring-version}
    
    
      org.springframework
      spring-context
      ${spring-version}
    
    
      org.springframework
      spring-core
      ${spring-version}
    
    
      org.springframework
      spring-expression
      ${spring-version}
    
    
    
      org.springframework
      spring-aop
      ${spring-version}
    

    
    
      org.apache.logging.log4j
      log4j-api
      ${log4j-api-version}
    
    
      org.apache.logging.log4j
      log4j-core
      ${log4j-api-version}
    
    
      org.apache.logging.log4j
      log4j-jcl
      ${log4j-api-version}
    

2、创键业务层

package io.spirng.service;

public interface SomeService {
    void doSome();
}

实现类

package io.spring.service.impl;

import io.spring.service.SomeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * 注解@Component,该注解的 value 属性用于指定该 bean 的 id 值
 * 外,Spring 还提供了 3 个功能基本和@Component 等效的注解:
 * @Repository 用于对 DAO 实现类进行注解
 * @Service 用于对 Service 实现类进行注解
 * @Controller 用于对 Controller 实现类进行注解
 * 之所以创建这三个功能与@Component 等效的注解,是为了以后对其进行功能上的扩展
 * 注解不指定 value 属性,bean 的 id 是类名的首字母小写
 */
@Service("someService")
public class SomeServiceImpl implements SomeService {
    /**
     * 需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。
     * 使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
     */
    @Value("张三")
    private String  name;

    private String address;

    @Value("北京")
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * @Autowired 该注解式通过类型注入的,同样可以在放在set方法上可以放在变量上,按类型注入
     * 如果要按名称注入可以配合@Qualifier("")使用
     *  @Autowired 的required 默认值为 true表示当匹配失败后,会终止程序运行。
     *  若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
     */
    @Autowired(required = false)
    @Qualifier("myOrder")
    private Order order;

    /**
     * JDK 注解@Resource 自动注入
     * Spring 提供了对jdk中@Resource注解的支持。@Resource注解既可以按名称匹配Bean,
     * 也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
     * @Resource 可在属性上,也可在 set 方法上。
     */
    @Resource(name = "friend")
    private Friend myFriend;


    public SomeServiceImpl(){
        System.out.println("调用了无参构造");
    }
    @Override
    public void doSome() {
        System.out.println("业务方法执行doSome...");
    }

    /**
     * Bean 的生命始末@PostConstruct 与@PreDestroy
     * 在方法上使用@PostConstruct 与原来的 init-method 等效。在方法上使用@PreDestroy,
     * 与 destroy-method 等效。
     */
    @PostConstruct
    public void initMethod(){
        System.out.println("初始化方法执行...");
    }
    @PreDestroy
    public void destroyMethod(){
        System.out.println("销毁方法执行...");
    }

    @Override
    public String toString() {
        return "SomeServiceImpl{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", order=" + order +
                ", myFriend=" + myFriend +
                '}';
    }
}

@Component("myOrder")
class Order{
    @Value("订单编号1")
    private String orderNum;

    @Override
    public String
    toString() {
        return "Order{" +
                "orderNum='" + orderNum + '\'' +
                '}';
    }
}

@Component("friend")
class Friend{

    @Value("张三")
    private String name;

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                '}';
    }
}

实体类

package io.spring.domain;

import java.util.*;

public class Student {

    private String name;
    private int age;
    private School mySchool;
    private String[] strings;
    private List schoolLists;
    private Set schoolSet;
    private Map map;
    private Properties properties;
    private List> mapList;

    public Student() {
    }

    public Student(String name, int age, School mySchool) {
        this.name = name;
        this.age = age;
        this.mySchool = mySchool;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setMySchool(School mySchool) {
        this.mySchool = mySchool;
    }

    public void setStrings(String[] strings) {
        this.strings = strings;
    }

    public void setSchoolLists(List schoolLists) {
        this.schoolLists = schoolLists;
    }

    public void setSchoolSet(Set schoolSet) {
        this.schoolSet = schoolSet;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setMapList(List> mapList) {
        this.mapList = mapList;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mySchool=" + mySchool +
                ", strings=" + Arrays.toString(strings) +
                ", schoolLists=" + schoolLists +
                ", schoolSet=" + schoolSet +
                ", map=" + map +
                ", properties=" + properties +
                ", mapList=" + mapList +
                '}';
    }
}
class School {
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}

3、在resources文件下创键Spring配置文件applicationContext.xml

如何配置去这里面https://repo.spring.io/release/org/springframework/spring/4.3.9.RELEASE/spring-framework-4.3.9.RELEASE-dist.zip找spring-framework-4.3.9.RELEASE-dist.zip\spring-framework-4.3.9.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html,CV就行了。


 

    
    



    
    
    
    
   
    
        
    

    
    
    
        
        
        
        
        
        

        
        
        
        

        
        
        
            
                abc
                def
            
        
        
        
            
                
            
        
        
        
            
                值1
                值2
            
        
        
        
            
                
                
            
        
        
        
            
                110
                北京
            
        
        
        
            
                
                    
                    
                
                
                    
                    
                
            
        
    

新建log4j2.xml文件



    
    
        
        
            
            
            
            
        

        
        
            
            
        
    

    
    
        
        
            
            
        
    

四、测试

查看类或接口的继承关系:IDEA中使用Ctrl+h,Eclipse使用Ctrl+t

SpringIOC和AOP简介以及基本使用_第2张图片

package io.spring.test;

import io.spring.domain.Student;
import io.spring.service.SomeService;
import javafx.scene.media.SubtitleTrack;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Test01 {
    public static void main(String[] args) {
        //创建spring容器对象
        //1、spring配置文件在类路径下使用该方式加载applicationContext.xml
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

        //2、spring配置文件存放在本地磁盘目录中,使用 FileSystemXmlApplicationContext 实现类进行加载。
        //ApplicationContext applicationContext=new FileSystemXmlApplicationContext("c:\\applicationContext.xml");

        //3、Spring 配置文件存放在项目的根路径下同样使用 FileSystemXmlApplicationContext实现类进行加载
        //下面是存放在项目根路径下的情况,该配置文件与src目录同级
        //ApplicationContext applicationContext=new FileSystemXmlApplicationContext("applicationContext.xml");

        //获取实例
        SomeService someService = applicationContext.getBean("someService", SomeService.class);
        //调用方法
        someService.doSome();

        System.out.println(someService);

        Student student = applicationContext.getBean("student", Student.class);
        System.out.println(student);

        ((AbstractApplicationContext) applicationContext).close();


    }
}

三、AOP 面向切面

AOP 简介

AOPAspect Orient Programming),面向切面编程,是面向对象编程 OOP 的一种补充。 面向对象编程是从静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序运行过 程。

AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理

AOP Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和 运行期动态代理实现程序功能的统一维护的一种技术。AOP OOP 的延续,是软件开发中 的一个热点,也是 Spring 框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进 行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开 发的效率。

                                                                                                                                                         百度百科《AOP

面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到 主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、 事务、日志、缓存等。

若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样, 会使主业务逻辑变的混杂不清。

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事 务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占 比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大 大干扰了主业务逻辑---转账。

AOP 编程术语

1) 切面(Aspect

切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。

2) 织入(Weaving织入是指将切面代码插入到目标对象的过程。 JDK静态代理、JDK动态代理、Cglib动态代理区别中TimeInvocationHandler 类中的invoke() 方法完成的工作,就可以称为织入。

3) 连接点(JoinPoint

连接点指可以被切面织入的方法。通常业务接口中的方法均为连接点。

4) 切入点(Pointcut

切入点指切面具体织入的方法。在 一个类中有两个方法doSome(),doOther(),若 doSome()将被增强,而 doOther()不被增强,则 doSome()为切入点,而 doOther()仅为连接点。

被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

5) 目标对象(Target

目标对象指将要被增强的对象。 即包含主业务逻辑的类的对象 。上例中的SomeServiceImpl的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然, 不被增强,也就无所谓目标不目标了。

6) 通知(Advice

通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。上例 中的 TimeInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码 切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 切入点定义切入的位置,通知定义切入的时间

AspectJ 对 AOP 的实现

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

AspectJ 简介

AspectJ 是一个面向切面的框架,它扩展了 Java 语言。AspectJ 定义了 AOP 语法,它有 一个专门的编译器用来生成遵守 Java 字节编码规范的 Class 文件。

                                                                                                                                           ---- 百度百科《AspectJ

官网地址:http://www.eclipse.org/aspectj/

AspetJ Eclipse 的开源项目,官网介绍如下:

SpringIOC和AOP简介以及基本使用_第3张图片

a seamless aspect-oriented extension to the Javatm programming language(一种基于 Java 平台 的面向切面编程的语言

Java platform compatible(兼容 Java 平台,可以无缝扩展)

easy to learn and use(易学易用)

Aspectj 是静态织入。静态织入:指在编译时期就织入,即:编译出来的 class 文件,字节码就已经被织入了。

AspectJ 的通知类型

AspectJ 中常用的通知有五种类型:

1)前置通知

2)后置通知

3)环绕通知

4)异常通知

5)最终通知

AspectJ 的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution ( [modifiers-pattern] 访问权限类型

                     ret-type-pattern 返回值类型

                     [declaring-type-pattern] 全限定性类名

                     name-pattern(param-pattern) 方法名(参数名)

                     [throws-pattern] 抛出异常类型

         )

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中 可以使用以下符号:

SpringIOC和AOP简介以及基本使用_第4张图片

举例:

execution(public * *(..))

指定切入点为:任意公共方法。

execution(* set*(..))

指定切入点为:任何一个以“set”开始的方法。

execution(* com.xyz.service.*.*(..))

指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.xyz.service..*.*(..))

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后 面必须跟“*”,表示包、子包下的所有类。

execution(* *.service.*.*(..))

指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点

execution(* *..service.*.*(..))

指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

execution(* *.ISomeService.*(..))

指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点

execution(* *..ISomeService.*(..))

指定所有包下的 ISomeSerivce 接口中所有方法为切入点

execution(* com.xyz.service.IAccountService.*(..))

指定切入点为:IAccountService 接口中的任意方法。

execution(* com.xyz.service.IAccountService+.*(..))

指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意 方法;若为类,则为该类及其子类中的任意方法。

execution(* joke(String,int)))

指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参 数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用 全限定类名,如 joke( java.util.List, int)

execution(* joke(String,*)))

指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类 型,如joke(String s1,String s2)joke(String s1,double d2)都是,但 joke(String s1,double d2,String s3)不是。

execution(* joke(String,..)))

指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且 参数类型不限,如 joke(String s1)joke(String s1,String s2)joke(String s1,double d2,String s3) 都是。

execution(* joke(Object))

指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob) 是,但,joke(String s)joke(User u)均不是。

execution(* joke(Object+)))

指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。 不仅 joke(Object ob)是,joke(String s)joke(User u)也是。

AspectJ 的开发环境

(1)在上面案例继续添加AspectJ依赖

1.8.9


    
      org.aspectj
      aspectjweaver
      ${aspectjweaver-version}
    

(2)引入 AOP 约束

SpringIOC和AOP简介以及基本使用_第5张图片

在前面 Spring 实现 AOP 时,并未引入 AOP 的约束,而在 AspectJ 实现 AOP 时,才提出 要引入 AOP 的约束。说明,配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的而非 Spring 框架本身在实现 AOP 时使用的。

AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

AspectJ 基于注解的 AOP 实现

AspectJ 提供了以注解方式对于 AOP 的实现

step1:定义业务接口

接口

package io.spring.aop.service;


public interface SomeService {
    String doSome(String arg);
}

实现类

package io.spring.aop.service.impl;

import io.spring.aop.service.SomeService;

public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String arg) {
        System.out.println("执行doSome...参数为:"+arg);
        return "aaa";
    }
}

step2:定义切面 POJO 类

step3:在切面类上添加@Aspect 注解

Step4:在 POJO 类的普通方法上添加通知注解

package io.spring.aop.aspect;


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//该类为一个 POJO 类,将作为切面出现。其中定义了若干普通方法,将作为不同的通知方法
//在定义的 POJO 类上添加@Aspect 注解,指定当前 POJO 类将作为切面。
@Aspect
public class MyAspect {
    //在 POJO 类的普通方法上添加通知注解
    /**
     * 切面类是用于定义增强代码的,即用于定义增强目标类中目标方法的增强方法。这些增
     * 强方法使用不同的“通知”注解,会在不同的时间点完成织入。当然,对于增强代码,还要
     * 通过 execution 表达式指定具体应用的目标类与目标方法,即切入点。
     */
    @Before("execution(* *..SomeServiceImpl.doSome(..))")
    public void beforeSome() {
        System.out.println("前置增强");
    }
}

Step5:注册目标对象与 POJO 切面类,仙剑一个applicationContext-aop.xml文件

Step6:注册 AspectJ 的自动代理


 

    
    
    


    
    
    
    

Step7:测试类中使用目标对象的 id

package io.spring.aop.test;


import io.spring.aop.service.SomeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext-aop.xml");
        SomeService someService = applicationContext.getBean("someServiceImpl", SomeService.class);
        someService.doSome("arg1");

    }
}

通知类型

(1)@Before 前置通知-方法有 JoinPoint 参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参 数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、 目标对象等。

不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。

 特点:
     *  1.在目标方法之前先执行
     *  2.不会影响目标方法的执行
     *  3.不会改变目标方法的执行结果。

package io.spring.aop.aspect;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//该类为一个 POJO 类,将作为切面出现。其中定义了若干普通方法,将作为不同的通知方法
//在定义的 POJO 类上添加@Aspect 注解,指定当前 POJO 类将作为切面。
@Aspect
public class MyAspect {
    //在 POJO 类的普通方法上添加通知注解
    /**
     * 切面类是用于定义增强代码的,即用于定义增强目标类中目标方法的增强方法。这些增
     * 强方法使用不同的“通知”注解,会在不同的时间点完成织入。当然,对于增强代码,还要
     * 通过 execution 表达式指定具体应用的目标类与目标方法,即切入点。
     */
    @Before("execution(* *..SomeServiceImpl.doSome(..))")
    public void beforeSome(JoinPoint joinPoint) {
        System.out.println("前置通知(切入点表达式为):"+joinPoint);
        System.out.println("前置通知(方法签名为)"+joinPoint.getSignature());
        System.out.println("前置通知(目标对象为)"+joinPoint.getTarget());
        Object[] args = joinPoint.getArgs();
        System.out.print("前置通知(方法参数为):");
        for (Object o:args){
            System.out.print(o+" ");
        }
        System.out.println("\n前置增强");
    }
}

(2)@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变 量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

特点:
     *   1.在目标方法之后执行的。
     *   2.能够获取到目标方法的执行结果
     *     1)String和基本数据类型是不能改变调用结果的。
     *     2)对象类型是可以修改返回结果的属性值
     *   3.不会影响目标方法的执行

  @AfterReturning(value = "execution(* *..SomeServiceImpl.doSome(..))",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result) {
        System.out.println("后置通知(切入点表达式为):"+joinPoint);
        System.out.println("后置通知(方法签名为)"+joinPoint.getSignature());
        System.out.println("后置通知(目标对象为)"+joinPoint.getTarget());
        Object[] args = joinPoint.getArgs();
        System.out.print("后置通知(方法参数为):");
        for (Object o:args){
            System.out.print(o+" ");
        }

        System.out.println("后置增强目标方法结果"+result);

        System.out.println("\n后置增强");
    }

(3)@Around 环绕通知-增强方法有 ProceedingJoinPoint 参数

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个 proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

 特点:
     *   1.在目标方法的前和后都能增强功能。
     *   2.能够改变目标方法的执行结果
     *   3.控制目标方法的访问。

 @Around(value = "execution(* *..SomeServiceImpl.doSome(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知:前");
        //拿到结果可以修改结果
        Object result = proceedingJoinPoint.proceed();
        //修改目标方法的执行结果
		if( result != null){
			String str  = (String)result;
			result = "Hello Aspect Around " + str.toUpperCase(); 
		}
        System.out.println("目标方法的返回值为:"+proceed);
        System.out.println("环绕通知:后");
        //返回目标方法执行结果
        return result;
    }

(4)@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。 当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

 特点:
     *   1.在目标方法抛出异常时执行的。
     *   2.获取到异常通知,不是异常处理程序,异常还是被抛出的。比如在切面方法中把异常信息记录道数据库、发邮件通知等

SpringIOC和AOP简介以及基本使用_第6张图片

   @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSome(..))",throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("异常通知" + e);
    }

(5)@After 最终通知

无论目标方法是否抛出异常,该增强均会被执行

 特点:
     *   1.在目标方法之后执行的。
     *   2.总是会被执行的,比如清理内存等。

  @After(value = "execution(* *..SomeServiceImpl.doSome(..))")
    public void after(){
        System.out.println("最终通知,在目标方法后执行");
    }

(6)@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。 AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。

其用法是,将@Pointcut 注解在一个方法之上,以后所有的 executeion value 属性值 均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcute 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。

SpringIOC和AOP简介以及基本使用_第7张图片

四、在Servlet中使用 Spring 的监听器 ContextLoaderListener

对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个 ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将 Spring 容器的创建时机, 放在 ServletContext 初始化时,就可以保证 Spring 容器的创建只会执行一次,也就保证了 Spring 容器在整个应用中的唯一性。

Spring 容器创建好后,在整个应用的生命周期过程中,Spring 容器应该是随时可以被访问的。即Spring 容器应具有全局性。而放入 ServletContext 对象的属性,就具有应用的全局性。

所以,将创建好的 Spring 容器,以属性的形式放入到 ServletContext 的空间中,就 保证了 Spring 容器的全局性。 上述的这些工作,已经被封装在了如下的 Spring Jar 包的相关 API 中: spring-web-4.3.9.RELEASE

step1:在上面的案例中继续引入spring-web依赖

3.1.0
        1.2
        1.2.1


        
            org.springframework
            spring-web
            ${spring-version}
        


            
                javax.servlet
                javax.servlet-api
                ${servlet-api-version}
            
            
                javax.servlet.jsp.jstl
                jstl-api
                ${jstl-api-version}
            
            
                org.apache.taglibs
                taglibs-standard-spec
                ${taglibs-standard-version}
            
            
                org.apache.taglibs
                taglibs-standard-impl
                ${taglibs-standard-version}
            

step2:注册监听器 ContextLoaderListener

若要在 ServletContext 初始化时创建 Spring 容器,就需要使用监听器接口 ServletContextListener ServletContext 进行监听。在 web.xml 中注册该监听器。

  
    org.springframework.web.context.ContextLoaderListener
  

Spring 为该监听器接口定义了一个实现类 ContextLoaderListener,完成了两个很重要的工作:创建容器对象,并将容器对象放入到了 ServletContext 的空间中。

打开 ContextLoaderListener 的源码。看到一共四个方法,两个是构造方法,一个初始化 方法,一个销毁方法。

SpringIOC和AOP简介以及基本使用_第8张图片

在这四个方法中较重要的方法就是 contextInitialized()context 初始化方法。

SpringIOC和AOP简介以及基本使用_第9张图片

点击去 initWebApplicationContext()方法,可以看到,在其中创建了容器对象。

SpringIOC和AOP简介以及基本使用_第10张图片

并且,将创建好的容器对象放入到了 ServletContext 的空间中,key 为一个常量: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

SpringIOC和AOP简介以及基本使用_第11张图片

Step3:指定 Spring 配置文件的位置

ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认 Spring 配置文件位置与名称为:WEB-INF/applicationContext.xml。但,一般会将该配置文件放置于项目的 classpath 下,即 resources 下,所以需要在 web.xml 中对 Spring 配置文件的位置及 名称进行指定。

SpringIOC和AOP简介以及基本使用_第12张图片


    contextConfigLocation
    classpath:applicationContext.xml

从监听器 ContextLoaderListener 的父类 ContextLoader 的源码中可以看到其要读取的配 置文件位置参数名称 contextConfigLocation

SpringIOC和AOP简介以及基本使用_第13张图片

Step4:获取 Spring 容器对象

Servlet 中获取容器对象的常用方式有两种:

(1) 直接从 ServletContext 中获取

从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中放的 key WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。所以可 以直接通过 ServletContext getAttribute()方法,按照指定的 key 将容器对象获取到。

package com.web;

import org.springframework.web.context.WebApplicationContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //直接从 ServletContext 中获取
        WebApplicationContext webApplicationContext = (WebApplicationContext)
                this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }
}

(2)通过 WebApplicationContextUtils 获取

工具类 WebApplicationContextUtils 有一个方法专门用于从 ServletContext 中获取 Spring 容器对象:getRequiredWebApplicationContext(ServletContext sc) 查看源码,看调用关系,就可看到其是从 ServletContext 中读取的属性值,即 Spring 容器。

package com.web;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过 WebApplicationContextUtils 获取
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
    }
}

SpringIOC和AOP简介以及基本使用_第14张图片

SpringIOC和AOP简介以及基本使用_第15张图片

SpringIOC和AOP简介以及基本使用_第16张图片

可以看到   Object attr = sc.getAttribute(attrName);    arrtName为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE初始化ServletContext的时候放进去的

 

 

 

更多可以学习参考:Spring 教程

你可能感兴趣的:(Spring)