spring笔记(自用)

1、spring核心技术

 spring出现在2002年左右,主要用于减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。spring的核心技术是iocaop。能实现模块之间,类之间的解耦合

IOC:
ioc(inversion of control 控制反转):把对象的创建、赋值、管理工作都交给代码之外的容器实现,也就是对象的创建都是有其他外部资源完成。
控制:创建对象,对象的属性赋值,对象之间的关系管理。
反转:把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现,由容器代替开发人员管理对象、创建对象、给对象赋值。
正转:由开发人员在代码中,使用new构造方法创建对象,开发人员主动管理对象。
容器:是一个服务器软件,一个框架(spring)。
ioc的体现:和servlet原理一样
 1:创建类继承HttpServlet
 2:在web.xml中注册servlet,使用

<servlet-name>myservlet</servlet-name>
<servlet-class>com.fd.controller.MyServlet</servlet-class>

 3:没有创建Servlet对象,没有使用MyServlet myservlet=new MyServlet();
 4:Servlet是Tomcat服务器,它能帮你创建对象。Tomcat也称为容器。Tomcat作为容器,里面存放的有Servlet对象,Filter、Listener对象。
ioc的技术实现:DI(Dependency injection)依赖注入是ioc的技术实现。使用者只需要在程序中提供要使用的对象名称就行,至于赋值、查找,都可以交由容器内部实现。
spring是使用的DI实现了ioc的功能,spring底层创建对象,使用的是反射机制。


**AOP:**

1.动态代理
 jdk动态代理:使用jdk中的Proxy,Method,InvocationHandler创建代理对象。jdk动态代理要求目标类必须实现接口。使用方式如3.5、动态代理
 cglib动态代理:第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的。

2.动态代理的作用:
 1)在目标类源代码不改变的情况下,增加功能
 2)减少代码的重复
 3)专注业务逻辑代码
 4)解耦合,让你的业务和日志,事务非业务功能分离

3.Aop(Aspect Orient Programming):
Aspect: 切面,给你的目标类增加功能就是切面。像日志、事务都是切面。
     切面的特点:一般都是非业务的方法,独立使用的
Orient: 面向
Programming: 编程
 Aop(Aspect Orient Programming)面向切面编程,基于动态代理的,可以使用jdk,cglib两种方式。
 Aop就是动态代理的规范化,把动态代理的实现步骤、方式都定义好了,让开发人员用一种统一的方式使用动态代理。

术语
 1)Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能。非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证。
 2)JoinPoint:连接点,连接业务方法和切面的位置。就是某个类中的业务方法。
 3)PointCut:切入点,指多个连接点方法的集合。多个方法。
 4)目标对象:给哪个类的方法增加功能,这个类就是目标对象。
 5)Advice:通知,通知表示切面功能执行的时间。

切面功能的三个关键要素
 1)切面的功能代码,切面是用来干什么的。
 2)切面的执行位置,使用PointCut表示切面执行的位置。
 3)切面的执行时间,使用Advice表示时间,在目标方法之前还是在目标方法之后。

2、spring配置文件相关标签及使用

在使用spring之前,需要先添加依赖

    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

2.1、beans根标签

<?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的配置文件
    1.beans:是根标签,spring把java对象称为bean
    2.spring-beans.xsd是约束文件,和mybatis中dtd是一样的
-->
</beans>

2.2、bean对象

	<!--
    声明bean,就是告诉spring要创建某个类的对象
    id:对象的自定义名称,唯一值,spring通过这个名称找到对象。
    class:类的全限定名称(不能是接口,因为spring使用反射机制创建对象,必须用类)
    autowire:自动装载,分别有byName和byType可选,用于引用属性的自动注入,详情看3.3.1

    spring框架有一个map用来存放对象,在bean配置好后
    使用spring创建对象相当于 springMap.put("someService",new SomeServiceImpl());
    -->
    <bean id="someService" class="com.wxk.service.SomeServiceImpl"/>
    <!--spring也能创建非自定义的类的对象-->
    <bean id="myDate" class="java.util.Date"/>

