Spring framework 笔记

spring

1. 什么是spring

spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器。

2. 框架概述
  1. IoC(Inversion of Control)控制反转

    对象创建责任的反转,在spring中BeanFactory
    是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。
    XmlBeanFactory实现BeanFactory接口,通过获取xml配置文件数据,组成应用对象及对
    象间的依赖关系。

    简单认知就是将原本对象的创建控制权交由spring进行管理,使用者无需关注对象的创建过程;

    DI 依赖注入

    指 Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。

    spring的三种注入方式:
    set注入 、 接口注入 、 构造方法注入

  2. AOP面向切面编程

    • AOP是一种编程范式,提供从还有一个角度来考虑程序结构以完好面向对象编
      程(OOP)。
    • AOP为开发人员提供了一种描写叙述横切关注点的机制,并可以自己主动将横
      切关注点织入到面向对象的软件系统中。从而实现了横切关注点的模块化。
    • AOP可以将那些与业务无关,却为业务模块所共同调用的逻辑或责任。比如
      事务处理、日志管理、权限控制等。封装起来,便于降低系统的反复代码,降
      低模块间的耦合度,并有利于未来的可操作性和可维护性。
Spring 框架的主要优点具体如下:
  1. 方便解耦,简化开发

    Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。

  2. 方便集成各种优秀框架

    Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。

  3. 降低 Java EE API 的使用难度

    Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。

  4. 方便程序的测试

    Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。

  5. AOP 编程的支持

    Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。

  6. 声明式事务的支持

    只需要通过配置就可以完成对事务的管理,而无须手动编程。


Spring 官网列出的 Spring 的 6 个特征:
  • 核心技术 :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。
  • 测试 :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。
  • 数据访问 :事务,DAO支持,JDBC,ORM,编组XML。
  • Web支持 : Spring MVC和Spring WebFlux Web框架。
  • 集成 :远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
  • 语言 :Kotlin,Groovy,动态语言。

spring结构:

spring结构

  1. Data Access/Integration(数据访问/集成)

    • JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
    • ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
    • OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
    • JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
    • Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
  2. Web 模块

    • Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
    • Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
    • Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
    • WebSocket 模块:提供对WebSocket的集成支持。
  3. Core Container(核心容器)

    • Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
    • Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
    • Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
    • Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
  4. Spring Aspects :提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。

  5. Spring AOP :提供了面向切面的编程实现。

  6. Spring Test : 提供了对 JUnit 和 TestNG 测试的支持。

  7. Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。

IOC容器
  1. BeanFactory
    BeanFactory 是基础类型的 IoC 容器,它由 org.springframework.beans.factory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。

    BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。

    创建 BeanFactory 实例时,需要提供 Spring 所管理容器的详细配置信息,这些信息通常采用 XML 文件形式管理。其加载配置信息的代码具体如下所示:

    BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(“D://applicationContext.xml”));

  2. ApplicationContext

    ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。

    ApplicationContext 接口有两个常用的实现类:
    ClassPathXmlApplicationContext
    该类从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);

    FileSystemXmlApplicationContext该类从指定的文件系统路径中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作

    ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);

bean的使用方法
public class SpringTest{ 
    @Test
    public void Test(){
        String xmlPath="applicationContext.xml";
        String xmlPath="src/main/java/applicationContext.xml";
        //获取factory工厂对象(ApplicationContext)
        //以ClassPathXMLApplicationContext对象创建factory工厂
        ApplicationContext applicationContext= new ClassPathXmlApplicationContext(xmlPath);
        //以FileSystemXmlApplicationContext对象创建factory工厂        
        ApplicationContext applicationContext1=new FileSystemXmlApplicationContext(xmlPath1);
        //获取对象  对象获取后为object对象,需要对其进行强制转换      
        applicationContext.getBean("beanId");//beanId为applicationContest.xml文件中配置的bean的id
        applicationContext1.getBean("beanId");
    }
}
DI
依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入。具体介绍如下。
  1. 属性 setter 注入
    指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。

  2. 构造方法注入

    指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,每个参数代表一个依赖。

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <!-- 由 Spring容器创建该类的实例对象 -->
    <bean id="personDao" class="com.jm.notes.PersonDaoImpl" />

    <bean id="personService" class="com.jm.notes.di.PersonServiceImpl">
        <!-- 将personDao实例注入personService实例中 -->
        <!--setter方式注入-->
        <!--<property name="personDao" ref="personDao"/>-->
        <!--构造方法注入-->
        <constructor-arg index="0" ref="personDao"/>
    </bean>
