Spring学习笔记

Spring概念学习


参考: java 廖雪峰

IoC容器

IoC原理

IoC,Inversion of Control,直译为控制反转

IoC又称为依赖注入(DI:Dependency Injection),它解决了一个最主要的问题:将组件的创建+配置与组件的使用相分离,并且,由IoC容器负责管理组件的生命周期。

如果一个系统有大量的组件,其生命周期和相互之间的依赖关系如果由组件自身来维护,不但大大增加了系统的复杂度,而且会导致组件之间极为紧密的耦合,继而给测试和维护带来了极大的困难。

核心问题:

  • 谁负责创建组件
  • 谁负责根据依赖关系组装组件?
  • 销毁时,如何按依赖顺序正确销毁?

解决问题的核心方案就是IoC。

  • 传统的应用程序中,控制权在程序本身,程序的控制流程完全由开发者控制
  • IoC模式下,控制权发生了反转,即从应用程序转移到了IoC容器,所有组件不再由应用程序自己创建和配置,而是由IoC容器负责,这样,应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制

因为IoC容器要负责实例化所有的组件,因此,有必要告诉容器如何创建组件,以及各组件的依赖关系。一种最简单的配置是通过XML文件

<beans>
    <bean id="dataSource" class="HikariDataSource" />
    <bean id="bookService" class="BookService">
        <property name="dataSource" ref="dataSource" /> <!-- 注入 -->
    </bean>
    <bean id="userService" class="UserService">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

上述XML配置文件指示IoC容器创建3个JavaBean组件,并把id为dataSource的组件通过属性dataSource(即调用setDataSource()方法)注入到另外两个组件中。

一些概念

**Bean:**在Spring的IoC容器中,我们把==所有组件统称为JavaBean==,即配置一个组件就是配置一个Bean。

**无侵入容器:**Spring的IoC容器是一个高度可扩展的无侵入容器。所谓无侵入,是指应用程序的组件无需实现Spring的特定接口,或者说,组件根本不知道自己在Spring的容器中运行

原来的写法:BookService需要自身创建数据库实例dataSource

public class BookService {
    private HikariConfig config = new HikariConfig();
    private DataSource dataSource = new HikariDataSource(config);

    public Book getBook(long bookId) {
        try (Connection conn = dataSource.getConnection()) {
            ...
            return book;
        }
    }
}

依赖注入的方式:作为构造函数的参数 或 set的方式注入

// 构造函数
public class BookService {
    private DataSource dataSource;

    public BookService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

// set函数
public class BookService {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

装配Bean

注意到UserService通过setMailService()注入了一个MailService。然后,我们需要编写一个特定的application.xml配置文件,告诉Spring的IoC容器应该如何创建并组装Bean:

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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.itranswarp.learnjava.service.UserService">
        <property name="mailService" ref="mailService" />
    </bean>

    <bean id="mailService" class="com.itranswarp.learnjava.service.MailService" />
</beans>

注意观察上述配置文件,其中与XML Schema相关的部分格式是固定的,我们只关注两个<bean ...>的配置:

  • 每个<bean ...>都有一个id标识,相当于Bean的唯一ID;
  • userServiceBean中,通过<property name="..." ref="..." />注入了另一个Bean
  • Bean的顺序不重要,Spring根据依赖关系会自动正确初始化。

把上述XML配置文件用Java代码写出来,就像这样:

UserService userService = new UserService();
MailService mailService = new MailService();
userService.setMailService(mailService);

只不过Spring容器是通过读取XML文件后使用反射完成的。

总结

Spring的IoC容器接口是ApplicationContext,并提供了多种实现类;

通过XML配置文件创建IoC容器时,使用ClassPathXmlApplicationContext

持有IoC容器后,通过getBean()方法获取Bean的引用。

使用注解(Annotation)配置

使用XML配置的优点是所有的Bean都能一目了然地列出来,并通过配置注入能直观地看到每个Bean的依赖。它的缺点是写起来非常繁琐,每增加一个组件,就必须把新的Bean配置到XML中。

解决·:可以使用Annotation配置,可以完全不需要XML,让Spring自动扫描Bean并组装它们。

具体操作:

  • 注解@Component就相当于定义了一个Bean
  • 使用@Autowired就相当于把指定类型的Bean注入到指定的字段中。
@Component
public class UserService {
    @Autowired
    MailService mailService;

    ...
}

使用Annotation配合自动扫描能大幅简化Spring的配置,我们只需要保证:

  • 每个Bean被标注为@Component并正确使用@Autowired注入;
  • 配置类被标注为@Configuration@ComponentScan
  • 所有Bean均在指定包以及子包内。
@Configuration
@ComponentScan
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        User user = userService.login("bob@example.com", "password");
        System.out.println(user.getName());
    }
}