2.3、property属性(set注入)

 一个property只能给一个属性赋值,赋值仅仅只是调用的set方法,不管set方法中是否是赋值
如name=“age” 则实际是spring创建对象后调用了该类中的setAge方法

    <bean id="xxx" class="xxx">
        <property name="属性名字" value="该属性的值"/>
        <property ...../>
    </bean>

2.4、constructor-arg(构造注入)

	<!--
	构造注入:spring调用类的有参构造方法,在创建对象的同时,在构造方法中给属性赋值
      构造注入使用<constructor-arg>标签
      <constructor-arg>标签:一个<constructor-arg>标签只能表示构造方法的一个参数,因此该标签的数量应和想要赋值的属性数量相同
      <constructor-arg>标签属性:
        name:表示构造方法的形参名
        index:表示构造方法的参数的位置,参数从左到右位置是0,1,2……的顺序
        value:构造方法的形参是简单类型的,使用value
        ref:构造方法的形参是引用类型的,使用ref
	-->
	<bean id="xxx" class="xxx">
        <constructor-arg name="name" index="0" value="李四"/>
        <constructor-arg name="age" index="1" value="21"/>
        <constructor-arg name="school" index="2" ref="mySchool"/>
    </bean>

2.5、import导入

在这里插入图片描述

	<!--
    包含关系的配置文件:total.xml表示spring主配置文件,主配置文件包含其他配置文件,且主配置文件一般是不定义对象的。
    语法:
        <import resource="classpath:其他配置文件的路径"/>
    关键字"classpath":表示类路径(class文件所在的目录,即编译生成的target目录下的classes文件)
                     在spring配置文件中要指定其他文件的位置,需要用classpath,告诉spring到哪去加载读取。
    -->

    <!--加载的是文件列表-->
    <import resource="classpath:spring-school.xml"/>
    <import resource="classpath:spring-student.xml"/>

 除了输入完整的路径classpath同样可以用通配符表示

    <!--
        在包含关系的配置文件中,可以用通配符(*:表示任意字符)
        注意:主配置文件不能在通配符的范围之内,否则会形成死循环
    -->
    <import resource="classpath:spring-*.xml"/>

2.6、component-scan组件扫描器

    <!--
        声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你项目中的包名
        <component-scan>工作方式:spring会扫描遍历base-package指定的包,
            扫描该包中和其子包中所有的类,找到类中的注解,按照注解的功能创造对象,或给属性赋值
    -->

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

    <!--指定多个包的三种方式-->
    <!--第一种方式:使用多次组件扫描器,指定不同的包-->
    <context:component-scan base-package="com.wxk.vo"/>
    <context:component-scan base-package="com.wxk.service"/>

    <!--第二种方式:使用分隔符(,或者;)分隔多个包名-->
    <context:component-scan base-package="com.wxk.service;com.wxk.vo"/>

    <!--第三种方式:指定父包-->
    <context:component-scan base-package="com.wxk"/>

 如果component-scan无法被识别,可以手动引入约束xmlns:context="http://www.springframework.org/schema/context"同时加入http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
具体如下
在这里插入图片描述

2.7、property-placeholder加载资源文件

    <!--加载属性配置文件-->
    <context:property-placeholder location="classpath:test.properties"/>

 加载的文件可以用${}调用

2.8、aspectj-autoproxy自动代理生成器

相关信息点击跳转

3、spring框架的用法

 首先,要使用spring框架,需要先在maven中加入相关依赖,如下:

    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

3.1、创建spring容器、从容器中获得对象

		//使用spring容器创建对象
        //1.指定spring配置文件的名称
        String config="beans.xml";
        //2.创建表示spring容器的对象ApplicationContext
        //ApplicationContext表示spring容器,通过容器就能获取对象了
        //ClassPathXmlApplicationContext表示从编译后target目录下的类路径(classes)中加载spring的配置文件
        //这里有一点要注意的是,spring默认创建对象的时间是当spring容器被创建的时候,这时spring会创建配置文件中所有的对象。创建对象时,spring默认调用的是无参构造方法。
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);