</beans>
spring 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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

</beans>
常用的属性
属性名称描述
id是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成
nameSpring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开
class该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名
scope用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton
constructor-arg元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型
property元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名
ref 和 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用
value 和 等元素的子元素,用于直接指定一个常量值
list用于封装 List 或数组类型的依赖注入
set用于封装 Set 类型属性的依赖注入
map用于封装 Map 类型属性的依赖注入
entry 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值

bean的实例化

  1. 构造器实例化
       <!--bean实例化对象构造器-->
       <bean id="personTest" class="com.jm.notes.entity.PersonTest"/>
       <!--id为查找实例化对象的标记,通常命名为对象名, class为对象的完全限定名-->
    
  2. 静态工厂方法实例化
    /**
    *静态工厂类,里面包含创建对象的静态方法
    */
    public class StaticBaseFactory {   
        public static PersonTest createPersonTest(){
            return new PersonTest();
        }
    
    }
    
           <!--bean实例化对象静态工厂-->
               <bean id="personTest1" class="com.jm.notes.entity.StaticBaseFactory"
                     factory-method="createPersonTest"/>
           <!--id为查找实例化对象的标记,通常命名为对象名, class为静态工厂类的完全限定名
               factory-method为静态工厂类中对应实例对象的静态方法名
           -->
    
  3. 实例化工厂实例化
    /**
    *实例化工厂类,里面包含创建对象的普通方法
    */
    public class StaticBaseFactory {   
       //实例化类之前会先实例化工厂
       public StaticBaseFactory() {
           System.out.println("实例化工厂实例中");
       }
       public  PersonTest createPerson(){
           return new PersonTest();
       }
    
    }
    
    <!--bean实例化对象实例工厂-->
    <!--配置实例工厂-->
    <bean id="myBaseFactory" class="com.jm.notes.entity.StaticBaseFactory"/>
    <!--对象实例化配置-->    
    <bean id="personTest2" factory-bean="myBaseFactory"
              factory-method="createPerson"/>
    <!--实例化对象id为对象索引标记,factory-bean为指定的实例化工厂的id
    factory-method为指定实例化工厂中的对应对象的实例化方法
    -->
    

bean 的作用域

  1. 作用域的种类
    1. singleton(常用)

      单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。

    2. prototype(常用)

      原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。

    3. request

      在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。

    4. session

      在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。

    5. global Session

      在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。

singleton 作用域

singleton 是 Spring 容器默认的作用域,当一个 Bean 的作用域为 singleton 时,Spring 容器中只会存在一个共享的 Bean 实例,并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,就只会返回 Bean 的同一个实例。

在xml配置中配置对象实例(applicationContextBeanSingleton.xml)

<!--配置对象PersonTest实例化bean并指定作用域scope为Singleton-->
<bean id ="personTest" class="com.jm.notes.entity.PersonTest" scope="singleton"/>
<!--配置另一个PersonTest对象的bean不指定作用域scope-->
<bean id ="personTest1" class="com.jm.notes.entity.PersonTest"/>

测试方法


public class BeanSingletonTest{ 
    @Test
    public void SingletonTest(){
        String xmlPath = "applicationContextBaseSingleton.xml";
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext(xmlPath);
        System.out.println(applicationContext.getBean("personTest"));
        System.out.println(applicationContext.getBean("personTest"));
        System.out.println(applicationContext.getBean("personTest1"));
        System.out.println(applicationContext.getBean("personTest1"));
    }
 }