定制Bean

  • Spring默认使用Singleton(单例模式)创建Bean,也可指定Scope为Prototype(原型);
  • 可将相同类型的Bean注入List
  • 可用@Autowired(required=false)允许可选注入;
  • 可用带@Bean标注的方法创建Bean;
  • 可使用@PostConstruct@PreDestroy对Bean进行初始化和清理;
  • 相同类型的Bean只能有一个指定为@Primary,其他必须用@Quanlifier("beanName")指定别名;
  • 注入时,可通过别名@Quanlifier("beanName")指定某个Bean;
  • 可以定义FactoryBean来使用工厂模式创建Bean。

使用Resource

  • Spring提供了Resource类便于注入资源文件。
  • 最常用的注入是通过classpath以classpath:/path/to/file的形式注入。即@Value("classpath:/logo.txt")
  • 使用Maven的标准目录结构,所有资源文件放入src/main/resources即可(classpath类路径)。

注入配置

  • Spring容器可以通过@PropertySource自动读取配置,并以@Value("${key}")的形式注入;
  • 可以通过${key:defaultValue}指定默认值;
  • #{bean.property}形式注入时,Spring容器自动把指定Bean的指定属性值注入。

使用条件装配

  • Spring允许通过@Profile配置不同的Bean;
  • Spring还提供了@Conditional来进行条件装配,Spring Boot在此基础上进一步提供了基于配置、Class、Bean等条件进行装配。

Spring 依赖注入

参考:

Spring 依赖注入_w3cschool

  • 什么是Spring Bean:Bean就是由IOC容器初始化、装配及管理的对象,除此之外,和程序中的其他对象没有区别

  • 怎么使用Bean:bean是由spring容器创建和管理的,各组件之间的依赖关系也是由spring容器管理的

  • Spring Bean的装配(三种方式):

    • 基于xml配置装配

    • 基于Java代码装配@Configuration

    • 基于注解的装配@Component

      • 组件扫描(component scanning),Spring会自动发现应用上下文中所创建的bean。

      • 自动装配( autowiring),Spring自动满足bean之间的依赖

1、概要

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。

什么是依赖注入(DI,Dependency Injection):

每个基于应用程序的 java 都有几个对象,由这些对象一起工作来呈现出终端用户所看到的工作的应用程序。当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能独立于其他 Java 类来增加这些类重用的可能性,并且在做单元测试时,测试独立于其他类的独立性。依赖注入(或有时称为布线)有助于把这些类粘合在一起,同时保持他们独立。

2、基于构造函数的依赖注入

在这里我们所做的就是创建一个 TextEditor 和 SpellChecker 之间的依赖关系。而在控制反转IoC的场景中,我们会这样做:

