Spring入门详细教程三

前言

本篇紧接着spring入门详细教程(二),建议阅读本篇前,先阅读第一篇和第二篇。链接如下:

Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/10165538.html

Spring入门详细教程(二) https://www.cnblogs.com/jichi/p/10176601.html

本篇主要讲解spring的aop相关。

一、aop的概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

AOP主要实现功能日志记录,性能统计,安全控制,事务处理,异常处理等等。将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

AOP主要思想总结为横向重复,纵向抽取。

二、spring实现aop的原理及底层实现

spring实现aop的底层使用了两种代理机制。一种是jdk的动态代理,一种是cglib的动态代理。下面来分析一下两种代理模式。

1、jdk的动态代理

被代理对象必须要实现接口才能产生代理对象,如果被代理对象不能实现接口,则这种方式的动态代理技术无效。

接下来做一个底层代码的编写来进行理解。

(1)首先jdk的动态代理要求被代理对象必须实现接口。我们准备一个接口以及一个接口的实现类。

public interface UserDao {
    void saveUser();
}
class UserDaoImpl implements saveUser(){
        System.out.println("保存用户");
    }
}

(2)建立一个UserDao的动态代理类,实现接口InvocationHandler。

class UserProxy  InvocationHandler{
    private UserDao userDao ;
    
    public UserProxy(UserDao userDao) {
        this.userDao = userDao;
    }

     UserDao createProxy(){
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
        return userDaoProxy;
    }
    
    @Override
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
        System.out.println("动态代理" method.invoke(userDao,args);
    }

}

(3)进行单元测试,发现第一个方法执行的时候没有被动态代理,第二个执行的时候进行了动态代理。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
 TestJunit {
    @Test
     test3(){
        UserDaoImpl userDaoImpl = new UserDaoImpl();
        userDaoImpl.saveUser();
        UserProxy userProxy =  UserProxy(userDaoImpl);
        UserDao createProxy = userProxy.createProxy();
        createProxy.saveUser();
    }
}

2、cglib动态代理

针对一些不能实现接口的代理对象产生代理,可以对没有被final修饰的任何对象进行继承代理,其底层应用的是字节码增强的技术,生成代理对象的子类对象。如果被final修饰,类不可继承,便不可使用cglib动态代理。

(1)创建一个cglib动态代理对象实现接口。

class CglibProxy  MethodInterceptor{

     UserDaoImpl userDaoImpl;
    
     CglibProxy(UserDaoImpl userDaoImpl) {
        this.userDaoImpl = userDaoImpl;
    }
    
     UserDaoImpl createProxy(){
        Enhancer enhancer =  Enhancer();
        enhancer.setSuperclass(UserDaoImpl.);
        enhancer.setCallback();
        UserDaoImpl udi = (UserDaoImpl) enhancer.create();
         udi;
    }
    
    @Override
    public Object intercept(Object proxy,Object[] args,MethodProxy methodProxy)  Throwable {
        Object obj = methodProxy.invokeSuper(proxy,args);
        System.out.println("动态代理" obj;
    }

}

(2)进行单元测试。

     test4(){
        UserDaoImpl userDaoImpl =  UserDaoImpl();
        userDaoImpl.saveUser();
        CglibProxy cglib =  CglibProxy(userDaoImpl);
        UserDaoImpl userDaoImpl2 = cglib.createProxy();
        userDaoImpl2.saveUser();
    }

可以发现第一个saveUser没有执行动态代理,第二个执行了动态代理。

结论:两种代理技术针对不同情况,互相弥补,从而使任何对象都可以实现动态代理。spring在进行aop的时候,默认使用jdk的动态代理技术,当发现jdk的动态代理技术不好使的情况下,使用cglib动态代理技术,保证被代理对象能够被正常代理。如需使用cglib动态代理可以再spring的配置文件中进行配置。

<aop:config proxy-target-class="true">

三、aop开发中的相关概念

1、Joinpoint(连接点):目标对象中,所有可以增强的方法。

2、Pointcut(切入点):目标对象中,已经增强的方法。

3、Advice(通知):对于目标对象来说,需要给目标对象增强的方法。

4、Target(目标对象):被代理对象。

5、Weaving(织入):将通知应用到切入点的过程。

6、Proxy(代理):将通知织入到目标对象后,形成的增强后的对象。

7、Aspect(切面):切入点和通知的结合。

四、spring中aop的实现方式

分两种方式介绍,一种是xml配置方式,一种是注解方式。

1、xml配置方式

(1)实现spring的aop需要导入aop包,aspect包,aopalliance包,weaver包。在spring教程一中可以找到获取这些包的方法。

(2)编写需要增加的方法类。

 UserDaoImpl{
    
    );
    }
     deleteUser(){
        System.out.println("删除用户");
    }
}