输出结果

com.jm.notes.entity.PersonTest@1563da5
com.jm.notes.entity.PersonTest@1563da5
com.jm.notes.entity.PersonTest@2bbf4b8b
com.jm.notes.entity.PersonTest@2bbf4b8b

从结果上看,前两次获取的Id为personTest的对象的地址完全相同,后两次Id为personTest1的对象地址完全相同,说明申明了Singleton的bean和默认的bean都为单例模式实例化对象。

###prototype 作用域

使用 prototype 作用域的 Bean 会在每次请求该 Bean 时都会创建一个新的 Bean 实例。因此对需要保持会话状态的 Bean(如 Struts2 的 Action 类)应该使用 prototype 作用域。

在xml配置中配置对象实例

<!-配置bean实例对象PersonTest,配置scope为prototype-->
<bean id="personTest" class="com.jm.notes.entity.PersonTest" scope="prototype"/>

test class:

public class PrototypeBeanTest{ 
    @Test
    public void prototypeTest(){
        String xmlPath="applicationContextBeanPrototype.xml";
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext(xmlPath);
        System.out.println(applicationContext.getBean("personTest"));
        System.out.println(applicationContext.getBean("personTest"));
        System.out.println(applicationContext.getBean("personTest"));
    }
 }

输出结果:

com.jm.notes.entity.PersonTest@1877ab81
com.jm.notes.entity.PersonTest@305fd85d
com.jm.notes.entity.PersonTest@458c1321

每次的输出结果都不相同,说明bean scope申明为prototype的作用域下每次都创建了不同的PersonTest的实例。


###Spring生命周期

spring生命周期图

spring bean 的装配

Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种形式的 Bean 的装配方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。

基于xml装配bean

基于xml的注入方式有值注入和构造方法注入两种方式,值注入需要确保值都有对应的setter方法和一个默认无参构造方法;构造方法注入需要有一个对应注入参数对应的构造方法。

创建personTest类:

package com.jm.notes.entity;

import java.util.Objects;

/**
 * @描述
 * @author(创建者) jm
 * @date(创建时间) 2020/5/27 13:15
 */

public class PersonTest {
    private String name;
    private Integer age;
    private String sex;

