Spring

Posted xue_yun_xiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring相关的知识,希望对你有一定的参考价值。

一、什么是AOP?

Aop 就是面向切面编程
通俗点:就是用一个切面类去拦截 你想拦截的方法,用以一个切面 切向 拦截方法
切面:就是一个类,内部封装了抽取了公共方法(日志,鉴权登录,拦截,过滤)。

面向切面编程作用:
1.包程序中重复代码,冗余的代码,需要统一处理的代码放在切面公共方法
2.提高程序的可维护性,在不改变原有代码情况下,对目标方法进行增强

在这里插入图片描述
在这里插入图片描述

Proxy:代理,就是aop 生成的代理对象
Advice(通知/增强处理):通知就是切面中封装的方法
Target Object(目标对象):要拦截的对象,代理的对象
Aspect(切面):切面就是一个类,封装了公共的方法(通知)
Joinpoint(连接点):就是普通的方法
Pointcut(切入点):就是切面和拦截的方法交会的方法(连接点)
切入点就是连接点,连接点不一定时切入点。
Weaving(织入):将切面 切向切入点(拦截的方法)过程,生成代理对象就是织入

二、SpringAop 基于ProxyFactoryBean实现

ProxyFactoryBean:作用就是产生代理对象,实现了 FactoryBean接口
在这里插入图片描述

FactoryBean & BeanFacotry?
FactoryBean:生成对象
BeanFacotry:spring 核心容器,获取对象(getBean(""))

1、引入依赖


    <properties>
        <!--在当前pom 或者父类pom 中声明属性  -->
        <spirng.version>5.0.16.RELEASE</spirng.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spirng.version}</version>
        </dependency>

        <!-- 导入spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spirng.version}</version>
        </dependency>

    </dependencies>

2、创建切面,并加入到容器

/**
 * 创建一个切面
 *
 */
public class MyAspect implements MethodInterceptor {

    // 公共方法------>通知
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("即将执行方法:"+methodInvocation.getMethod().getName());
        // 调用目标方法执行 获取结果
         Object result =   methodInvocation.proceed();

        System.out.println("切面打印结果:"+result);

        return result;
    }
}

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 id="studentDao" class="com.qfedu.dao.impl.StudentDaoImpl"></bean>



        <!--
                将切面 实例 加入到容器中
        -->
        <bean id="myAspect" class="com.qfedu.aspect.spring.MyAspect"></bean>



        <!--

                将界面 切向 连接点  这个过程就是织入
                ProxyFactoryBean :就是创建代理对象  实现了 FactoryBean接口
        -->

        <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
                <!--
                target 指定代理对象  的目标对象
                -->
                <property name="target" ref="studentDao"></property>

                <!--
                proxyInterfaces  如果代理的目标对象有实现接口 写上,没有接口不写
                -->
                <property name="proxyInterfaces" value="com.qfedu.dao.IStudentDao"></property>

                <!--
                        interceptorNames 指定切面  切记:使用value 指定
                -->
                <property name="interceptorNames" value="myAspect"></property>

                <!--
                        proxyTargetClass 指定产生代理对象的方式
                                        true: 代表强制使用cglib
                                        false: 目标对象实现接口 使用jdk,没有实现接口使用cglib

                -->
                <property name="proxyTargetClass" value="true"></property>

        </bean>


</beans>

4、测试

 public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        // 目标对象
//       IStudentDao studentDao = (IStudentDao) applicationContext.getBean("studentDao");
//        System.out.println("student:"+studentDao.findStudentById(200));

        // 通过 代理对象 ProxyFactoryBean  获取代理对象
        IStudentDao studentDao = (IStudentDao) applicationContext.getBean("proxyFactoryBean");
        System.out.println("student:"+studentDao.findStudentById(200));
    }

三、SpringAop 基于Aspectj 实现

spring Aop 引入优秀的 AspectJ 框架,更加丰富了springaop 的功能

AspectJ 实现分两种:
-xml 实现
-注解实现

xml 实现

1、引入依赖

    <properties>
        <!--在当前pom 或者父类pom 中声明属性  -->
        <spirng.version>5.0.16.RELEASE</spirng.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spirng.version}</version>
        </dependency>

        <!-- 导入spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spirng.version}</version>
        </dependency>


        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>

    </dependencies>

2、创建切面

package com.qfedu.aspect.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 基于AspectJ 的切面
 *
 * 切面:有公共的方法组成,公共的方法就是通知
 * 通知分为:
 *    前置通知
 *    后置通知
 *    环绕通知
 *    异常通知
 *    最终通知
 *
 */
public class MyAspectXml {


    /**
     * 前置通知
     *  调用目标方法前调用
     * JoinPoint  连接点 中的切点
     * @param joinPoint
     */
    public  void myBefore(JoinPoint joinPoint){

        System.out.println("前置通知:"+joinPoint.getSignature().getName());
    }


    /**
     * 环绕通知 :决定目标对象是否执行  拦截发生在 环绕不调用joinPoint.proceed(); 就是拦截
     * @param joinPoint
     */
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知---前:"+joinPoint.getSignature().getName());


//        Object result = null;
        // 调用 目标对象执行相应的方法
        Object result =   joinPoint.proceed();

        System.out.println("环绕通知---result"+result);
        System.out.println("环绕通知---后"+joinPoint.getSignature().getName());