package com.tutorialspoint;
public class TextEditor {
   private SpellChecker spellChecker;
   public TextEditor(SpellChecker spellChecker) {
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

在这里,我们已经从 TextEditor 中删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系(即 SpellChecker 类)通过类构造函数被注入到 TextEditor 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。

具体可以看:Constructor-based dependency injection的例子。要点:

  • 构造函数中传参

  • src文件夹下要创建对应的Beans配置文件——Beans.xml。

    • ref向一个对象传递一个引用
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <constructor-arg ref="spellChecker"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

</beans>

3、基于Setter方法的依赖注入

依赖注入的第二种方法是通过 TextEditor 类的 Setter 方法,我们将创建 SpellChecker 实例,该实例将被用于调用 setter 方法来初始化 TextEditor 的属性。

package com.tutorialspoint;
public class TextEditor {
   private SpellChecker spellChecker;
   // a setter method to inject the dependency.
   public void setSpellChecker(SpellChecker spellChecker) {
      System.out.println("Inside setSpellChecker." );
      this.spellChecker = spellChecker;
   }
   // a getter method to return spellChecker
   public SpellChecker getSpellChecker() {
      return spellChecker;
   }
   public void spellCheck() {
      spellChecker.checkSpelling();
   }
}

Bean.xml的内容略有不同:

<!-- Definition for textEditor bean -->
   <bean id="textEditor" class="com.tutorialspoint.TextEditor">
      <property name="spellChecker" ref="spellChecker"/>
   </bean>

   <!-- Definition for spellChecker bean -->
   <bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
   </bean>

唯一的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素

因此,DI 主要有两种变体和下面的两个子章将结合实例涵盖它们:

序号依赖注入类型 & 描述
1Constructor-based dependency injection当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。
2Setter-based dependency injection基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后,容器调用 beans 的 setter 方法来实现的。

你可以混合这两种方法,基于构造函数和基于 setter 方法的 DI,然而使用有强制性依存关系的构造函数和有可选依赖关系的 setter是一个好的做法。

PS:

Spring 注入内部 Beans_w3cschool

Spring 注入集合_w3cschool

Spring Beans自动装配

参考:

自动装配

依赖注入:可以使用<bean>元素来声明 bean 和通过使用 XML 配置文件中的<constructor-arg><property>元素来注入 。

Spring 容器可以在不使用<constructor-arg><property> 元素的情况下==自动装配==相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。

自动装配模式

下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用<bean>元素的 autowire 属性为一个 bean 定义指定自动装配模式。

模式描述
no这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。
byName属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
constructor类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
autodetect(3.0版本不支持)Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

可以使用 byType 或者 constructor 自动装配模式来连接数组和其他类型的集合。

例子:

什么是byName和byType

Spring中byName和byType的区别

  • byName就是通过Bean的属性名称**(id或name)**自动装配。

  • byType就是通过Bean的Class类型来自动装配。使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

    <bean id = "cat2" class="com.hello.pojo.Cat"/>
    <bean id = "dog" class="com.hello.pojo.Dog"/>
    <bean id = "people" class="com.hello.pojo.People" autowire="byName">
        <property name="name" value="wzh"/>
    </bean>

当一个bean节点带有 autowire byName的属性时。

(1)将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
(2)去spring容器中寻找是否有此字符串名称id的对象。
(3)如果有,就取出注入;如果没有,就报空指针异常。

自动装配的局限性

限制描述
重写的可能性你可以使用总是重写自动装配的 和 设置来指定依赖关系。
原始数据类型你不能自动装配所谓的简单类型包括基本类型,字符串和类。
混乱的本质自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。

Spring 基于注解的配置

参考:

Spring 基于注解的配置

基于注解的配置

使用 Spring 开发时,进行配置主要有两种方式:

  • 一是 xml 的方式
  • 二是 java config 的方式。

从 Spring 2.5 开始就可以使用注解来配置依赖注入。而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注解,将 bean 配置移动到组件类本身。

在使用 java config 的过程当中,我们不可避免的会有各种各样的注解打交道,其中,我们使用最多的注解应该就是 @Autowired 注解了。这个注解的功能就是为我们注入一个定义好的 bean。

在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。

一旦 被配置后,你就可以开始注解你的代码,表明 Spring 应该自动连接值到属性,方法和构造函数。让我们来看看几个重要的注解,并且了解它们是如何工作的:

序号注解 & 描述
1@Required@Required 注解应用于 bean 属性的 setter 方法。
2@Autowired@Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性
3@Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱
4JSR-250 AnnotationsSpring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。

Autowired注解

  • @autowired 注解来源于英文单词 autowire,这个单词的意思是自动装配的意思
  • 在 Spring 的世界当中,自动装配指的就是使用将 Spring 容器中的 bean 自动的和我们需要这个 bean 的类组装在一起

Qualifier注解

可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注解和 @Autowired 注解通过指定哪一个真正的 bean 将会被装配来消除混乱。

示例:

Profile.java

public class Profile {
   @Autowired
   @Qualifier("student1")
   private Student student;
   public Profile(){
      System.out.println("Inside Profile constructor." );
   }
   }
}

配置文件 Beans.xml 的示例(有student1和student2):

<?xml version="1.0" encoding="UTF-8"?>

<beans 省略一堆

   <context:annotation-config/>

   <!-- Definition for profile bean -->
   <bean id="profile" class="com.tutorialspoint.Profile">
   </bean>

   <!-- Definition for student1 bean -->
   <bean id="student1" class="com.tutorialspoint.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11"/>
   </bean>

   <!-- Definition for student2 bean -->
   <bean id="student2" class="com.tutorialspoint.Student">
      <property name="name"  value="Nuha" />
      <property name="age"  value="2"/>
   </bean>

</beans>

Spring 基于 Java 代码的配置

参考:基于java代码的配置

参考:SpringBoot 配置类 @Configuration和@Bean的使用

背景

基于 Java 的配置选项,可以使你在不用配置 XML 的情况下编写大多数的 Spring

@Configuration 和 @Bean 注解

1)什么是@Configuration 和 @Bean

  • @Configuration:

    • 声明当前类是一个配置类,相当于 Spring 中的一个 XML 文件。可理解为用spring的时候xml里面的标签

    • 带有 @Configuration 的注解类表示这个类可以使用 Spring IoC 容器作为 bean 定义的来源。

  • @Bean:

    • 作用在方法上,声明当前方法的返回值是一个 Bean。可理解为用spring的时候xml里面的标签
    • @Bean 注解告诉 Spring,一个带有 @Bean 的注解方法将返回一个对象,该对象应该被注册为在 Spring 应用程序上下文中的 bean。

最简单可行的 @Configuration 类如下所示:

package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

上面的代码将等同于下面的 XML 配置

<beans>
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>

Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。

SpringIOC 容器管理一个或者多个bean,这些bean都需要在@Configuration注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。

2)@Configuration 和@Component 的区别?

两者都是将注释的内容,交给spring管理。 而@configuration注解类,会去扫描类中携带@bean的方法,然后默认去执行这个方法。

而@component 注解的类,只是将类交给spring管理,而不会去执行方法。 只有当使用@autowired或者@resource注入对象的时候,使用对象去调用方法的时候才会执行方法。