    public PersonTest(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public PersonTest() {
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "PersonTest{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }


}

xml(applicationContextBean.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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <!--Spring bean 基于xml的装配bean-->
    <!-- 使用设值注入方式装配Person实例 -->
    <bean  id="personTest" class="com.jm.notes.entity.PersonTest">
        <property name="name" value="我是值注入测试的人"/>
        <property name="age" value="12"/>
        <property name="sex" value=""/>
    </bean>
    <!-- 使用构造方法装配Person实例 -->
    <bean id="personTest1" class="com.jm.notes.entity.PersonTest">
        <constructor-arg index="0" value="我是构造方法注入测试的人"/>
        <constructor-arg index="1" value="12"/>
        <constructor-arg index="2" value=""/>
    </bean>
</beans>

test类:

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @描述
 * @author(创建者) jm
 * @date(创建时间) 2020/5/27 15:10
 */
public class BeanAssemblyXmlTest {

    @Test
    public void assemblyXmlTest(){
        String xmlPath ="applicationContextBean.xml";
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
        System.out.println(applicationContext.getBean("personTest"));
        System.out.println(applicationContext.getBean("personTest1"));
    }
}

输出结果:

PersonTest{name='我是值注入测试的人', age=12, sex='男'}
PersonTest{name='我是构造方法注入测试的人', age=12, sex='女'}

######xml装配的当bean的数量过多时会导致xml文件变得臃肿不好维护和升级。

基于annotation(注解)装配bean

基于Annotation的常用注解:

  1. @Component

    用于表示一个组件bean,可以作用于任何层次上。

  2. @Repository

    用于数据访问层(dao层)的类标注为spring的bean。

  3. @Service

    通常作用域业务层(service层),用于将业务层的类标注为spring的bean。

  4. @Controller

    通常用于控制层(Controller层),用于将控制层的类标注为spring的bean。

  5. @Autowired

    用于对bean属性变量、属性的setter方法和构造函数进行标注,配合对应的注解助理bean的自动配置工作。默认按bean的类型进行装配。

  6. @Resource
    作用和@Autowired相同,其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。

    @Resource 中有两个重要属性:name 和 type。

    Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。

  7. @Qualifier

    与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。

Spring自动装配

自动装配是指Spring容器合一自动装配相互协作的Bean之间的关联关系,将Bean注入到其他的Bean的property中。

自动装配使用bean 元素中的autowire属性:
< bean id="" class="" autowire=""/>

autowire属性的值:

名称说明
byName根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。
byType根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。
constructor根据构造方法的参数的数据类型,进行 byType 模式的自动装配。
no默认情况下,不使用自动装配,Bean 依赖必须通过 ref 元素定义。
default----

实例代码:

public class TestController {


    private TestService testService;

    public void setTestService(TestService testService) {
        this.testService = testService;
    }

    public TestController() {
    }

    public TestController(TestService testService) {
        this.testService = testService;
    }


    public void add(){
        testService.add();
        System.out.println("TestController add方法被调用");
    }
}

public interface TestDao {
    void add();
}

public class TestDaoImpl implements TestDao {
    @Override
    public void add() {
        System.out.println("TestDao add方法被调用了-----");
    }
}

public interface TestService {
    void add();
}

public class TestServiceImpl implements TestService {
    private TestDao testDao;

    public void setTestDao(TestDao testDao) {
        this.testDao = testDao;
    }

    public TestServiceImpl() {
    }

    public TestServiceImpl(TestDao testDao) {
        this.testDao = testDao;
    }

    @Override
    public void add() {
        testDao.add();
        System.out.println("TestService add方法被调用");
    }
}
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!--自动配置byName 有setter方法-->
    <!--<bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
    <bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
    autowire="byName"/>
    <bean id="testController" class="com.jm.notes.annotation.TestController"
          autowire="byName"/>-->
    <!--byType-->
    <!--<bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
    <bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
          autowire="byType"/>
    <bean id="testController" class="com.jm.notes.annotation.TestController"
          autowire="byType"/>-->
    <!--constructor 需要有对应的构造函数和无参构造函数-->
    <bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
    <bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
          autowire="constructor"/>
    <bean id="testController" class="com.jm.notes.annotation.TestController"
          autowire="constructor"/>

</beans>

test代码:

    @Test
    public void TestController1(){
        //自动装配
        ApplicationContext applicationContext1 =new ClassPathXmlApplicationContext("ApplicationContextAutowire.xml");
        System.out.println(); applicationContext1.getBean("testService");
        TestController testController = (TestController) applicationContext1.getBean("testController");
        testController.add();
    }
TestDao add方法被调用了-----
TestService add方法被调用
TestController add方法被调用

AOP(Aspect Oriented Programming) 面向切面编程

面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。

AOP将业务逻辑的各个组成部分进行隔离,开发是只用专心编写核心业务,提高开发效率。
其应用主要体现在事务处理、日志管理、权限控制和异常处理等方面。

利用JDK实现AOP
/**
 * @描述 切面类
 * @author(创建者) jm
 * @date(创建时间) 2020/5/28 11:05
 */
@Component
public class AopAspect {

    public void MyBefore(){
        System.out.println("method  start before");
    }
    public void MyAfter(){
        System.out.println("method end after");
    }
}

public interface CustomerDao {
    void add();

    void update();

    void delete();

    void find();
}
@Repository
public class CustomerDaoImpl implements CustomerDao {
    @Override
    public void add() {
        System.out.println("Customer add method running----");
    }

    @Override
    public void update() {
        System.out.println("Customer update method running ---");
    }

    @Override
    public void delete() {
        System.out.println("Customer delete method running ----");
    }

    @Override
    public void find() {
        System.out.println("Customer find method running ----");
    }
}

/**
 * @描述 代理类
 * @author(创建者) jm
 * @date(创建时间) 2020/5/28 11:10
 */
@Component("factory")
public class AOPBaseFactory {
    //创建切面类实例
    private AopAspect aopAspect;

    @Autowired
    public void setAopAspect(AopAspect aopAspect) {
        this.aopAspect = aopAspect;
    }

    //目标类
    @Resource
    private CustomerDao customerDao;

    public static CustomerDao getBean() {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContextAnnotation.xml");
        final AOPBaseFactory baseFactory = (AOPBaseFactory)applicationContext.getBean("factory");
        //使用代理
        return (CustomerDao) Proxy.newProxyInstance(
                AOPBaseFactory.class.getClassLoader(),
                new Class[]{CustomerDao.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        baseFactory.aopAspect.MyBefore();
                        Object o = method.invoke(baseFactory.customerDao, args);
                        baseFactory.aopAspect.MyAfter();
                        return o;
                    }
                }
        );

    }
}

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!--使用context命名空间,通知spring扫描指定目录,进行注解的解析-->
    <context:component-scan base-package="com.jm.notes"/>
</beans>

test 类

public class AOPTest {
    @Test
    public void test() {
        // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
        CustomerDao customerDao = AOPBaseFactory.getBean();
        // 执行方法
        customerDao.add();
        customerDao.update();
        customerDao.delete();
        customerDao.find();
    }
}
method  start before
Customer add method running----
method end after
method  start before
Customer update method running ---
method end after
method  start before
Customer delete method running ----
method end after
method  start before
Customer find method running ----
method end after

###Spring通知实现AOP
######Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型:

名称说明
org.springframework.aop.MethodBeforeAdvice(前置通知)在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
org.springframework.aop.AfterReturningAdvice(后置通知)在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
org.aopalliance.intercept.MethodInterceptor(环绕通知)在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。
org.springframework.aop.ThrowsAdvice(异常通知)在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。
org.springframework.aop.IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。

确保存在aop的jar包。
proxy的基本配置属性:

  1. target: 代理的目标对象
  2. proxyInterface: 代理实现的接口,多个接口使用< list>< value>< /value>< /list>标签
  3. proxyTargetClass: 是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
  4. interceptorNames: 需要植入目标的 Advice
  5. singleton: 返回的代理是否为单例,默认为 true(返回单实例)
  6. optimize: 当设置为 true 时,强制使用 CGLIB

实例代码:

package com.jm.notes.AOP;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @描述 切面类
 * @author(创建者) jm
 * @date(创建时间) 2020/5/28 11:05
 */
@Component
public class AopAspectCopy implements MethodInterceptor {

    public void MyBefore(){
        System.out.println("(Advice)method  start before");
    }
    public void MyAfter(){
        System.out.println("(Advice)method end after");
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("环绕通知");
        MyBefore();
        // 执行目标方法
        Object proceed = methodInvocation.proceed();
        MyAfter();
        return proceed;
    }
}
class AopAspectCopy1 implements MethodBeforeAdvice {

    public void MyBefore(){
        System.out.println("(MethodBeforeAdvice)method  start before");
    }
    public void MyAfter(){
        System.out.println("(MethodBeforeAdvice)method end after");
    }


//在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知");
        MyBefore();
        // 执行目标方法
        //method.invoke(o,objects);
        MyAfter();
    }
}

class AopAspectCopy2 implements AfterReturningAdvice {

