Spring

一、spring框架概述

1.spring是轻量级的开源javaEE框架

2.spring可以解决企业应用开发的复杂性

3.spring有两个核心部分:IOC和AOP

(1)IOC:控制反转,把创建对象过程交给spring进行管理

(2)AOP:面向切面,不修改源代码进行功能增强

4.spring特点:

(1)方便解耦,简化开发

(2)AOP编程支持

(3)方便程序测试

(4)方便和其他框架进行整合

(5)方便进行事务操作

(6)降低API开发难度

二、spring的核心组件

1.spring的核心组件

 2.核心容器

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

  1. spring-core 模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
  2. spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
  3. context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。
  4. spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。

依赖关系如图: 

 3.数据访问/集成

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

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

  1. JDBC 模块提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
  2. ORM 模块提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合,比如前面提及的事务管理。
  3. OXM 模块提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
  4. JMS 模块包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 spring-messaging 模块。
  5. 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细)

4.web

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

  1. Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
  2. Web-MVC 模块为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
  3. Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
  4. Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。  

 5.其他

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

  1. AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
  2. Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
  3. Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
  4. Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

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

三、SpringIOC的使用

3.1基本使用

3.1.1创建项目并导入SpringIoc相关的jar包

创建一个maven项目

 在pom文件中,引入Spring的IoC核心jar包

<!--依赖-->
<dependencies>
    <!--引入spring的context依赖,可以传递出aop beans core expression-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.4</version>
    </dependency>
</dependencies>

3.1.2创建user类

public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String address;
    private Date birth;

3.1.3创建Spring的核心配置文件spring-context.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
    <bean id="userId"  name="user" class="com.bjpowernode.domain.User" />
</beans>

3.1.4编写测试程序

public class Test {
    public static void main(String[] args) {
        //根据 spring的配置文件 创建 应用容器
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        System.out.println("马上获取bean");
        //从容器中获取 对象
        User user = (User) context.getBean("user");
        System.out.println(user);
    }
}

3.2 Bean标签属性介绍

3.2.1  id

是 bean的唯一标识 一个bean,其id 值只能有一个 。整个IOC 容器id 值不允许重复,使用名称作为key。

3.2.2 name

一个bean的名称,可以存在多个,多个之间使用逗号分隔。不论bean有没有定义name属性,默认id都会当做name。

3.2.3 class   

bean的具体的类型,包名和类名组成。

3.2.4 scope

bean的作用域

prototype :非单例,每次获取都会创建一个新的bean对象。

singleton : 单例,多次获取永远同一个bean, 默认值。

request : 一次请求,基于web项目的bean的作用域。

session : 一次会话,基于web项目的bean的作用域。

3.2.5  lazy-init

延迟初始化,默认只要加载了配置文件。bean对象就会被初始化,lazy-init则是获取时才会初始化。只针对单例模式有效,非单例每次获取都会创建,没有延迟初始化的意义

3.2.6 depends-on

初始化时依赖的对象,当前对象初始化前需先初始化depends-on指定的对象

3.2.7  init-method

对象初始化后,调用的方法

3.2.8 destroy-method

对象销毁时,调用的方法

3.2.9 autowire

   属性自动装配

   byName 根据属性名称装配

   byType 根据类型装配

3.2.10  autowire-candidate

   是否允许作为自动装配的候选项

   true 作为自动装配的候选项

false 不作为自动装配的候选项

3.2.11  primary

优先使用该bean,因为Spring需要支持使用类型查找对象,在一个大类型下,可能存在多个小类型。如果根据大类型装配属性时,不知道使用哪个具体的对象,则可以根据primary设置优先级。

<?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.xsd">
    <!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
   <!-- <bean id="userId"  name="user" class="com.bjpowernode.domain.User"  />-->
    <!--
        id : bean的唯一标识  整个IOC容器不能重复。
        name : bean的key,多个name之间使用逗号,
        class : 具体的bean的全路径
        scope : bean的作用域
                singleton 单例  默认
                prototype 非单例
        lazy-init="true" 获取时创建对象
        depends-on="test" 默认自上而下创建  depends-on 会优先创建 depends-on 对应的bean
        init-method : 对象创建后调用的方法
        destroy-method :对象销毁时调用的方法, 容器调用close
        autowire : 属性自动装配
                    byName 根据属性名装配
                    byType 根据属性类型装配

        primary :  当存在多个同样的类型时, primary 为true 则优先使用该bean

    -->
    <bean id="userId2" name="user1,user2" class="com.bjpowernode.domain.User" scope="singleton" depends-on="test" init-method="init" destroy-method="destory" primary="true"  />
    <!--  test类 -->
    <bean name="test" class="com.bjpowernode.domain.Test" />

    <bean name="userService1" class="com.bjpowernode.service.impl.UserServiceImpl1" primary="true" />
   <bean name="userService2" class="com.bjpowernode.service.impl.UserServiceImpl2" />