在这里插入图片描述
 在编译后资源文件中的spring配置文件会在类路径下生成

        //3.从容器中获取某个对象
        //getBean("配置文件中bean的id值")
        SomeService someService= (SomeService) ac.getBean("someService");
        //4.使用spring创建好的方法
        someService.dosome();

3.2、获取容器中对象的数目和名称

		String config="beans.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        //使用spring提供的方法获取容器中对象的数量
        int num=ac.getBeanDefinitionCount();
        System.out.println("容器中对象数目:"+num);
        //获取容器中对象的名称
        String names[] =ac.getBeanDefinitionNames();
        for (String name:names){
            System.out.println("对象名称"+name);
        }

3.3、DI(配置文件实现)

    <!--
    声明Student对象
    注入:就是赋值的意思
    简单类型:spring中规定java的基本数据类型和String都是简单类型
    DI(配置文件实现):给属性赋值
    1.set注入(设值注入):spring调用类的set方法完成属性赋值,如果类没有相应的set方法,spring会报错
        1)简单的set注入语法规则
        <bean id="xxx" class="xxx">
            <property name="属性名字" value="该属性的值"/>//一个property只能给一个属性赋值,赋值仅仅只是调用的set方法,不管set方法中是否是赋值
            <property ...../>
        </bean>
        2)引用类型的set注入:spring调用类的set方法
        <bean id="xxx" class="xxx">
            <property name="属性名称" ref="bean的id"/>
        </bean>

    2.构造注入:spring调用类的有参构造方法,在创建对象的同时,在构造方法中给属性赋值
      构造注入使用<constructor-arg>标签
      <constructor-arg>标签:一个<constructor-arg>标签只能表示构造方法的一个参数,因此该标签的数量应和想要赋值的属性数量相同
      <constructor-arg>标签属性:
        name:表示构造方法的形参名
        index:表示构造方法的参数的位置,参数从左到右位置是0,1,2……的顺序
        value:构造方法的形参是简单类型的,使用value
        ref:构造方法的形参是引用类型的,使用ref
    -->

    <!--set注入-->
    <bean id="mySchool" class="com.wxk.vo.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
    <bean id="myStudent" class="com.wxk.vo.Student">
        <property name="name" value="张三"/><!--setName-->
        <property name="age" value="20"/>
        <!--引用类型-->
        <property name="school" ref="mySchool"/>
    </bean>

    <!--
    构造注入
    在构造注入中,<constructor-arg>标签需要name或者index二者选一个作为唯一标识符,如果都没有也行,都没有仅有value或ref属性则需要<constructor-arg>
    标签中value(或ref)的值按构造函数中形参相对应的顺序从上往下排列
    -->
    <bean id="myStudent2" class="com.wxk.vo.Student">
        <constructor-arg name="name" index="0" value="李四"/>
        <constructor-arg name="age" index="1" value="21"/>
        <constructor-arg name="school" index="2" ref="mySchool"/>
    </bean>
3.3.1、引用类型的自动注入

 如果一个类中的引用属性很多,那么就会重复很多次<property name="xxx" ref="xxx"/>,这就会使得代码存在大量的冗余,为了减轻这种冗余,spring提供的方法中有两种常用的自动注入引用属性的方法byNamebyType

    <!--
    引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值,使用的规则通常是byName,byType
    1.byName(按名称注入):java类型中引用类型的属性名和spring(容器)配置文件中<bean>的id名称一样,且数据类型一致,这样spring能
                         直接将<bean>赋值给引用属性
    语法:
        <bean id="xxx" class="xxx" autowire="byName">
            任意
        </bean>

    2.byType(按类型注入):java类中引用类型的数据类型和spring(容器)配置文件中<bean>的class是“同源关系”,这样<bean>能直接赋值
                         给引用类型
        同源关系:1)java类中引用类型的数据类型和bean中class的值是一样的
                2)java类中引用类型的数据类型(父)和bean(子)的class是父子关系
                3)java类中引用类型的数据类型(接口)和bean(实现类)的class是接口与实现类关系
    语法:
        <bean id="xxx" class="xxx" autowire="byType">
            任意
        </bean>

    -->

    <!--byName-->
    <bean id="school" class="com.wxk.vo.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀区"/>
    </bean>
    <bean id="myStudent" class="com.wxk.vo.Student" autowire="byName">
        <property name="name" value="张三"/><!--setName-->
        <property name="age" value="20"/>
        <!--引用类型-->
        <!--<property name="school" ref="mySchool"/>-->
    </bean>

    <!--byType-->
    <bean id="myStudent2" class="com.wxk.vo.Student" autowire="byType">
        <property name="name" value="李四"/><!--setName-->
        <property name="age" value="20"/>
        <!--引用类型-->
        <!--<property name="school" ref="mySchool"/>-->
    </bean>