    public void MyBefore(){
        System.out.println("(MethodBeforeAdvice)method  start before");
    }
    public void MyAfter(){
        System.out.println("(MethodBeforeAdvice)method end after");
    }


    //在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知");
        MyBefore();
        MyAfter();
    }
}

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <!--目标类 -->
    <bean id="customerDao" class="com.jm.notes.AOP.CustomerDaoImpl" />
    <!-- 通知 advice -->
    <bean id="myAspect" class="com.jm.notes.AOP.AopAspectCopy" />
    <bean id="myAspect1" class="com.jm.notes.AOP.AopAspectCopy1" />
    <bean id="myAspect2" class="com.jm.notes.AOP.AopAspectCopy2" />
    <!--生成代理对象 -->
    <bean id="customerDaoProxy"
          class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--代理实现的接口 list标签可以编写多个接口 -->
        <property name="proxyInterfaces" >
            <list >
                <value>com.jm.notes.AOP.CustomerDao</value>
            </list>
        </property>
        <!--代理的目标对象 -->
        <property name="target" ref="customerDao"/>
        <!--用通知增强目标 -->
        <property name="interceptorNames" value="myAspect" />
        <!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
        <property name="proxyTargetClass" value="true" />
    </bean>
    <!--生成代理对象 -->
    <bean id="customerDaoProxy1"
          class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--代理实现的接口 list标签可以编写多个接口 -->
        <property name="proxyInterfaces" >
            <list >
                <value>com.jm.notes.AOP.CustomerDao</value>
            </list>
        </property>
        <!--代理的目标对象 -->
        <property name="target" ref="customerDao"/>
        <!--用通知增强目标 -->
        <property name="interceptorNames" value="myAspect1" />
        <!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
        <property name="proxyTargetClass" value="true" />
    </bean>
    <!--生成代理对象 -->
    <bean id="customerDaoProxy2"
          class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--代理实现的接口 list标签可以编写多个接口 -->
        <property name="proxyInterfaces" >
            <list >
                <value>com.jm.notes.AOP.CustomerDao</value>
            </list>
        </property>
        <!--代理的目标对象 -->
        <property name="target" ref="customerDao"/>
        <!--用通知增强目标 -->
        <property name="interceptorNames" value="myAspect2" />
        <!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
        <property name="proxyTargetClass" value="true" />
    </bean>