(3)编写通知,也就是说想要增加的代码方法。

 UserAdvice{
    
     before(){
        System.out.println("前置通知");
    }
    
     afterReturning(){
        System.out.println("后置通知(不发生异常的情况下调用)");
    }

    public Object around(ProceedingJoinPoint pjp)  Throwable{
        System.out.println("执行前");
        Object proceed = pjp.proceed();
        System.out.println("执行后" proceed;
    }
    
     afterThrowException(){
        System.out.println("发生异常调用" after(){
        System.out.println("后置通知,发生异常也会调用");
    }
}

(4)在spring的配置文件中进行配置

    <bean name = "userDaoImpl" class="com.jichi.aop.UserDaoImpl"></bean>
    <bean name="userAdvice" class="com.jichi.aop.UserAdvice"></bean>
    <aop:config>
        <aop:pointcut expression="execution(* com.jichi.aop..UserDaoImpl.*(..))" id="pc"/>
        <aop:aspect ref="userAdvice">
            <aop:before method="before" pointcut-ref="pc"/>
            <aop:after-returning method="afterReturning" pointcut-ref="pc"/>
            <aop:around method="around" pointcut-ref="pc"/>
            <aop:after-throwing method="afterThrowException" pointcut-ref="pc"/>
            <aop:after method="after" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

(5)进行单元测试

@RunWith(SpringJUnit4ClassRunner. TestAop {

    @Resource
     UserDaoImpl userDaoImpl;
    
    @Test
     test1(){
        userDaoImpl.saveUser();
    }
}

结果如下:织入成功。

2、注解配置方式

(1)同第一种方式需要导入包

(2)编写需要增加的方法类。

public class UserDaoImpl{
    
    public void saveUser(){
        System.out.println("保存用户");
    }
    public void deleteUser(){
        System.out.println("删除用户");
    }
}

(3)编写通知,也就是说想要增加的代码方法。

public class UserAdvice{
    
    public void before(){
        System.out.println("前置通知");
    }
    
    public void afterReturning(){
        System.out.println("后置通知(不发生异常的情况下调用)");
    }

    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("执行前");
        Object proceed = pjp.proceed();
        System.out.println("执行后");
        return proceed;
    }
    
    public void afterThrowException(){
        System.out.println("发生异常调用");
    }
    
    public void after(){
        System.out.println("后置通知,发生异常也会调用");
    }
}

(4)在spring配置文件中进行配置,并开启注解aop

    <bean name = "userDaoImpl" class="com.jichi.aop.UserAdvice"></bean>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

(5)在通知类上打上aspect的注解。在方法上打上相应注解

@Aspect
 UserAdvice{
    
    @Before("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    );
    }
    
    @AfterReturning("execution(* com.jichi.aop..UserDaoImpl.*(..))");
    }

    @Around("execution(* com.jichi.aop..UserDaoImpl.*(..))" proceed;
    }
    
    @AfterThrowing("execution(* com.jichi.aop..UserDaoImpl.*(..))");
    }
    
    @After("execution(* com.jichi.aop..UserDaoImpl.*(..))");
    }
}

优化方式:每个方法都配置方法抽取,显得比较臃肿,可以进行提取,方法如下

 UserAdvice{
    
    @Pointcut("execution(* com.jichi.aop..UserDaoImpl.*(..))" adc(){}
    
    @Before("UserAdvice.adc()");
    }
    
    @AfterReturning("UserAdvice.adc()");
    }

    @Around("UserAdvice.adc()" proceed;
    }
    
    @AfterThrowing("UserAdvice.adc()");
    }
    
    @After("UserAdvice.adc()");
    }
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍了spring的事务传播属性REQUIRED_NESTED的原理介绍,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。传统事务中回滚点的使...
今天小编给大家分享的是一文解析spring中事务的传播机制,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
这篇文章主要介绍了SpringCloudAlibaba和SpringCloud有什么区别,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Cloud Netfli...
本篇文章和大家了解一下SpringCloud整合XXL-Job的几个步骤。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。第一步:整合pom文件,在S...
本篇文章和大家了解一下Spring延迟初始化会遇到什么问题。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。List 坑列表 = new ArrayList(2);...
这篇文章主要介绍了怎么使用Spring提供的不同缓存注解实现缓存的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇...
本篇内容主要讲解“Spring中的@Autowired和@Resource注解怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学...
今天小编给大家分享一下SpringSecurity怎么定义多个过滤器链的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家
这篇文章主要介绍“Spring的@Conditional注解怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring的@Con...
这篇文章主要介绍了SpringCloudGateway的熔断限流怎么配置的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SpringCloud&nb...
今天小编给大家分享一下怎么使用Spring解决循环依赖问题的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考
这篇文章主要介绍“Spring事务及传播机制的原理及应用方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Sp...
这篇“SpringCloudAlibaba框架实例应用分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价
本篇内容主要讲解“SpringBoot中怎么使用SpringMVC”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习...
这篇文章主要介绍“SpringMVC适配器模式作用范围是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringMVC
这篇“导入SpringCloud依赖失败如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家...
这篇文章主要讲解了“SpringMVC核心DispatcherServlet处理流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来
今天小编给大家分享一下SpringMVCHttpMessageConverter消息转换器怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以...
这篇文章主要介绍“Spring框架实现依赖注入的原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring框架...
本篇内容介绍了“Spring单元测试控制Bean注入的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下