====:

  • Configuration 和 Bean连用
  • Component 和 Autowired或resource连用

3)@Bean 和 @Autowired的关系

@Bean 和 @Autowired 做了两件完全不同的事情:

  1. @Bean 告诉 Spring:“这是这个类的一个实例,请保留它,并在我请求时将它还给我”。
  2. @Autowired 说:“请给我一个这个类的实例,例如,一个我之前用@Bean注释创建的实例”。

Spring AOP

参考:使用AOP

1、使用AOP

1)AOP是什么

AOP是Aspect Oriented Programming,即面向切面编程。

而AOP是一种新的编程方式,它和OOP(Object Oriented Programming)不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)。

背景:

要理解AOP的概念,我们先用OOP举例,比如一个业务组件BookService,它有几个业务方法:

  • createBook:添加新的Book;
  • updateBook:修改Book;
  • deleteBook:删除Book。

OOP对每个业务方法,例如,createBook(),除了业务逻辑,还需要安全检查、日志记录和事务处理

如果我们以AOP的视角来编写上述业务,可以依次实现:

  1. 核心逻辑,即BookService;
  2. 切面逻辑,即:
  3. 权限检查的Aspect;
  4. 日志的Aspect;
  5. 事务的Aspect。

然后,以某种方式,让框架来把上述3个Aspect以Proxy的方式“织入”到BookService中,这样一来,就不必编写复杂而冗长的Proxy模式。

2)AOP原理

如何把切面织入到核心逻辑中?这正是AOP需要解决的问题。换句话说,如果客户端获得了BookService的引用,当调用bookService.createBook()时,如何对调用方法进行拦截,并在拦截前后进行安全检查、日志、事务等处理,就相当于完成了所有业务功能。

Spring的AOP实现就是基于JVM的动态代理(运行期进行AOP织入)。由于JVM的动态代理要求必须实现接口,如果一个普通类没有业务接口,就需要通过CGLIB或者Javassist这些第三方库实现。

AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。

需要特别指出的是,AOP对于解决特定问题,例如事务管理非常有用,这是因为分散在各处的事务代码几乎是完全相同的,并且它们需要的参数(JDBC的Connection)也是固定的。

另一些特定问题,如日志,就不那么容易实现,因为日志虽然简单,但打印日志的时候,经常需要捕获局部变量,如果使用AOP实现日志,我们只能输出固定格式的日志,因此,使用AOP时,必须适合特定的场景。

2、装配AOP(较少用)

在AOP编程中,我们经常会遇到下面的概念:

  • Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
  • Weaving:织入,指将切面整合到程序的执行流程中;
  • Interceptor:拦截器,是一种实现增强的方式;
  • AOP Proxy:AOP代理,是客户端持有的增强后的对象引用

其实,我们不用关心AOP创造的“术语”,只需要理解AOP本质上只是一种代理模式的实现方式,在Spring的容器中实现AOP特别方便。

虽然Spring容器内部实现AOP的逻辑比较复杂(需要使用AspectJ解析注解,并通过CGLIB实现代理类),但我们使用AOP非常简单,一共需要三步:

  1. 定义执行方法,并在方法上通过AspectJ的注解告诉Spring应该在何处调用此方法;
  2. 标记@Component@Aspect
  3. @Configuration类上标注@EnableAspectJAutoProxy

1)定义一个LoggingAspect

@Aspect
@Component
public class LoggingAspect {
    // 在执行UserService的每个方法前执行:
    @Before("execution(public * com.itranswarp.learnjava.service.UserService.*(..))")
    public void doAccessCheck() {
        System.err.println("[Before] do access check...");
    }

    // 在执行MailService的每个方法前后执行:
    @Around("execution(public * com.itranswarp.learnjava.service.MailService.*(..))")
    public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
        System.err.println("[Around] start " + pjp.getSignature());
        Object retVal = pjp.proceed();
        System.err.println("[Around] done " + pjp.getSignature());
        return retVal;
    }
}
  • 观察doAccessCheck()方法,我们定义了一个@Before注解,后面的字符串是告诉AspectJ应该在何处执行该方法,这里写的意思是:执行UserService的每个public方法前执行doAccessCheck()代码。

  • 再观察doLogging()方法,我们定义了一个@Around注解,它和@Before不同,@Around可以决定是否执行目标方法,因此,我们在doLogging()内部先打印日志,再调用方法,最后打印日志后返回结果。

  • LoggingAspect类的声明处,除了用@Component表示它本身也是一个Bean外,我们再加上@Aspect注解,表示它的@Before标注的方法需要注入到UserService的每个public方法执行前,@Around标注的方法需要注入到MailService的每个public方法执行前后。

2)紧接着,我们需要给@Configuration类加上一个@EnableAspectJAutoProxy注解:

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AppConfig {
    ...
}
  • Spring的IoC容器看到这个注解,就会自动查找带有@Aspect的Bean,然后根据每个方法的@Before@Around等注解把AOP注入到特定的Bean中。