</beans>

test类

import com.jm.notes.AOP.AOPBaseFactory;
import com.jm.notes.AOP.CustomerDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @描述
 * @author(创建者) jm
 * @date(创建时间) 2020/5/28 11:27
 */
public class AOPTest {

    @Test
    public void test1() {
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
        // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy");
        // 执行方法
        customerDao.add();
        customerDao.update();
        customerDao.delete();
        customerDao.find();
    }
    @Test
    public void test2() {
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
        // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy1");
        // 执行方法
        customerDao.add();
        customerDao.update();
        customerDao.delete();
        customerDao.find();
    }
    @Test
    public void test3() {
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
        // 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
        CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy2");
        // 执行方法
        customerDao.add();
        customerDao.update();
        customerDao.delete();
        customerDao.find();
    }
}

环绕通知
(Advice)method  start before
Customer add method running----
(Advice)method end after
环绕通知
(Advice)method  start before
Customer update method running ---
(Advice)method end after
环绕通知
(Advice)method  start before
Customer delete method running ----
(Advice)method end after
环绕通知
(Advice)method  start before
Customer find method running ----
(Advice)method end after


前置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer add method running----
前置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer update method running ---
前置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer delete method running ----
前置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer find method running ----


Customer add method running----
后置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer update method running ---
后置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer delete method running ----
后置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after
Customer find method running ----
后置通知
(MethodBeforeAdvice)method  start before
(MethodBeforeAdvice)method end after

使用 AspectJ 开发 AOP 使用 Annotation方式

AspectJ 除了需要导入 Spring AOP 的 JAR 包以外,还需要导入与 AspectJ 相关的 JAR 包:spring-aspects-*;

Annotation 注解的介绍
名称说明
@Aspect用于定义一个切面。
@Before用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。
@Around用于定义环绕通知,相当于MethodInterceptor。
@AfterThrowing用于定义抛出通知,相当于ThrowAdvice。
@After用于定义最终final通知,不管是否异常,该通知都会执行。
@DeclareParents用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。

@RestController vs @Controller

  • @Controller 返回一个页面
  • @RestController 返回JSON 或 XML 形式数据
  • @Controller +@ResponseBody 返回JSON 或 XML 形式数据
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值