        return  result;
    }


    /**
     * 最终通知  无论方法 是否异常 都会通知
     * @param joinPoint
     */
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知:"+joinPoint.getSignature().getName());
    }

    /**
     * 后置通知 :  可以获取目标执行的结果  ,如果发生异常 不调用
     * @param joinPoint
     */
    public void  myAfterReturning(JoinPoint joinPoint,Object result){
        System.out.println("后置通知:"+joinPoint.getSignature().getName());

        System.out.println("后置通知--result:"+result);
    }


    /**
     * 异常通知 :不发生异常不调用
     *
     * Throwable e 接收异常信息
     * @param joinPoint
     */
    public void myThrowing(JoinPoint joinPoint,Throwable e){

        System.out.println("异常通知"+joinPoint.getSignature().getName());
        System.out.println("异常通知--异常信息:"+e.getMessage());
    }



}

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" 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="studentDao" class="com.qfedu.dao.impl.StudentDaoImpl"></bean>

    <!--
        声明 切面 加入容器中
    -->
    <bean id="myAspectXml" class="com.qfedu.aspect.aspectj.MyAspectXml"></bean>

    <!--
        织入
    -->
    <aop:config>

        <!-- 声明切面中的切点
             expression="execution()"  切点表达式  就是要拦截方法的集合

             声明在切面中 只能被当前切面使用,如果声明在   <aop:config> 可以被所有切面使用
         -->
        <aop:pointcut id="myPointcut" expression="execution(* com.qfedu.dao.*.*.*(..))"/>



        <!--  切面进行织入也就是将切面 和 切入点(拦截的方法发生关联)
              ref="myAspectXml"  引用切面
          -->
        <aop:aspect ref="myAspectXml">


            <!--
                将切面中的 前置通知 和  切点绑定
            -->
            <aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>


            <!--
                最终通知
            -->
            <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>

            <!--
                后置通知 :获取目标对象执行方法的结果  returning=""
            -->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="result"></aop:after-returning>

            <!--
                环绕通知: 决定是否调用目标对象
            -->
            <aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>

            <!--
                throwing="e" 将异常信息传递给 异常通知myThrowing
            -->
            <aop:after-throwing method="myThrowing" pointcut-ref="myPointcut" throwing="e"></aop:after-throwing>

        </aop:aspect>

    </aop:config>


</beans>

4、测试

public class MyAspectXmlTest {
    public static void main(String[] args) {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean_aspectj_xml.xml");

        // 获取目标对象
        IStudentDao studentDao = (IStudentDao) applicationContext.getBean("studentDao");

        System.out.println("studentDao:"+studentDao.findStudentById(300));
    }
}

注解实现

1、创建切面

/**
 * 使用注解完成  aspectj aop
 */
@Component // 将当前切面加入到容器中
@Aspect // 声明当前对象 是一个切面 相当于xml   <aop:aspect     @Aspect 也需要激活 <aop:aspectj-autoproxy>
public class MyAspectAnnotation {


//    @Pointcut("execution(* com.qfedu.dao.*.*.*(..))")// 声明一个切点  方法名就是切点名
    @Pointcut(value = "execution(* com.qfedu.dao.*.*.*(..))")
    public void myPoint(){

    }

    @Before(value = "myPoint()") // 1.声明当前方法 是前置通知  2.和切点绑定
    public void myBefore(JoinPoint joinPoint){

        // joinPoint.getSignature().getName() 获取方法名
        System.out.println("前置通知:"+joinPoint.getSignature().getName());

    }



    /**
     * 环绕通知 :决定目标对象是否执行  拦截发生在 环绕不调用joinPoint.proceed(); 就是拦截
     * @param joinPoint
     */
    @Around(value = "myPoint()")
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知---前:"+joinPoint.getSignature().getName());


//        Object result = null;
        // 调用 目标对象执行相应的方法
        Object result =   joinPoint.proceed();

        System.out.println("环绕通知---result"+result);
        System.out.println("环绕通知---后"+joinPoint.getSignature().getName());

        return  result;
    }



    /**
     * 后置通知 :  可以获取目标执行的结果  ,如果发生异常 不调用
     * @param joinPoint
     */
    @AfterReturning(pointcut = "myPoint()",returning = "result")
    public void  myAfterReturning(JoinPoint joinPoint,Object result){
        System.out.println("后置通知:"+joinPoint.getSignature().getName());

        System.out.println("后置通知--result:"+result);
    }



    /**
     * 最终通知  无论方法 是否异常 都会通知
     * @param joinPoint
     */
    @After("myPoint()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知:"+joinPoint.getSignature().getName());
    }




    /**
     * 异常通知 :不发生异常不调用
     *
     * Throwable e 接收异常信息
     * @param joinPoint
     */
    @AfterThrowing(pointcut = "myPoint()",throwing = "e")
    public void myThrowing(JoinPoint joinPoint,Throwable e){

        System.out.println("异常通知"+joinPoint.getSignature().getName());
        System.out.println("异常通知--异常信息:"+e.getMessage());
    }



}

2、在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:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">



    <!--
        使用包扫描器 让注解生效  @Repository  @Component @Service @Controller ....
    -->
    <context:component-scan base-package="com.qfedu"></context:component-scan>

    <!--
        激活 @Aspect
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

3、测试

public class AspectAnnotationTest {

    public static void main(String[] args) {

        ApplicationContext applicationContext =  new ClassPathXmlApplicationContext("bean_aspectj_annotation.xml");

        // 只能获取接口 因为从容器获取的代理对象 是接口 
        IStudentDao studentDao =  applicationContext.getBean(IStudentDao.class);


        System.out.println("student:"+studentDao.findStudentById(2000));
    }
}

以上是关于Spring的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot:thymeleaf 没有正确渲染片段

What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

Spring Rest 文档。片段生成时 UTF-8 中间字节无效 [重复]

解决spring-boot启动中碰到的问题:Cannot determine embedded database driver class for database type NONE(转)(代码片段

一张图帮你记忆,Spring Boot 应用在启动阶段执行代码的几种方式