</beans>

`/*4

package com.bjpowernode.service.impl;

import com.bjpowernode.service.IUserService;

/**
 * @Description: 接口实现类1
 * @author: Mr.T
 * @date 2020-09-26 14:33
 */
public class UserServiceImpl1 implements IUserService {
}

package com.bjpowernode.service.impl;

import com.bjpowernode.service.IUserService;
public class UserServiceImpl2 implements IUserService {
}

package com.bjpowernode.domain;
public class User {

    private Integer id;

    private String name;

    public User(){
        System.out.println("构造方法执行  user 对象进行创建");
    }

    public  void sleep(){
        System.out.println("早睡早起!!!");
    }

    public void init(){
        System.out.println("对象初始化后调用的方法");
    }
    public  void  destory(){
        System.out.println("对象销毁时调用");
    }
}

package com.bjpowernode.test;

import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        //根据 spring的配置文件 创建 应用容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        System.out.println("马上获取bean");
        //从容器中获取 对象
        // 装备对象 : XX   XXX1  XX2   xx3
        User user = (User) context.getBean("user2");//
        System.out.println("21:"+user);
        System.out.println("22:"+context.getBean("user2"));

        //获取接口的类型
        System.out.println(context.getBean(IUserService.class));
        context.close();//关闭容器  此时会调用销毁的方法
    }
}

3.3 Bean对象创建的4种方式

3.3.1  构建方法的创建 

 使用构造方法创建bean对象,是spring默认的创建方式。

<!-- 使用构造方法创建对象 -->
<bean id="user" class="com.bjpowernode.domian.User" />

3.3.2 静态工厂创建

<!-- 使用静态工厂创建对象 -->
<!--
    id : bean 的唯一标识
    class : 工厂类
    factory-method : 工厂方法
 -->
<bean id="user2" class="com.bjpowernode.factory.UserStaticFactory" factory-method="getObj" />

package com.bjpowernode.factory;

import com.bjpowernode.domian.User;

public class UserStaticFactory {
    /**
     * 静态工厂中用于创建对象的方法
     * @return
     */
    public static User getObj(){
        System.out.println("静态工厂中 创建对象的方法 执行了");
        return  new User();
    }
}

3.3.3 非静态工厂创建

<!--
    非静态工厂创建对象
        在非静态工厂中,创建对象的方法是非静态方法。非静态方法的执行,首先需要该类对象
        注意: 使用非静态工厂创建对象,首先需要创建工厂类对象
-->
<!-- 工厂类对象 -->
<bean id="userFactory" class="com.bjpowernode.factory.UserFactory" />
<!-- 使用非静态工厂创建对象 -->
<!--
    factory-bean : 非静态工厂对象
    factory-method : 创建对象的非静态方法
-->
<bean id="user3" factory-bean="userFactory" factory-method="getObj" />

package com.bjpowernode.factory;

import com.bjpowernode.domian.User;

public class UserFactory {
    /**
     * 工厂中用于创建对象的方法
     * @return
     */
    public  User getObj(){
        System.out.println("非静态工厂中创建对象的方法 执行了");
        return  new User();
    }
}

3.3.4 注解创建

Spring为简化对象的创建方式,提供了注解。

组件注解        

@Component

表示该类为一个被Spring管理的组件。但是,由于在开发中为了让代码的可读性更高。

Spring基于分层思想,将需要创建的组件分为以下几类:

@Controller

@Controller注解,标识该类是controller层的类。并且,注意在使用SpringMVC时,所有的Constroller,必须使用@Controller注解。

@Service

@Service注解,标识该类是业务层的类。

@Repository

@Repository注解,标识该类是操作数据层的类。

注意:

以上注解是Spring中定义的创建对象的注解,都可以创建对象,如果该类有明确的作用,有自己所属的层,则建议使用相应的注解,如果实在无法区分该类所属层,可以使用@Component注解。

 注解的使用步骤

开启组件扫描

在spring的核心配置文件中,开启注解扫描,让Spring将被注解修饰的类,创建对相关。

<!--
    开启组件扫描
 -->
<context:component-scan base-package="com.bjpowernode.*" />

添加注解

package com.bjpowernode.domian;

import org.springframework.stereotype.Component;
@Component
public class Person {

    public Person(){
        System.out.println("Person的构造方法.........");
    }
}

3.4 IOC 属性注入的3种方式

为对象属性设置值,就是属性注入。

构造方法属性注入

<?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.xsd">

        <!-- 无参构造方法 -->

        <bean class="com.bjpowernode.domain.User" />

        <!--

           有参数的构造方法

                此时是2个参数的构造方法

                index : 参数下标  从 0开始

                value : 属性的值

         -->

        <bean id="user2" class="com.bjpowernode.domain.User" >

            <constructor-arg index="0" value="1001" />

            <constructor-arg index="1" value="韩梅梅" />

        </bean>

        <!-- 有参数的构造方法

                使用index下标查找 属性 存在问题 都只有一个参数 则默认使用后面的构造方法

                可以使用 type 指定参数的类型

                更推荐 使用name属性 : name表示构造器中参数的名称

         -->

        <bean id="user3" class="com.bjpowernode.domain.User">

            <constructor-arg index="0" value="1001" type="java.lang.Integer"  />

        </bean>

        <bean id="user4" class="com.bjpowernode.domain.User">

                <constructor-arg name="name" value="韩梅梅" />

        </bean>

</beans>
package com.bjpowernode.domain;

  

  public class User {

  

    /**

     *  id 属性

     */

    private Integer id;

    /**

     *  name 属性

     */

    private String name;

  

  

    public User(){

        System.out.println("无参数构造方法");

    }

  

    public User(Integer id) {

        System.out.println("id参数方法");

        this.id = id;

    }

  

    public User(String name) {

        System.out.println("name参数构造方法");

        this.name = name;

    }

  

    public User(Integer id, String name) {

        System.out.println("id,name参数构造方法");

        this.id = id;

        this.name = name;

    }

  

    public Integer getId() {

        return id;

    }

  

    public void setId(Integer id) {

        this.id = id;

    }

  

    public String getName() {

        return name;

    }

  

    public void setName(String name) {

        this.name = name;

    }

}

 set方法属性注入

<?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.xsd">

         <!--

           使用set方法进行属性注入

            property 表示属性

                name : 表示属性对应的set方法名,去掉set前缀,首字母小写。并不是真正的属性名

         -->

        <bean id="student1" class="com.bjpowernode.domain.Student">

           <property name="studentId" value="1001" />

        </bean>

</beans>

 注解属性注入

在spring中,为了简化属性的注入,Spring提供注解:@Autowired,Spring会自动从IOC容器中,为这个属性查找相应类型的值,进行注入。

开启包的注解扫描

<context:component-scan base-package="com.*" />

使用注解

package com.bjpowernode.domain;

  

  import org.springframework.beans.factory.annotation.Autowired;

  

  import java.util.Date;

  public class Student {

  

    public Integer id;

  

    public String name;

  

    @Autowired  //使用注解自动注入

    //User 对象

    public User user;

    public void setStudentId(Integer id) {

        System.out.println("set方法被调用了............");

        this.id = id;

    }

 /*   public void setUser(User user) {

        this.user = user;

    }*/

  }

注意:

在使用自动注入时,可以在bean标签上,配置autowire,但是此时必须有该属性的set方法,@Autowired注解,是不需要set方法的。

如果是在xml中注入对象,值使用ref属性。value属性,只支持boolean,数字,字符串等。

常见类型的属性注入

在Spring中,提供了丰富的标签,进行各种属性的注入。常见的类型:

数字、字符串、boolean、数组、set、list、map、properties。

package com.bjpowernode.domain;

  

  import java.util.List;

  import java.util.Map;

  import java.util.Properties;

  import java.util.Set;

  

  public class Person {

  

    private Integer id;

  

    private String name;

  

    private boolean sex;// true  男 false 女

  

    private String[] likes;//爱好

  

    private Set<String> girlFriends; //女朋友

  

    private List<Dream> dreams;//梦想

  

    private Map<String,String> house; //房子

  

    private Properties properties; //配置文件属性

  

    public Integer getId() {

        return id;

    }

  

    public void setId(Integer id) {

        this.id = id;

    }

  

    public String getName() {

        return name;

    }

  

    public void setName(String name) {

        this.name = name;

    }

  

    public boolean getSex() {

        return sex;

    }

  

    public void setSex(boolean sex) {

        this.sex = sex;

    }

  

    public String[] getLikes() {

        return likes;

    }

  

    public void setLikes(String[] likes) {

        this.likes = likes;

    }

  

    public Set<String> getGirlFriends() {

        return girlFriends;

    }

  

    public void setGirlFriends(Set<String> girlFriends) {

        this.girlFriends = girlFriends;

    }

  

    public List<Dream> getDreams() {

        return dreams;

    }

  

    public void setDreams(List<Dream> dreams) {

        this.dreams = dreams;

    }

  

    public Map<String, String> getHouse() {

        return house;

    }

  

    public void setHouse(Map<String, String> house) {

        this.house = house;

    }

  

    public Properties getProperties() {

        return properties;

    }

  

    public void setProperties(Properties properties) {

        this.properties = properties;

    }

}package com.bjpowernode.domain;

  

  public class Dream {

  

    private String title;

  

    public String getTitle() {

        return title;

    }

  

    public void setTitle(String title) {

        this.title = title;

    }

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

  <beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

  

  

  

    <!-- 简单的属性注入 : 基本数据类型 + string -->

    <bean id="person1" class="com.bjpowernode.domain.Person" >

        <!-- Integer 类型的属性注入 -->

        <property name="id" value="1001" />

        <!-- String 类型的属性注入 -->

        <property name="name" value="佚名" />

        <!-- boolean 属性注入 -->

        <property name="sex"  value="true" />

    </bean>

    <!-- 数组类型 -->

    <bean id="person2" class="com.bjpowernode.domain.Person" >

        <property name="likes" >

            <array>

                <value>足球</value>

                <value>篮球</value>

                <value>羽毛球</value>

            </array>

        </property>

    </bean>

    <!--  set 类型注入 -->

    <bean id="person3" class="com.bjpowernode.domain.Person" >

        <property name="girlFriends" >

           <set>

                <value>韩梅梅</value>

                <value>Lucy</value>

                <value>Lucy</value>

                <value>Rose</value>

           </set>

        </property>

    </bean>

    <bean id="dream1" class="com.bjpowernode.domain.Dream">

        <property name="title" value="数钱数到手抽筋" />

    </bean>

    <!-- list 类型 -->

    <bean id="person4" class="com.bjpowernode.domain.Person" >

        <property name="dreams" >

            <list>

                <bean class="com.bjpowernode.domain.Dream">

                    <property name="title" value="解放全人类" />

                </bean>

                <bean class="com.bjpowernode.domain.Dream">

                    <property name="title" value="世界和平" />

                </bean>

                <ref bean="dream1"/>

            </list>

        </property>

    </bean>

  

    <!-- map结构   -->

    <bean id="person5" class="com.bjpowernode.domain.Person" >

        <property name="house" >

            <map>

                <entry key="wh" value="江滩" />

                <entry key="bj" value="后海" />

                <entry key="hz" value="西湖" />

            </map>

        </property>

    </bean>

  

    <!-- properties  -->

    <bean id="person6" class="com.bjpowernode.domain.Person" >

        <property name="properties" >

           <props>

               <prop key="driver">驱动</prop>

               <prop key="url">url</prop>

           </props>

        </property>

    </bean>

</beans>
package com.bjpowernode.test;

  

  import com.bjpowernode.domain.Dream;

  import com.bjpowernode.domain.Person;

  import com.bjpowernode.domain.Student;

  import com.bjpowernode.domain.User;

  import org.springframework.context.ApplicationContext;

  import org.springframework.context.support.ClassPathXmlApplicationContext;

  

  import java.util.Arrays;

  import java.util.List;

  import java.util.Properties;

  

  public class Test {

  

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");

        Person person1 = (Person) context.getBean("person1");

        System.out.println(person1.getId() +"  "+ person1.getName() + " "+ person1.getSex() );

        Person person2 = (Person) context.getBean("person2");

        String[] likes = person2.getLikes();

        System.out.println(Arrays.asList(likes));

        Person person3= (Person) context.getBean("person3");

        System.out.println(person3.getGirlFriends());

  

        Person person4 = (Person) context.getBean("person4");

        List<Dream> dreams = person4.getDreams();

        System.out.println(dreams.get(0).getTitle());

        System.out.println(dreams.get(1).getTitle());

        System.out.println(dreams.get(2).getTitle());

  

        Person person5 = (Person) context.getBean("person5");

        System.out.println(person5.getHouse());

  

        Person person6 = (Person) context.getBean("person6");

        Properties properties = person6.getProperties();

        System.out.println(properties.getProperty("driver"));

        System.out.println(properties.getProperty("url"));

    }

}

四、AOP

4.1 AOP简介

代理

静态代理

静态代理,每个被代理类都需要创建对应的代理类。随着程序的扩展,代理类也会增多,臃肿,维护量变多,为了解决这个问题,Java中,提供了动态代理技术,开发者不需要自己定义代理类,代理类由JDK动态的创建,开发只需要指定被代理的类即可。

4.2 动态代理

4.2.1JDK动态代理

proxy:

该类提供了方法创建代理类和代理类的对象的方法

创建一个代理类并返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

loader : 类加载器,指定类加载器,是为了精确的定位类

interfaces : 接口Class类,使用JDK的反射,必须要有接口

h :InvocationHandler ,代理的处理器,每个代理类都有一个关联的处理器

InvocationHandler:

是每个代理类对应的处理器

Object  方法调用的返回值,可以作为被代理的方法调用的返回值

proxy : 代理类对象

method : 目标类中被代理的方法

args : 目标类中被代理的方法的运行参数

Object invoke(Object proxy,Method method,Object[] args)

JDK动态代理的不足

  在JDK中使用动态代理,必须有类的接口。因为生成的代理需要实现这个接口,这样我们生成的代理类对象,才能转化为代理目标的接口对象,然后根据接口中的方法,调用处理器中invoke方法。

4.2.Cglib动态代理

为了弥补JDK动态代理的不足,第三方组织封装一套工具包,cglib的工具包,这套包不基于接口,基于父子继承,通过重写的形式扩展方法,但是这个子类工具自动生成的。

早期,Cglib动态代理,性能相于JDK的动态代理高一些。JDK进行一些列优化,目前Spring默认使用的动态代理JDK,也支持Cglib。

Cglib动态代理的使用

​​​​​​​MethodInterceptor:

cglib中,提供的对方法执行拦截的接口。其中intercept是对具体方法进行拦截处理的方法。
 public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) 
Object  : 方法执行返回的结果
obj :增强类的对象
method :目标方法
proxy :用于回调的方法的对象

 ​​​​​​​动态代理的不足

不论是JDK的动态代理,还是第三方cglib动态代理,都需要开发者编写代码处理程序。程序结构基本上大同小异,重复造轮子。基于这样的情况,在Spring中,提供了2种方式:xml配置形式和注解形式,使用动态代理。这种模式就是Spring Aop技术。其底层依然是动态代理。

4.3 SpringAoP的配置 

4.3.1 AoP的相关配置

连接点(JoinPoint):所谓连接点是指那些被拦截的点,而spring中这些点就是指方法,因为spring只支持方法类型的连接点。

切入点(PointCut):所谓切入点就是指我们要对那些JoinPoint进行拦截的定义,指的是具体的拦截的位置

增强/通知(Advice) : 增强就是对具体的连接点进行扩展的功能。由于一般对方法进行增强,分为在方法前执行或者方法后,或者发生异常执行等等,所以增强被分为:前置增强(前置通知)、后置增强(后置通知)、环绕通知(环绕增强)、异常增强(异常通知)

引介(Introduction):引介是一种特殊的Advice,在不修改代码的前提下,引介可以在运行期为类动态的添加一些方法或Field.

目标(Target) :被代理的类(需要增强类)

织入(Weaving) :把Advice应用到Target的过程

代理(Proxy):使用AOP配置后产生的代理类

切面(Aspect):切点和增强整合形成了切面

4.3.2 Spring自身AOP具体配置

①​​​​​​​引入aop相关jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>02-spring-aop01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.2.0.RELEASE</spring.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>

    </dependencies>


</project>

 ②​​​​​​​定义增强类

​​​​​​​前置增强-MethodBeforeAdvice​​​​​​​

package com.bjpowernode.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @Description: 后置增强接口
 * @author: Mr.T
 * @date 2020-09-27 15:55
 */
public class MyAfterAdvice implements AfterReturningAdvice {

    /**
     *
     * @param returnValue 被增强的方法运行后返回的数据
     * @param method     被增强的方法
     * @param args      方法运行的参数
     * @param target       目标类对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置增强方法");
    }
}

 ​​​​​​​环绕增强-MethodInterceptor​​​​​​​

package com.bjpowernode.advice;


import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * @Description: 环绕增强
 * @author: Mr.T
 * @date 2020-09-27 16:04
 */
public class MyAroundAdvice implements MethodInterceptor {


    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("前置增强");
        Object rs = invocation.proceed();//调用 目标方法
        System.out.println("后置增强");
        return rs;
    }
}

 ​​​​​​​异常增强-ThrowsAdvice

异常增强中的增强方法必须叫:afterThrowing,并且必须定义参数接收发生的异常信息。

import org.springframework.aop.ThrowsAdvice;

/**
 * @Description: 异常增强类
 * @author: Mr.T
 * @date 2020-09-27 16:12
 */
public class MyExceptionAdvice implements ThrowsAdvice {

    public void afterThrowing(Exception ex){
        System.out.println("异常增强的方法!!!!!!");
    }
}

③目标类



public interface ITargetClass {
    /**
     * 待增强的目标方法
     */
    void targetMethod();

    /**
     * 待增强的方法
     * @param msg
     * @return
     */
    String afterTargetMethod(String msg);

    /**
     * 环绕增强的方法
     */
    void aroundTargetMethod();

    /**
     *  执行会发生异常
     */
    void runException();
}

package com.bjpowernode.impl;

import com.bjpowernode.ITargetClass;

/**
 * @Description: 目标类
 * @author: Mr.T
 * @date 2020-09-27 15:38
 */
public class TargetClassImpl implements ITargetClass {
    @Override
    public void targetMethod() {
        System.out.println("待增强的目标方法");
    }

    @Override
    public String afterTargetMethod(String msg) {
        System.out.println("待增强的目标方法 --- afterTargetMethod");
        return "被增强的方法的返回值";
    }


    public void aroundTargetMethod() {
        System.out.println("待增强的目标方法 --- aroundTargetMethod");
    }

    /**
     * 发生异常
     */
    public void runException() {
        System.out.println("待增强的目标方法 --- runException");
        int m = 0;
        int n = 100/m;
    }


}

④  AOP配置

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

    <!-- 定义目标类对象 -->
    <bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" />
    <!-- 定义增强类对象 前置增强类 -->
    <bean id="myBeforeAdvice" class="com.bjpowernode.advice.MyBeforeAdvice" />
    <!-- 定义增强类对象  后置增强 -->
    <bean id="myAfterAdvice" class="com.bjpowernode.advice.MyAfterAdvice" />
    <!-- 定义增强类 环绕增强 -->
    <bean id="myAroundAdvice" class="com.bjpowernode.advice.MyAroundAdvice" />
    <!-- 定义增强类  异常增强类 -->
    <bean id="myExceptionAdvice" class="com.bjpowernode.advice.MyExceptionAdvice" />
    <!-- 进行织入 -->
    <aop:config>
        <!--
            id : 连接点的唯一标识
            expression : 连接点的表达式
                execution(* 包名.类名.方法名(..))
                * 指任意字符
                .. 表示参数可以是任意个
        -->
        <aop:pointcut id="beforePoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.targetMethod(..))"/>
        <!-- 后置增强的切点 -->
        <aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterTargetMethod(..))"/>
        <!-- 环绕增强的切点 -->
        <aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundTargetMethod(..))"/>
        <!-- 异常增强的切点 -->
        <aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runException(..))"/>
        <!--
            织入
            将增强和连接点 结合
        -->
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="beforePoint" />

        <!-- 织入
            后置增强的织入
         -->
        <aop:advisor advice-ref="myAfterAdvice" pointcut-ref="afterPoint" />

        <!--
            织入
            环绕增强的织入
        -->
        <aop:advisor advice-ref="myAroundAdvice"  pointcut-ref="aroundPoint" />

        <!--
          织入
          异常增强的织入
      -->
        <aop:advisor advice-ref="myExceptionAdvice"  pointcut-ref="exceptionPoint" />
    </aop:config>

</beans>

⑤测试类

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

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        ITargetClass targetClass = context.getBean(ITargetClass.class);
        targetClass.runException();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值