3.4、DI(注解实现)

  关于DI的使用,建议是需要经常更改的对象,使用配置文件来实现,反之,不是需要经常更改的用注解实现

使用注解的步骤:
 1.加入maven依赖spring-context,在你加入spring-context的同时,spring也会间接加入spring-aop的依赖(打开包的扩展就可以看见),而要使用注解就必须使用spring-aop的依赖。
 2.在类中加入spring 的注解(多个不同功能的注解)
 3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你项目中的位置
主要学习的注解有@Component,@Repository@Service@Controller,@Value,@Autowired,@Resource

3.4.1、@Component
/*
* @Component:创建对象的,等同于<bean>的作用
* 属性:value:对象的名称,也就是bean的id,value值是唯一的,创建的对象在整个spring中就一个
*            位置:在需要创建对象的类的上方
* 
* @Component(value = "student1")等同于
* <bean id="student1" class="com.wxk.vo.Student">
* */
@Component(value = "student1")
public class Student {
    ……
}

 注解中的value也能够省略(一般最常用的注解方式),如

@Component("student1")
public class Student {
    ……
}

 还能够不指定对象名称,由spring来提供默认名称:类名的首字母小写,如

@Component
public class Student {
    ……
}

//在获取该对象时使用ac.getbean("student"),其中student为类名的首字母小写
3.4.2、@Repository@Service@Controller

spring中和@Component 功能一样,创建对象的注解还有:
 1.@Repository(持久层):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的
 2.@Service(业务层):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有业务功能的
 3.@Controller(控制器):放在控制器(处理器)类的上面,创建controller对象,controller能够接受用户提交的参数,显示请求的处理结果
 以上三个注解的使用语法和@Component一致,都能创建对象,但是这三个注解还有额外的功能,是用来给项目分层的。

3.4.3、@Value
    /*
    * @Value:简单类型的属性赋值
    * 属性:value是String类型的,表示简单类型的属性值
    * 位置:1.在属性定义的上面,无需set方法,直接通过反射给属性赋值,推荐使用。
    *     2.在set方法的上面。
    * */
    @Value("张三")
    private String name;
    @Value("20")
    private int age;
3.4.4、@Autowired
    /*
    * 引用类型的属性赋值
    * @Autowired:spring框架提供的注解,实现引用类型的赋值
    * spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName、byType(默认)
    * 属性:required,是一个boolean类型的,默认是true
    *      required=true:表示应用类型赋值失败,程序报错,并终止执行。推荐使用
    *      required=false:表示应用类型赋值失败,程序正常执行,应用类型为null
    * 位置:1.在属性定义的上面,无需set方法,推荐使用
    *      2.在set方法的上面
    * */
    @Autowired
    private School school;

    /*
    * 如果要用byName方式,需要做的是:
    * 1.在属性上面加@Autowired
    * 2.在属性上面加@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
    * 两者没有先后顺序
    * */
    @Autowired
    @Qualifier("mySchool")
    private School school;