拦截器类型

顾名思义,拦截器有以下类型:

  • @Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了;

  • @After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行;

  • @AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码;

  • @AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码;

  • @Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能。

3、使用注解装配AOP

使用AOP时,被装配的Bean最好自己能清清楚楚地知道自己被安排了。例如,Spring提供的@Transactional(事务)就是一个非常好的例子。如果我们自己写的Bean希望在一个数据库事务中被调用,就标注上@Transactional

@Component
public class UserService {
    // 有事务:
    @Transactional
    public User createUser(String name) {
        ...
    }

    // 无事务:
    public boolean isValidName(String name) {
        ...
    }

    // 有事务:
    @Transactional
    public void updateUser(User user) {
        ...
    }
}

或者直接在class级别注解,表示“所有public方法都被安排了”:

@Component
@Transactional
public class UserService {
    ...
}

WEB开发基础

参考:Web开发 - 廖雪峰的官方网站

1、JavaEE及背景

JavaEE是Java Platform Enterprise Edition的缩写,即Java企业平台。JavaEE也不是凭空冒出来的,它实际上是完全基于JavaSE,只是多了一大堆服务器相关的库以及API接口。所有的JavaEE程序,仍然是运行在标准的JavaSE的虚拟机上的。

JavaEE最核心的组件就是基于Servlet标准的Web服务器,开发者编写的应用程序是基于Servlet API并运行在Web服务器内部的:

2、Servlet入门

在JavaEE平台上,处理TCP连接,解析HTTP协议这些底层工作统统扔给现成的Web服务器去做,我们只需要把自己的应用程序跑在Web服务器上。为了实现这一目的,JavaEE提供了Servlet API,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能:

最简单的Servlet:

// WebServlet注解表示这是一个Servlet,并映射到地址/:
@WebServlet(urlPatterns = "/")
public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 设置响应类型:
        resp.setContentType("text/html");
        // 获取输出流:
        PrintWriter pw = resp.getWriter();
        // 写入响应:
        pw.write("<h1>Hello, world!</h1>");
        // 最后不要忘记flush强制输出:
        pw.flush();
    }
}

另外,我们首先要找一个支持Servlet API的Web服务器来运行war文件。常用的服务器有:

  • Tomcat:由Apache开发的开源免费服务器;
  • Jetty:由Eclipse开发的开源免费服务器;
  • GlassFish:一个开源的全功能JavaEE服务器。

实际上,类似Tomcat这样的服务器也是Java编写的,启动Tomcat服务器实际上是启动Java虚拟机,执行Tomcat的main()方法,然后由Tomcat负责加载我们的.war文件,并创建一个HelloServlet实例,最后以多线程的模式来处理HTTP请求。

因为我们编写的Servlet并不是直接运行,而是由Web服务器加载后创建实例运行,所以,类似Tomcat这样的Web服务器也称为Servlet容器

3、Servlet开发

开发Servlet时,推荐使用main()方法启动嵌入式Tomcat服务器并加载当前工程的webapp,便于开发调试,且不影响打包部署,能极大地提升开发效率。

4、JSP开发

Servlet就是一个能处理HTTP请求,发送HTTP响应的小程序,而发送响应无非就是获取PrintWriter,然后输出HTML。

用PrintWriter输出HTML比较痛苦,因为不但要正确编写HTML,还需要插入各种变量。如果想在Servlet中输出一个类似新浪首页的HTML,写对HTML基本上不太可能。

PrintWriter pw = resp.getWriter();
pw.write("<html>");
pw.write("<body>");
pw.write("<h1>Welcome, " + name + "!</h1>");
pw.write("</body>");
pw.write("</html>");
pw.flush();

更简单的输出HTML的办法——JSP

JSP是Java Server Pages的缩写,它的文件必须放到/src/main/webapp下,文件名必须以.jsp结尾,整个文件与HTML并无太大区别,但需要插入变量,或者动态输出的地方,使用特殊指令<% ... %>

总结:

JSP是一种在HTML中嵌入动态输出的文件,它和Servlet正好相反,Servlet是在Java代码中嵌入输出HTML;

JSP可以引入并使用JSP Tag,但由于其语法复杂,不推荐使用;

JSP本身目前已经很少使用,我们只需要了解其基本用法即可。

5、MVC开发

UserServlet代码:

@WebServlet(urlPatterns = "/user")
public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 假装从数据库读取:
        School school = new School("No.1 Middle School", "101 South Street");
        User user = new User(123, "Bob", school);
        // 放入Request中:
        req.setAttribute("user", user);
        // forward给user.jsp:
        req.getRequestDispatcher("/WEB-INF/user.jsp").forward(req, resp);
    }
}

我们把UserServlet看作业务逻辑处理,把User看作模型,把user.jsp看作渲染,这种设计模式通常被称为MVC:Model-View-Controller,即UserServlet作为控制器(Controller),User作为模型(Model),user.jsp作为视图(View),整个MVC架构如下:

