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 {
    public void saveUser();
}
public class UserDaoImpl implements UserDao {
    public void saveUser(){
        System.out.println("保存用户");
    }
}

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

public class UserProxy implements InvocationHandler{
    private UserDao userDao ;

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

    public 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("动态代理");
        return method.invoke(userDao, args);
    }

}

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

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

2、cglib动态代理

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

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

public class CglibProxy implements MethodInterceptor{

    private UserDaoImpl userDaoImpl;

    public CglibProxy(UserDaoImpl userDaoImpl) {
        this.userDaoImpl = userDaoImpl;
    }

    public UserDaoImpl createProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserDaoImpl.class);
        enhancer.setCallback(this);
        UserDaoImpl udi = (UserDaoImpl) enhancer.create();
        return udi;
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object obj = methodProxy.invokeSuper(proxy, args);
        System.out.println("动态代理");
        return obj;
    }

}

(2)进行单元测试。

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

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

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

<aop:con?g 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)编写需要增加的方法类。

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的配置文件中进行配置

    <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.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAop {

    @Resource
    private UserDaoImpl userDaoImpl;

    @Test
    public void 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.UserDaoImpl"></bean>
    <bean name="userAdvice" class="com.jichi.aop.UserAdvice"></bean>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

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

@Aspect
public class UserAdvice{

    @Before("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public void before(){
        System.out.println("前置通知");
    }

    @AfterReturning("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public void afterReturning(){
        System.out.println("后置通知(不发生异常的情况下调用)");
    }

    @Around("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("执行前");
        Object proceed = pjp.proceed();
        System.out.println("执行后");
        return proceed;
    }

    @AfterThrowing("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public void afterThrowException(){
        System.out.println("发生异常调用");
    }

    @After("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public void after(){
        System.out.println("后置通知,发生异常也会调用");
    }
}

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

@Aspect
public class UserAdvice{

    @Pointcut("execution(* com.jichi.aop..UserDaoImpl.*(..))")
    public void adc(){}

    @Before("UserAdvice.adc()")
    public void before(){
        System.out.println("前置通知");
    }

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

    @Around("UserAdvice.adc()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("执行前");
        Object proceed = pjp.proceed();
        System.out.println("执行后");
        return proceed;
    }

    @AfterThrowing("UserAdvice.adc()")
    public void afterThrowException(){
        System.out.println("发生异常调用");
    }

    @After("UserAdvice.adc()")
    public void after(){
        System.out.println("后置通知,发生异常也会调用");
    }
}

原文地址:https://www.cnblogs.com/jichi/p/10177004.html

时间: 2024-10-14 08:50:23

Spring入门详细教程(三)的相关文章

Spring入门详细教程(二)

前言 本篇紧接着spring入门详细教程(一),建议阅读本篇前,先阅读第一篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/10165538.html 一.spring注入方式 1.set方法注入 <bean name="user" class="com.jichi.entity.User" > <property name="name" value="小明

Spring入门详细教程(一)

一.spring概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建.简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架. 二.Spring特点 1.方便解耦,简化开发. 2.AOP编程的支持. 3.声明式事务的支持. 4.方便程序的测试 5.方便集成各种优秀框架 三.

经典Spring入门基础教程详解

经典Spring入门基础教程详解 https://pan.baidu.com/s/1c016cI#list/path=%2Fsharelink2319398594-201713320584085%2F%E7%BB%8F%E5%85%B8Spring%E5%85%A5%E9%97%A8%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%E8%AF%A6%E8%A7%A3&parentPath=%2Fsharelink2319398594-201713320584085 博达远

Android视频录制从不入门到入门系列教程(三)————视频方向

运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子的: 就是说,即使你是竖着拿手机的,Camera提供给你的视频图像的方向还是上图那样横着的图片. 我们可以通过下述方向改变Camera提供的视频图像的方法: camera.setDisplayOrientation(90); 让图像顺时针旋转90度,视频图像的方向就正常的. 本篇文章DEMO下载.

Xcode和github入门详细教程

Xcode和github详细教程! 主要是参考了现在网上的一些资料给没整过的人一个详细的指南. (1)先在github上注册账号,自行解决! (2)在导航栏右上角new一个repository(仓库). (3)填写仓库的名称.描述等信息.第二部是设置公开或者私人项目,隐私项目适合于公司的代码托管但是是收费的. (4)下面就不用管了,在MAC电脑上生成你的ssh秘钥,此处转载http://www.cnblogs.com/sorex/archive/2012/05/25/2517763.html.

Xcode和github入门详细教程!

Xcode和github详细教程! 主要是参考了现在网上的一些资料给没整过的人一个详细的指南. (1)先在github上注册账号,自行解决! (2)在导航栏右上角new一个repository(仓库). (3)填写仓库的名称.描述等信息.第二部是设置公开或者私人项目,隐私项目适合于公司的代码托管但是是收费的. (4)下面就不用管了,在MAC电脑上生成你的ssh秘钥,此处转载http://www.cnblogs.com/sorex/archive/2012/05/25/2517763.html.

android开发入门详细教程

随着移动端开发的火热,越来越多的IT程序员转做移动开发,做J2EE的转做Android开发,现在让麦子学院android开发老师给大家讲讲一些入门经验,希望能给你带来帮助. 工具/原料 .        JDK,ADT,JAVA 方法/步骤 1.   开发工具的准备: 第一个工具:JDK1.7, 第二个工具:Android Developer Tools简称ADT. JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)  只需以上两个工具就可以进行开发

Spring入门到精通&lt;三&gt;Spring访问数据库

Spring在数据访问方面提供了模板化和Dao支持类的提供,方便操作数据库. 一.Spring JDBC 1.配置数据源 DataSource 不管选择哪一种Spring Dao的支持方式,你都需要配置一个数据源的引用.配置方式有三种: ①.使用JNDI查找数据源: ②.使用数据源连接池:(dbcp c3p0 druid)  <!-- 配置数据源 -->      <bean id="dataSource" class="org.apache.commons

ThinkJS框架入门详细教程(一)开发环境

一.前端标配环境 1.nodeJS正确安装,可以参考:http://www.cnblogs.com/chengxs/p/6221393.html 2.git正确安装,可以参考:http://www.cnblogs.com/chengxs/p/6244023.html 二.安装ThinkJS命令 npm install -g think-cli 安装完成后,系统中会有 thinkjs 命令(可以通过 thinkjs -v 查看 think-cli 的版本号,此版本号非 thinkjs 的版本号).