3.4.5、@Resource
    /*
    * 引用类型
    * @Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值
    *            使用的也是自动注入的原理,支持byName,byType,默认是byName
    * 位置:1.在属性定义的上面,无需set方法,推荐使用
    *      2.在set方法的上面
    *
    * 若想@Resource只使用byName方式,需要增加一个属性name
    * name的值是bean的id
    * */
    @Resource(name = "mySchool")
    private School school;

3.5、动态代理

 动态代理:可以在程序执行过程中,创建代理对象。 通过代理对象执行方法,给目标类的方法增加额外的功能(功能增强)。

jdk动态代理实现步骤:
 1.创建目标类,someServiceImpl目标类,给它的doSome,doOther 增加事务

public class SomeServiceImpl implements SomeService {

    @Override
    public void doSome() {
        System.out.println("执行了someServiceImpl的doSome()方法");
    }

    @Override
    public void doOther() {
        System.out.println("执行了someServiceImpl的doOther()方法");
    }

}

 2.创建InvocationHandler接口的实现类,在这个类实现给目标代码增加功能

public class MyInvocationHandler implements InvocationHandler {
    //目标对象
    private Object target;
    
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过代理对象执行方法时,会调用这个invoke

        //执行结果
        Object res=null;

        System.out.println("这在加入在目标方法执行前需要执行的方法");

        //执行目标类的方法,通过Method类实现
        res=method.invoke(target,args);

        System.out.println("在这加入在目标方法执行后需要执行的方法");

        //目标方法的执行结果
        return res;
    }
}

 3.使用jdk中类proxy,创建代理对象。实现创建对象的能力。

        //使用jdk的Proxy创建代理对象
        //创建目标对象
        SomeService target=new SomeServiceImpl();

        //创建InvocationHandler对象
        InvocationHandler handler =new MyInvocationHandler(target);

        //使用Proxy创建代理
        SomeService proxy= (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                                target.getClass().getInterfaces(),
                                                handler);

        //通过代理执行方法,会调用handler中的invoke()
        proxy.doSome();

3.6、Aop实现

 Aop是一个规范,是动态代理的一个规范化,一个标准
 Aop的技术实现框架:
 1.spring:spring在内部实现了aop规范,能做aop的工作。
      spring主要在事务处理时使用aop。
一般项目开发很少使用spring的aop实现,因为spring的aop比较笨重。
 2.apectj:一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
 aspectj框架实现aop有两种方式:
 1)使用xml配置文件:配置全局事务。
 2)使用注解:aspectj有5个注解@Before@AfterReturning@Around@AfterThrowing@After,在项目中要做aop功能,一般都用注解

3.6.1、aspectj框架的使用

 在使用aspectj框架之前,需要先加上依赖

    <!--aspectj依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
1.aspectj框架的使用流程

1.创建切面类:普通类
 1)在类的上面加入@Aspect
 2)在类中定义方法,方法就是切面要执行的功能代码
 在方法的上面加入aspectj中的通知注解,例如@Before
 还需要指定切入点表达式execution()

/*
* @Aspect:是aspectj框架中的注解
*   作用:表示当前类是切面类
*   切面类:是用来给业务方法增加功能的类,在这个类中有切面类的功能代码
*   位置:在类定义的上面
* */
@Aspect
public class MyAspect {
    /*
    * 定义方法,方法是实现切面类功能的
    * 方法定义的要求:
    *   1.必须是公共方法public
    *   2.方法没有返回值
    *   3.方法名称可以自定义
    *   4.方法可以有参数,也可以没有参数
    *     如果方法有参数,参数不能自定义,有几个参数类型可以使用
    * */

    @Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
    public void myBefore(){
        //切面类执行的功能代码
        System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
    }
}

2.创建spring的配置文件:声明对象,把对象交给容器统一管理
 声明对象你可以使用注解或者xml配置文件
 1)声明目标对象
 2)声明切面类对象
 3)声明aspectj框架中的自动代理生成器标签。
  自动代理生成器:用来完成代理对象的自动创建功能的。

    <!--声明目标对象-->
    <bean id="someService" class="com.wxk.service.SomeServiceImpl"/>

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.wxk.aspect.MyAspect"/>

    <!--
        声明自动代理生成器:使用aspectj框架内部的功能,创建目标代理的对象。
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象。
        所以目标对象就是被修改后的代理对象

        aspectj-autoproxy:会扫描spring容器中的所有目标对象,一次性都生成代理对象。
    -->
    <aop:aspectj-autoproxy/>