6、Filter

总结:

  • @WebFilter(urlPatterns = "/*")
    public class EncodingFilter implements Filter {
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            System.out.println("EncodingFilter:doFilter");
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            chain.doFilter(request, response);
        }
    }
    
  • Filter是一种对HTTP请求进行预处理的组件,它可以构成一个处理链,使得公共处理代码能集中到一起;

  • Filter适用于日志、登录检查、全局设置等;

  • 设计合理的URL映射可以让Filter链更清晰。

  • 修改请求、响应:借助HttpServletRequestWrapper,我们可以在Filter中实现对原始HttpServletRequestHttpServletResponse的的修改。

6、使用Listener

通过Listener我们可以监听Web应用程序的生命周期,获取HttpSession等创建和销毁的事件;

ServletContext是一个WebApp运行期的全局唯一实例,可用于设置和共享配置信息。

Spring开发Web应用

参考:开发Web应用 - 廖雪峰的官方网站

Spring虽然都可以集成任何Web框架,但是,Spring本身也开发了一个MVC框架,就叫Spring MVC。这个MVC框架设计得足够优秀以至于我们已经不想再费劲去集成类似Struts这样的框架了

1、Spring MVC

使用Spring MVC时,整个Web应用程序按如下顺序启动:

  1. 启动Tomcat服务器;
  2. Tomcat读取web.xml并初始化DispatcherServlet;
  3. DispatcherServlet创建IoC容器并自动注册到ServletContext中。

启动后,浏览器发出的HTTP请求全部由DispatcherServlet接收,并根据配置转发到指定Controller的指定方法处理。

2、使用REST

@RestController
@RequestMapping("/api")
public class ApiController {
    @Autowired
    UserService userService;

    @GetMapping("/users")
    public List<User> users() {
        return userService.getUsers();
    }

    @GetMapping("/users/{id}")
    public User user(@PathVariable("id") long id) {
        return userService.getUserById(id);
    }

    @PostMapping("/signin")
    public Map<String, Object> signin(@RequestBody SignInRequest signinRequest) {
        try {
            User user = userService.signin(signinRequest.email, signinRequest.password);
            return Map.of("user", user);
        } catch (Exception e) {
            return Map.of("error", "SIGNIN_FAILED", "message", e.getMessage());
        }
    }

    public static class SignInRequest {
        public String email;
        public String password;
    }
}

使用@RestController可以方便地编写REST服务,Spring默认使用JSON作为输入和输出。

要控制序列化和反序列化,可以使用Jackson提供的@JsonIgnore@JsonProperty注解。

3、集成Filter

在Spring MVC中,DispatcherServlet只需要固定配置到web.xml中,剩下的工作主要是专注于编写Controller。

可以自己编写一个EncodingFilter,也可以直接使用Spring MVC自带的一个CharacterEncodingFilter。配置Filter时,只需在web.xml中声明即可

4、使用Interceptor

如果只基于Spring MVC开发应用程序,还可以使用Spring MVC提供的一种功能类似Filter的拦截器:Interceptor。和Filter相比,Interceptor拦截范围不是后续整个处理流程,而是仅针对Controller拦截

一个Interceptor必须实现HandlerInterceptor接口,可以选择实现preHandle()postHandle()afterCompletion()方法。

  • preHandle()是Controller方法调用前执行

  • postHandle()是Controller方法正常返回后执行,

  • afterCompletion()无论Controller方法是否抛异常都会执行,参数ex就是Controller方法抛出的异常(未抛出异常是null)。

其他

1、Java注解和Python的装饰器

参考:

Java 的注解 和 Python 的装饰器 是一回事吗?

java中注解和python中装饰器的区别

java注解:

  • java的注解(Annotation),实际上是给语法元素打一个标记。比如你可以给一个函数打一个标记,给一个类打一个标记等等。Java只保证记录这个标记,但是不会主动根据这给标记做任何事。

  • 如果你不用Spring框架的话,不会有任何事情发生,直接访问这个字段就是空。当如果你配置了合适的处理流程,而这个流程就会根据有没有这个标记干活。比如你要求Spring “Auto Scan” 并且注入依赖,这个处理过程会用反射去读哪些元素被做了某个特定标记。没有标记就不理,有标记就注入。

Python装饰器:

  • python里的decorator是一个语法糖,是希望把“decorator”这个形式写得更漂亮。就是通过一层壳对一个函数的行为进行修饰

总结:

第一点:对代码块的影响

  • java注解:不会对所修饰的代码产生直接的影响。
  • python装饰器:可以对所修饰的代码产生直接的影响。

第二点:共通处

  • java中注解+反射 可以实现 python装饰器同样的功能,包括面向切面编程、参数校验等。

第三点:从用途看

  • 从用途看注解像是注释文档一样,用于生成javadoc文档(以参数形式标注)、检查等。
  • 装饰器像是为函数提供更多的功能,并装在不同的函数身上。

第四点:从原理看

  • java注解:所有注解本质是继承自接口(Annotation)的接口
  • python装饰器:被装饰函数的返回值 作为参数传给闭包函数执行(这个闭包函数名前面加个@,就是装饰器)

Java三大器(过滤器Filter、监听器、拦截器)

参考:

Java三大器之过滤器、监听器、拦截器

Java中的Filter过滤器

Tables过滤器(Filter)监听器(Listener)拦截器(Interceptor)
关注的点wed请求系统级别参数、对象Action(部分web请求)
如何实现的函数回调事件Java反射机制(动态代理)
应用场景设置字符编码统计网站在线人数拦截未登录用户
URL级别的权限访问控制清除过期session审计日志
过滤敏感词汇
压缩响应信息
是否依赖servlet容器依赖不依赖
Serverlet提供的支持Filter接口ServletContextListener抽象接口Action(部分web请求)
HttpSessionListener抽象接口
Spring提供的支持HandlerinterceptorAdapter类
HandlerInterceptor接口
级别系统级系统级非系统级

注意:拦截器的对象只能是实现了接口的类,而不能拦截URL这种链接。

1、过滤器Filter

参考:

Java中的Filter过滤器

Filter简介:

Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

它主要用于对用户请求HttpServletRequest进行预处理,也可以对响应HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPbn2t50-1662295786941)(images/Spring学习.assets/1444343-20190809135320083-426474192.png)]

FIlter的功能
  • 在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。

  • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据

Fileter的实现

Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,Filter接口源代码:

public abstract interface Filter{
    public abstract void init(FilterConfig paramFilterConfig) throws ServletException;
    public abstract void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain 
        paramFilterChain) throws IOException, ServletException;
    public abstract void destroy();
}
如何借助Fileter实现拦截功能

Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的

  • 调用目标资源之前,让一段代码执行。
  • 是否调用目标资源(即是否让用户访问web资源)。
  • 调用目标资源之后,让一段代码执行。
    • web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,
    • 开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
Filter开发流程

Filter开发分为2步:

  • 写java类实现Filter接口,并实现其doFilter方法。
  • 在web.xml 文件中使用和元素对编写的filter类进行注册,并设置它所能拦截的资源。

过滤器范例:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
 * @author yangcq
 * @description 过滤器Filter的工作原理
 */
public class FilterTest implements Filter{
 
    public void destroy() {
        System.out.println("----Filter销毁----");
    }
 
    public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException, ServletException {
        // 对request、response进行一些预处理
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        System.out.println("----调用service之前执行一段代码----");
        filterChain.doFilter(request, response); // 执行目标资源,放行
        System.out.println("----调用service之后执行一段代码----");
    }
 
    public void init(FilterConfig arg0) throws ServletException {
        System.out.println("----Filter初始化----");
    }
}

Web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <!--配置过滤器-->
  <filter>
      <filter-name>FilterTest</filter-name>
      <filter-class>com.yangcq.filter.FilterTest</filter-class>
  </filter>
  <!--映射过滤器-->
  <filter-mapping>
      <filter-name>FilterTest</filter-name>
      <!--/*”表示拦截所有的请求 -->
      <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

2、监听器Listener

参考:

Java 监听器

一、监听器简介

监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行
监听器可以用来检测网站的在线人数,统计网站的访问量等等!

二、监听器组件

监听器涉及三个组件:事件源,事件对象,事件监听器

  • 事件源:拥有事件
  • 监听器:监听事件源所拥有的事件(带事件对象参数的)
  • 事件对象:事件对象封装了事件源对象

当事件源发生某个动作的时候,它会调用事件监听器的方法,并在调用事件监听器方法的时候把事件对象传递进去。

我们在监听器中就可以通过事件对象获取得到事件源,从而对事件源进行操作!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W32oyhha-1662295786943)(images/Spring学习.assets/webp)]

三、模拟监听器

可以看参考的例子。还是比较好理解的。

事件源要与监听器有关系,就得注册监听器【提供方法得到监听器对象】
触发事件源的事件,实际会提交给监听器对象处理,并且把事件对象传递过去给监听器。

四、Servelet监听器

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别ServletContext, HttpSession和ServletRequest这三个域对象

1.监听对象的创建和销毁

HttpSessionListener、ServletContextListener、ServletRequestListener分别监控着Session、Context、Request对象的创建和销毁

  • HttpSessionListener(可以用来收集在线者信息)
  • ServletContextListener(可以获取web.xml里面的参数配置)
  • ServletRequestListener

示例【统计当前在线人数】:

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
 * @description HttpSessionListener监听器实现统计网站在线人数的功能
 */
public class SessionListener implements HttpSessionListener{
 