<aop:aspectj-autoproxy/>的约束如下

xmlns:aop="http://www.springframework.org/schema/aop"
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd

在这里插入图片描述

2.切面的执行时间(Advice)

在aspectj框架中使用注解表示,也可以使用xml配置文件中的标签

1)@Before
    /*
    *@Before:前置通知注解
    *   属性: value:是切入点表达式,表示切面的功能执行的位置
    *   位置:方法的上面
    *   特点:1.在目标方法之前先执行的
    *       2.不会改变目标方法的执行结果
    *       3.不会影响目标方法的执行
    * */

    @Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
    public void myBefore(){
        //切面类执行的功能代码
        System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
    }
2)@AfterReturning
    /*
    * 后置通知定义方法,方法是实现切面功能的
    * 方法的定义要求:
    *   1.公共方法public
    *   2.方法没有返回值
    *   3.方法名称自定义
    *   4.方法有参数,推荐是Object,参数自定义名称
    * */

    /*
    * @AfterReturning:后置通知
    *   属性:1.value 切入点表达式
    *       2.returning 自定义的变量,表示目标方法的返回值的。
    *         自定义变量名必须和通知方法的形参名一样。
    *   位置:在方法定义的上面
    *
    *   特点:1.在目标方法之后执行
    *       2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
    *       3.可以修改这个返回值
    * */

    @AfterReturning(value = "execution(* *..service.*.*(..))",returning = "obj")
    public void myAfterReturning(Object obj){
        //Object obj:是目标方法执行之后的返回值,可以根据返回值做切面功能处理
        System.out.println("后置通知接受方法返回值:"+obj);
        School school= (School) obj;
        school.setName("北京大学");
        System.out.println("==========方法执行结束=============");
    }

值得注意的是,后置通知所接收的参数如果是引用类型的参数,将其修改后,如上文school.setName,也会影响到接受此返回值的方法。
在这里插入图片描述

3)@Around
    /*
    * 环绕通知方法的定义格式
    * 1.公共方法public
    * 2.必须有一个返回值,推荐使用Object
    * 3.方法名称自定义
    * 4.方法有参数,固定的参数ProceedingJoinPoint
    * */

    /*
    * @Around:环绕通知
    *   属性:value 切入点表达式
    *   位置:在方法的定义上面
    *   特点:1.它是功能最强的通知
    *       2.在目标方法的前后都能增强功能
    *       3.控制目标方法是否被调用执行
    *       4.修改原来的目标方法的执行结果。影响最后的调用结果
    * 环绕通知等同于jdk的动态代理,InvocationHandler接口
    *   参数:ProceedingJoinPoint 等同于 jdk动态代理中的Method
    *       作用:执行目标方法
    *   返回值:就是目标方法的执行结果,可以被修改。
    * */

    @Around(value = "execution(* *..service.*.*(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        //实现环绕通知
        Object result=null;
        
        System.out.println("========="+pjp.getSignature().getName()+"开始执行============");
        //1.目标方法调用
        result=pjp.proceed();//相当于method.invoke()

        //2.在目标方法的前或者后添加功能或者事务。
        System.out.println("========="+pjp.getSignature().getName()+"执行结束============");
        return result;
    }
4)@AfterThrowing
    /*
    * 异常通知方法的定义格式
    * 1.公共方法public
    * 2.没有返回值
    * 3.方法名称自定义
    * 4.方法的参数有一个Exception,如果还有一个参数,那就是JoinPoint
    * */

    /*
    * @AfterThrowing:异常通知
    *   属性:1.value 切入点表达式
    *       2.throwing 自定义的变量,表示目标方法抛出的异常对象
    *         变量名必须和方法的参数一样
    *   特点:1.在目标方法抛出时执行
    *       2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
    *         如果有异常,可以发送邮件,短信进行通知
    * */

    @AfterThrowing(value = "execution(* *..service.*.*(..))",throwing = "ex")
    public void myAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println(jp.getSignature()+"方法出现异常");
    }