    public static int TOTAL_ONLINE_USERS = 0;
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
        TOTAL_ONLINE_USERS = (Integer) servletContext.getAttribute("TOTAL_ONLINE_USERS");
        // 如果用户退出,TOTAL_ONLINE_USERS自减1
        if(TOTAL_ONLINE_USERS == 0){
            servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
        }
        else{
            TOTAL_ONLINE_USERS--;
            servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
        }
    }
 
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
        TOTAL_ONLINE_USERS = (Integer) servletContext.getAttribute("TOTAL_ONLINE_USERS");
        // 如果用户登录,TOTAL_ONLINE_USERS自增1
        if(TOTAL_ONLINE_USERS == 0){
            servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
        }
        else{
            TOTAL_ONLINE_USERS++;
            servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
        }
    }
}

3、拦截器Interceptor

参考:

Java拦截器

一、拦截器的概念

java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者:

  • 在一个Action执行的前后执行一段代码
  • 在一个Action执行前阻止其执行
  • 同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前进行拦截,然后再之前或者之后加入某些操作。
二、拦截器的原理

大部分时候,拦截器方法都是通过代理的方式来调用的。

三、过滤器与拦截器的区别

过滤器可以简单的理解为“取你所想取”,过滤器关注的是web请求;拦截器可以简单的理解为“拒你所想拒”,拦截器关注的是方法调用,比如拦截敏感词汇。
1.拦截器是基于java反射机制来实现的,而过滤器是基于函数回调来实现的。(有人说,拦截器是基于动态代理来实现的)
2.拦截器不依赖servlet容器,过滤器依赖于servlet容器。
3.拦截器只对Action起作用,过滤器可以对所有请求起作用。
4.拦截器可以访问Action上下文和值栈中的对象,过滤器不能。
5.在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时调用一次。

四、Spring拦截器
  • 第一步 定义拦截器类,该类实现HandlerInterceptor接口
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@Component
public class MyInterceptor implements HandlerInterceptor {
 
    /**
     * 该方法是在执行执行servlet的 service方法之前执行的
     * 即在进入controller之前调用
     * @return 如果返回true表示继续执行下一个拦截器的PreHandle方法;如果没有拦截器了,则执行controller
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        String url = request.getRequestURI();
        System.out.println("进入Controller之前");
        if(url.indexOf("login") >= 0){
            return true;
        }
 
        String username = request.getParameter("username");
        if(username == null){
            return false;
        }
 
        return true;
    }
 
    /**
     *在执行完controller之后,返回视图之前执行,我们可以对controller返回的结果做处理
     * 执行顺序:先执行最后一个拦截器的postHandle方法,一次向前
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception{
 
        System.out.println("解析视图之前.....");
    }
 
    /**
     * 整个请求结束之后,即返回视图之后执行
     *该方法需要同一拦截器的preHandle返回true时执行,
     * 如果该拦截器preHandle返回false,则该拦截器的afterCompletion不执行
     * 执行顺序:先执行最后一个返回true的拦截器的afterCompletion,在依次向前
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                      Exception ex) throws Exception{
        System.out.println("视图解析完成...");
    }
}

  • Springboot环境配置拦截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
 
    @Autowired
    private MyInterceptor interceptor;
 
    public void addInterceptors(InterceptorRegistry registry) {
        //addPathPatterns指定拦截器要拦截的路径
        //excludePathPatterns指定拦截器不拦截的路径
        registry.addInterceptor(interceptor).addPathPatterns("/**").excludePathPatterns("/logout");
    }
}

  • Controller测试
@RestController
public class LoginController {
 
    @RequestMapping("/login")
    public String intercept(){
        System.out.println("Controller执行中....");
        return "Hello Interceptor!";
    }
 
    @RequestMapping("/logout")
    public String logout(){
        System.out.println("注销中....");
        return "logouting.....!";
    }
}

先访问login,测试结果如下:

进入Controller之前
Controller执行中....
解析视图之前.....
视图解析完成...

访问logout:

注销中....

4、总结

过滤器可以简单的理解为“取你所想取”,过滤器关注的是web请求;拦截器可以简单的理解为“拒你所想拒”,拦截器关注的是方法调用,比如拦截敏感词汇。

1.过滤器(Filter):所谓过滤器顾名思义是用来过滤的,Java的过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,这一点,是拦截器无法做到的。在Java Web中,你传入request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。filter 流程是线性的,url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收。

2.监听器(Listener):Java的监听器,也是系统级别的监听。监听器随web应用的启动而启动。Java的监听器在c/s模式里面经常用到,它会对特定的事件产生一个处理。监听在很多模式下用到,比如说观察者模式,就是一个使用监听器来实现的,在比如统计网站的在线人数。Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。

3.拦截器(Interceptor):java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性。Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现的动态代理。它依赖于具体的接口,在运行期间动态生成字节码。拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作。java的拦截器主要是用在插件上,扩展件上比如 Hibernate Spring Struts2等,有点类似面向切片的技术,在用之前先要在配置文件即xml,文件里声明一段的那个东西。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值