5)@After
    /*
     * 最终通知方法的定义格式
     * 1.公共方法public
     * 2.没有返回值
     * 3.方法名称自定义
     * 4.方法没有参数,如果有一个参数,那就是JoinPoint
     * */

    /*
    * @After:最终通知
    *   属性:value 切入点表达式
    *   位置:方法的上面
    *   特点:1.总是会执行
    *       2.在目标方法之后执行的
    * 一般是用来做资源清除工作的。
    * 执行顺序是环绕通知--》最终通知--》后置通知
    * */
    @After(value = "execution(* *..service.*.*(..))")
    public void myAfter(){
        System.out.println("执行最终通知");
    }
6) 通知方法中的参数JoinPoint
    /*
    * 指定通知方法中的参数:JoinPoint
    * JoinPoint:业务方法,要加入切面功能的业务方法
    *   作用:可以在通知方法中获取目标方法执行的信息,例如方法名称,方法的实参等
    *   如果你的切面功能中需要用到方法的信息,就加入JoinPoint
    *   这个JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
    * */

    @Before("execution(public void com.wxk.service.SomeServiceImpl.do*(..))")
    public void myBefore(JoinPoint jp){
        //获取方法的完整定义
        System.out.println("方法的定义:"+jp.getSignature());
        System.out.println("方法的名称:"+jp.getSignature().getName());
        //获取方法的实参
        Object args[]=jp.getArgs();
        for (Object arg:args){
            System.out.println("参数:"+arg);
        }

        //切面类执行的功能代码
        System.out.println("前置通知,切面功能:在目标方法之前输出执行时间:"+new Date());
    }
7) Pointcut注解,切入点表达式的复用
    /*
    * @Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的。
    *           可以使用@Pointcut
    *   属性:value 切入点表达式
    *   位置:在自定义的方法上面
    *   特点:当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名。
    *   其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式了。
    * */

    @Pointcut("execution(* *..service.*.*(..))")
    public void mypt(){
        //不需要代码
    }

    @After("mypt()")
    public void myAfter(){
        System.out.println("执行最终通知");
    }
3.aspectj的切入点表达式

表达式:
 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
解释:
 modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
 declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
 throws-pattern 抛出异常类型
 ?表示可选部分(是否使用)
加粗部分为必须使用的,实际项目开发中往往也只用这两个
以上表达式共4个部分
 execution(访问权限 方法返回值 方法声明(参数) 异常类型)

表达式中可使用以下符号

符号 意义
* 0至多个任意字符
.. 用在方法参数中,表示任意多个参数
用在包名后,表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类
用在接口后,表示当前接口及其实现接口
举例:

execution(public * *(…))
指定切入点为:任意公共方法

execution(* set*(…))
指定切入点为:任意返回值,任意一个以“set”开始的方法

execution(* com.wxk.service..(…))
指定切入点为:任意返回值的,定义在com.wxk.service包下的所有类的所有方法(不包括service的子包)

execution(* com.wxk.service….(…))
指定切入点为:任意返回值的,定义在com.wxk.service包和其子包下的所有类的所有方法

execution(* …service..*(…))
指定切入点为:任意返回值的,所有包下的service子包的所有类下的所有方法

execution(* .service..*(…))
指定切入点为:任意返回值的,只有一级包下的service子包的所有类下的所有方法

4.jdk动态代理和cglib动态代理

 目标类没有接口,那么spring框架会自动调用cglib动态代理,反之,目标类有接口,那么spring框架则会调用jdk动态代理。
 如果你期望目标类有接口也使用cglib动态代理,那么可以进行如下设置

    <!--
        proxy-target-class="true":告诉框架,要使用cglib动态代理
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值