【JavaWeb-24】AOP介绍和术语、手动/半自动/自动实现AOP、基于XML和注解的AspectJ使用、JdbcTemplate的使用

前端之家收集整理的这篇文章主要介绍了【JavaWeb-24】AOP介绍和术语、手动/半自动/自动实现AOP、基于XML和注解的AspectJ使用、JdbcTemplate的使用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1、什么是AOP?面向切面编程。它和面向对象编程(OOP)都是一种编程思想。AOP也是为了实现代码重用,只是它的代码重用是通过代理来实现的,而OOP的代码重用是通过集成来实现的。比如我有一个B类,B类里面有几个方法,现在的需求是不破坏B类的前提下,给B类里的几个方法添加事务。

——如果采用OOP的思想,就是我们继承B类,在新的类里面重写这几个方法,有几个方法,就如下德重写几遍。

class C extends B{
    public void aaa(){
        //开启事务
        super.aaa();
        //提交事务
    }
}

——AOP思想的话,我们需要一个新的类,里面封装的是我们要给B类增强的功能代码,比如此处的事务管理代码。然后我们写一个B类的代理类,我们在代理类里面对B类的每个方法执行前后进行改造,执行前加上开启事务,执行结束加上提交事务。

——这个代理类就是我们学习AOP需要掌握的东西,我们先手写代理类,实际中代理类是由spring帮我们自动生成的。

——动态代理类的实现方法有两种:一种是接口+实现类的方式,spring采用的是jdk的动态代理Proxy。另一种是实现类方式,spring采用的是cglib字节码增强。

2、AOP术语。

——target目标类就是需要被代理被增强的类。joinpoint连接点就是可能被增强的方法。pointcut切入点就是被增强的方法。advice通知/增强,也就是增强代码,有前置的后置的等等。weaving织入过程就是把advice和pointcut连接起来的过程。proxy代理类就是我们主要研究和书写的东西。最后一个aspect切面,就是advice和pointcut的结合。

——补充一下:切面,一般就是指的切面方法或者切面代码,也就是要切入到业务逻辑里的那些增强功能。为什么叫切面呢?我想大概是因为,如果把业务逻辑想象成从上到下的流程的话(想象成圆柱形的),这些代码就是在这些流程的某些位置切入进去,比如在执行一个方法之前开启事务,在方法之后提交事务等,这些切入就像是在圆柱形的流程的某些地方横切了一刀,所以形象地称为切面编程。其本质是,在运行时动态地将代码(增强代码advice)切入到指定类的指定方法中。一般应用比如事务管理、日志管理、性能监测、缓存等。

3、我们先来看看利用JDK的动态代理实现AOP切面编程的功能

——先编写一个UserService接口(必须要接口,因为动态代理就是凭借获得这套接口,才说明这个代理最终是代理谁的),写两简单的方法,然后写一个UserServiceImpl实现类。

——再编写一个增强类,也就是切面类,也就是我们需要添加代码,这个类叫MyAspect,里面写一个myBefore和一个myAfter方法

——再写一个工厂类,为什么?因为我们是模拟动态代理的过程,实际中spring的代理都是由工厂生成出来的,所以我们也要写一个工厂类Mybeanfactory,里面写一个生成代理类的方法createUserService。下面就是模拟的核心。其实本质上就是生成一个代理类(代理的是UserService),然后在处理方法添加了切面方法

package com.hello.factory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.hello.aspect.MyAspect;
import com.hello.service.UserService;
import com.hello.service.impl.UserServiceImpl;


public class Mybeanfactory {
    public static UserService createUserService(){
        //目标类
        final UserService userService=new UserServiceImpl();
        //切面类
        final MyAspect myAspect=new MyAspect();
        //整合,就是生成代理,返回这个代理对象
        UserService userServiceProxy=(UserService) Proxy.newProxyInstance(Mybeanfactory.class.getClassLoader(),userService.getClass().getInterfaces(),new InvocationHandler() {

            public Object invoke(Object proxy,Method method,Object[] args)
                    throws Throwable {
                myAspect.myBefore();
                Object invoke = method.invoke(userService,args);
                myAspect.myAfter();
                return invoke;
            }
        });

        return userServiceProxy;
    }
}

——当然,最终使用的就是我们的代理类,而不使用我们的原先的类:

@Test
    public void test01(){
        UserService createUserService = Mybeanfactory.createUserService();
        createUserService.addUser();
        createUserService.updateUser();
    }

代码JavaEE JDK动态代理实现AOP切面功能

4、CGLIB字节码增强实现AOP编程。

——字节码增强有很多方式,CGLIB只是其中一种,它增强的核心原理是生成一个目标类的子类,然后对子类进行一些增强,最终返回。所以它不需要目标类的接口,只需要目标类即可。

——首先,我们需要导入jar包,这个jar包分为cglib核心包,和asm依赖包。这两个包我们在Hibernate和Struts里面都看到过,但我们的spring-core核心包里面已经包含了这两个,所以我们直接导入spring的core包即可。

——我们写一个目标类UserServiceImpl,写了一个切面类MyAspect同上,只是在工厂类里面有所不同,不是采用的动态代理,而是字节码增强的方式,但我们可以看到,其实核心逻辑都类似,里面的方法都可以和动态代理的一些方法一一对应起来联系:

package com.hello.factory;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.hello.aspect.MyAspect;
import com.hello.service.impl.UserServiceImpl;


public class Mybeanfactory {
    public static UserServiceImpl createUserService(){
        //目标类
        final UserServiceImpl userService=new UserServiceImpl();
        //切面类
        final MyAspect myAspect=new MyAspect();
        //整合,就是生成代理,返回这个代理对象
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(userService.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            ////前3个参数和动态代理的类似,第4个参数其实也有作用,是另一种调用父类方法方法,我们这里的代理其实是UserServiceImpl的子类
            public Object intercept(Object arg0,Method arg1,Object[] arg2,MethodProxy arg3) throws Throwable {
                myAspect.myBefore();
                Object invoke = arg1.invoke(userService,arg2);
//              Object invokeSuper = arg3.invokeSuper(arg0,arg2);
                myAspect.myAfter();
                return invoke;
//              return invokeSuper;
            }
        });
        UserServiceImpl create = (UserServiceImpl) enhancer.create();
        return create;
    }
}

代码JavaEE CGLIB字节码增强方式实现AOP编程

5、知识点。

——利用JDK动态代理实现的方法,我们返回的UserService就是一个动态代理类,对象名称如下:

用CGLIB方式生成的类,从名字也看得出来。

——AOP联盟为Advice做了一些分类。我们只需要掌握环绕通知即可。Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:

@H_502_296@
  • 前置通知 org.springframework.aop.MethodBeforeAdvice,在目标方法执行前实施增强
  • 后置通知 org.springframework.aop.AfterReturningAdvice,在目标方法执行后实施增强
  • 环绕通知 org.aopalliance.intercept.MethodInterceptor,在目标方法执行前后实施增强
  • 异常抛出通知 org.springframework.aop.ThrowsAdvice,在方法抛出异常后实施增强
  • 引介通知 org.springframework.aop.IntroductionInterceptor,在目标类中添加一些新的方法属性
  • 环绕通知,必须手动执行目标方法
    try{
       //前置通知
       //执行目标方法
       //后置通知
    } catch(){
       //抛出异常通知
    }

    6、spring编写代理:半自动

    ——让spring帮我们创建代理对象,我们从spring容器中获取代理对象。所以,我们需要的就是一个目标类(和接口)和切面类,然后配置一下,但是这个切面类我们是要遵循规范的,也就是实现环绕方法并实现了里面的方法,在这个方法里面我们写之前我们写的那些前置和后置代码。然后剩下的生成代理类都是通过spring的xml配置文件配置出来的。

    ——目标类和它的接口没什么好说的,我们再写一个切面类,这时候有所不同了:

    package com.hello.aspect;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAspect implements MethodInterceptor {
    
        public Object invoke(MethodInvocation arg0) throws Throwable {
            System.out.println("方法前");
            //手动执行方法
            Object proceed = arg0.proceed();
            System.out.println("方法后");
            return proceed;
        }
    }

    ——然后再配置文件

    <?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 http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 目标类 -->
        <bean id="userService" class="com.hello.service.UserServiceImpl"></bean>
        <!-- 切面类 -->
        <bean id="myAspect" class="com.hello.aspect.MyAspect"></bean>
        <!-- 代理类,以后真正要用的,就是这个代理类 -->
        <!-- 我们之前说过 factorybean只生产一种特定的类,所以Proxyfactorybean只生产代理类,这是spring自带的工厂 -->
        <bean id="userServiceProxy" class="org.springframework.aop.framework.Proxyfactorybean">
            <!-- 目标类,值是引用 -->
            <property name="target" ref="userService"></property>
            <!-- 实现的接口,这应该是一个array里面包含一个value,但是只有1个value的时候可以省略array而直接写成如下 -->
            <property name="interfaces" value="com.hello.service.UserService"></property>
            <!-- 切面类,这里面也应该是一个array,里面包含多个value,此处同样省略,但是里面的值可以直接用id,上面的接口没有id,所以只能写全限定名称 -->
            <property name="interceptorNames" value="myAspect"></property>
            <!-- 是否采用cglib,配置了,它一定是采用cglib;不配置时,如果有接口优先采用jdk动态代理,否则用cglib -->
            <property name="optimize" value="true"></property>
        </bean>
    </beans>

    ——最后测试一下:

    package com.hello.test;
    
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.hello.service.UserService;
    
    public class TestSemiAutoAOP {
        @Test
        public void test01(){
            String xmlPath="com/hello/aspect/applicationContext.xml";
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
            UserService us=(UserService) classPathXmlApplicationContext.getBean("userServiceProxy");
            us.addUser();
            us.updateUser();
        }
    }

    代码JavaEE spring半自动实现AOP代理

    7、上面半自动的是spring帮我们创建代理对象,我们再从spring获得代理对象。也就是说我们在xml配置文件中写一个代理对象的bean。然后使用获取的时候就取这个代理对象bean的id。

    而全自动的配置思路和这个不太一样,全自动的配置意思就是,我们还是在xml里面配置正常的目标类和切面类的bean,然后不配置代理对象的bean了,而是做一个aop的配置,这里面的大概意思就是把什么切面方法和什么目标方法连接起来。我们使用的时候,还是获取目标类bean的id,但是因为我们有了aop的配置,所以我们表面上是获取目标类bean的id,但是实际上spring给我们返回的是目标类bean的代理对象。如果我们不需要代理对象的话,只要把那段aop的配置删除即可。

    ——我们首先需要导入一个jar包,com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

    ——并且在配置文件添加一个aop命名空间,这样我们就能使用<aop:xxx>了,这个和之前说的P命名空间类似。仍然是之前的那个文件里面去找。

    ——然后开始配置。

    <?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" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 目标类 -->
        <bean id="userService" class="com.hello.service.UserServiceImpl"></bean>
        <!-- 切面类 -->
        <bean id="myAspect" class="com.hello.aspect.MyAspect"></bean>
        <!-- aop配置,proxy-target-class取true表示采用cglib,否是是jdk动态代理-->
        <aop:config proxy-target-class="true">
            <!-- AspectJ表达式:选择方法(任意返回值 包名.任意类。任意方法(任意参数)) -->
            <aop:pointcut expression="execution(* com.hello.service.UserServiceImpl.*(..))" id="myPointCut"/>
            <!-- advisor表示切面,是由一个切入点和一个切面类组成的切面 -->
            <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/>
        </aop:config>
    </beans>

    ——最后测试,注意我们测试的时候,获取对象,是直接获取的目标类,因为我们配置了aop,spring就会自动帮我们返回目标类的代理对象给我们。

    @Test
        public void test01(){
            String xmlPath="com/hello/aspect/applicationContext.xml";
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
            UserService us=(UserService) classPathXmlApplicationContext.getBean("userService");
            us.addUser();
            us.updateUser();
        }

    代码JavaEE spring自动实现AOP代理

    8、AspectJ。是基于java的AOP框架,不太常用,它主要用于自定义的开发的时候。我们先说说切入点表达式。

    ——execution(),用于描述方法。语法execution(修饰符 返回值 包.类.方法名(参数) throws异常)

    @H_502_296@
  • 修饰符,一般省略。public公共方法,*任意
  • 返回值,不能省略。void返回没有值,String返回值字符串,*任意
  • 包,[可省略]。com.itheima.crm固定包,com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service),com.itheima.crm.. crm包下面的所有子包(含自己),com.itheima.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包
  • 类,[可省略]。UserServiceImpl 指定类,*Impl 以Impl结尾,User* 以User开头,*任意
  • 方法名,不能省略。addUser固定方法,add* 以add开头,*Do 以Do结尾,* 任意
  • (参数)。`()无参,(int) 一个整型,(int,int)两个,(..)参数任意
  • throws,可省略,一般不写。
  • 常用案例:

    <aop:pointcut expression="execution(* com.hello.do*.*(..))||execution(* com.hello.*Service.*(..))" id="myPointCut"/>

    ——除了execution之外,还有:

    @H_502_296@
  • within:匹配包或子包中的方法(了解),within(com.hello.aop..*)
  • this:匹配实现接口的代理对象中的方法(了解),this(com.hello.aop.user.UserDAO)
  • target:匹配实现接口的目标对象中的方法(了解),target(com.hello.aop.user.UserDAO)
  • args:匹配参数格式符合标准的方法(了解),args(int,int)
  • bean(id) 对指定的bean所有的方法(了解),bean(‘userServiceId’)
  • 9、AspectJ的通知类型。注意之前我们说的是AOP的通知类型Advice。这里的通知有6种,需要掌握的就是around环绕通知

    @H_502_296@
  • before:前置通知(应用:各种校验)。在方法执行前执行,如果通知抛出异常,阻止方法运行。
  • afterReturning:后置通知(应用:常规数据处理)。方法正常返回后执行,如果方法中抛出异常,通知无法执行。必须在方法执行后才执行,所以可以获得方法的返回值。
  • around:环绕通知(应用:十分强大,可以做任何事情)。方法执行前后分别执行,可以阻止方法的执行。必须手动执行目标方法
  • afterThrowing:抛出异常通知(应用:包装异常信息)。方法抛出异常后执行,如果方法没有抛出异常,无法执行。
  • after:最终通知(应用:清理现场)。方法执行完毕后执行,无论方法中是否出现异常。
  • 环绕
    
    try{
         //前置:before
        //手动执行目标方法
        //后置:afterRetruning
    } catch(){
        //抛出异常 afterThrowing
    } finally{
        //最终 after
    }

    我们看源码的话,也是如上面格式一样,如果是after的源码,那么它的主要部分就是写在finally里面的。

    10、使用AspectJ。基于XML。先导入jar包,除了4+1之外,还需要aop联盟规范和spring-aop,aspectJ规范和spring-aspects。

    ——这里基于XML的AspectJ的使用和上面自动实现AOP的编程很像,我们同样需要的就是目标类(和接口)、切面类和xml配置,不同的是此处的切面类不需要实现标准接口和实现标准方法,这里面的方法我们可以随便写跟平常自定义一样,xml里面的配置自然也是有所不同,就这么一些差别。另外,测试几种通知类型的时候,不要一起测,因为它们彼此之间没有什么关联关系,顺序上不容易把握。

    ——先写目标接口和实现类,我们贴出实现类的代码即可:

    package com.hello.service;
    
    public class UserServiceImpl implements UserService {
    
        public void addUser() {
            System.out.println("addUser方法");
        }
    
        public Integer updateUser() {
            System.out.println("updateUser方法");
            return 888;
        }
    
        public void deleteUser() {
            System.out.println("deleteUser方法");
        }
    }

    下面是切面类的,我们定义一个方法,虽然我们这里名字叫做前置的通知,但说了不算,真正说这个方法是前置通知是通过aop配置指定它为前置通知才行,所以说我们这里的方法名字是任意的随便写。

    package com.hello.aspect;
    
    import org.aspectj.lang.JoinPoint;
    
    public class MyAspect {
        //JoinPoint和Joinpoint不要搞混,它是连接点的信息,就是方法的信息
        public void myBefore(JoinPoint joinpoint){
            System.out.println("我是前置通知"+joinpoint.getSignature().getName());
        }
    }

    然后是配置文件

    <?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 http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 目标类 -->
        <bean id="userService" class="com.hello.service.UserServiceImpl"></bean>
        <!-- 切面类 -->
        <bean id="myAspect" class="com.hello.aspect.MyAspect"></bean>
    
        <!-- aop配置 -->
        <aop:config>
            <aop:aspect ref="myAspect">
                <!-- 定义了一个切入点 -->
                <aop:pointcut expression="execution(* com.hello.service.UserServiceImpl.*(..))" id="myPointCut"/>
                <!-- 下面这里还有一个pointcut="",但是我们上面配置过切入点了,所以用上面的id,否则可以用这个属性,值是上面的表达式,二选一 -->
                <!-- 这就是定义了一个前置通知,把我们写的方法关联进来 -->
                <!-- 这里method是切面类里面我们自己写的方法名,因为上面已经引用了切面类,所以这里直接写方法名即可 -->
                <aop:before method="myBefore" pointcut-ref="myPointCut"/>
            </aop:aspect>
        </aop:config>
    </beans>

    测试:

    @Test
        public void test01(){
            String xmlPath="com/hello/service/applicationContext.xml";
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
            UserService us=(UserService) classPathXmlApplicationContext.getBean("userService");
            us.addUser();
            us.updateUser();
            us.deleteUser();
        }

    结果:

    我是前置通知addUser
    addUser方法
    我是前置通知updateUser
    updateUser方法
    我是前置通知deleteUser
    deleteUser方法

    ——其他几个后置通知、环绕通知、抛出异常通知、after通知等流程都一样,主要是在通知的写法和xml配置参数上稍有差别。我们先说后置通知,这里需要注意的就是返回值的设置和配置:
    后置通知

    public void myReturning(JoinPoint joinpoint,Object ret){
        <!-- 把返回值也输出来 -->
        System.out.println("我是后置通知"+joinpoint.getSignature().getName()+"->"+ret);
    }

    配置:

    <!-- 后置通知 -->
    <!-- returning取值就是后置通知的第二个参数 -->
    <aop:after-returning method="myReturning" pointcut-ref="myPointCut" returning="ret"/>

    测试都一样,免了,直接看结果,有返回值的就显示出来了:

    addUser方法
    我是后置通知addUser->null
    updateUser方法
    我是后置通知updateUser->888
    deleteUser方法
    我是后置通知deleteUser->null

    ——环绕通知。有返回值Object,需要使用JoinPoint的子接口ProceedingJoinPoint 。需要手动执行方法

    public Object myAround(ProceedingJoinPoint joinpoint) throws Throwable{
        System.out.println("我是around的前置");
        Object proceed = joinpoint.proceed();
        System.out.println("我是around的后置");
        return proceed;
    }

    配置,没什么好说的:

    <!-- 环绕通知 -->
                <aop:around method="myAround" pointcut-ref="myPointCut"/>

    直接看结果:

    我是around的前置
    addUser方法
    我是around的后置
    我是around的前置
    updateUser方法
    我是around的后置
    我是around的前置
    deleteUser方法
    我是around的后置

    ——再看抛出异常通知,注意第二个参数是异常信息。

    public void myAfterThrowing(JoinPoint joinpoint,Throwable e){
        System.out.println("我是抛出异常通知"+"->"+e.getMessage());
    }

    看配置:

    <!-- 抛出异常通知 -->
    <!-- throwing就是抛出异常通知的第二个参数,这个配合和返回值的那个配置类似 -->
    <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>

    测试之前我们把目标类的一个方法修改一下得出异常:

    public Integer updateUser() {
        System.out.println("updateUser方法");
        int i=1/0;
        return 888;
    }

    看结果:

    addUser方法
    updateUser方法
    我是抛出异常通知->/ by zero

    ——最后一个是最终通知,没什么新增的属性和配置。

    public void myAfter(JoinPoint joinpoint){
        System.out.println("我是最终通知"+joinpoint.getSignature().getName());
    }

    配置:

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

    结果:

    addUser方法
    我是最终通知addUser
    updateUser方法
    我是最终通知updateUser
    deleteUser方法
    我是最终通知deleteUser

    代码JavaEE AspectJ基于XML的配置

    11、基于注解的AspectJ。主要就是把在xml里面的配置都替换成@xxx之类的。

    ——我们需要先添加命名空间,context命名空间,因为需要扫描注解类。

    <?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" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!-- 扫描注解包 -->
        <context:component-scan base-package="com.hello"></context:component-scan>
    
        <!-- aop的注解也需要开启 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    然后在UserServiceImpl实现类上面添加

    @Service("userService")

    然后在切面类上面添加

    //它三层都不是,所以就用@Component,然后把它声明为成切面,相当于<aop:aspect ref="myAspect">
    @Component
    @Aspect

    在切面类里面声明前置方法

    //此处省略了value=
    @Before("execution(* com.hello.service.UserServiceImpl.*(..))")
    public void myBefore(JoinPoint joinpoint){
        System.out.println("我是前置通知"+joinpoint.getSignature().getName());
    }

    就OK了。

    ——下面流程是一样的,后置通知,注意返回值的写法,以及我们在此声明了一个公共切入点,后面直接引用即可,不用每次都写一大串的表达式了。

    //公共切入点声明 private void xxx(){} 引用的时候用方法
    @Pointcut("execution(* com.hello.service.UserServiceImpl.*(..))")
        private void myPointCut(){
    }

    然后写后置方法即可:

    @AfterReturning(value="myPointCut()",returning="ret")
    public void myReturning(JoinPoint joinpoint,Object ret){
        System.out.println("我是后置通知"+joinpoint.getSignature().getName()+"->"+ret);
    }

    ——环绕通知

    @Around(value="myPointCut()")
    public Object myAround(ProceedingJoinPoint joinpoint) throws Throwable{
        System.out.println("我是around的前置");
        Object proceed = joinpoint.proceed();
        System.out.println("我是around的后置");
        return proceed;
    }

    ——抛出异常通知

    @AfterThrowing(value="myPointCut()",throwing="e")
    public void myAfterThrowing(JoinPoint joinpoint,Throwable e){
        System.out.println("我是抛出异常通知"+"->"+e.getMessage());
    }

    ——最终通知

    @After(value="myPointCut()")
    public void myAfter(JoinPoint joinpoint){
        System.out.println("我是最终通知"+joinpoint.getSignature().getName());
    }

    会使用XML配置后,注解的使用原理是一致的,只需要注意注解的各种写法。

    代码JavaEE AspectJ基于注解的配置

    12、 JdbcTemplate,spring提供的操作JDBC的工具类,类似于DBUtils。它依赖数据源(连接池)dataSource。

    ——我们先来一个最原始的,使用不适用spring配置文件直接使用api的时候。先创建数据表和导入jar包:

    然后写实体类User。此处略去。

    最后写一个测试类。直接在测试类里面写数据源的设置,而不是用xml配置。我们在此可知的是:JdbcTemplate模板创建的话需要数据源,然后创建后得到的JdbcTemplate对象可以使用query和update等操作数据

    @Test
        public void test01(){
            //创建数据源
            BasicDataSource dataSource=new BasicDataSource();
            //设置数据源
            dataSource.setDriverClassName("com.MysqL.jdbc.Driver");
            dataSource.setUrl("jdbc:MysqL://localhost:3306/spring01");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
    
            //创建JdbcTemplate模板
            JdbcTemplate jdbcTempplate=new JdbcTemplate(dataSource);
    
            //操作数据
            jdbcTempplate.update("insert into t_user(username,password) values(?,?)","Eric","1234");
        }

    ——DBCP配置。我们知道上面只要写到new的都可以用Ioc在xml里面让spring管理,而且写到setxxx的基本都可以在xml里用property注入来解决。所以我们可以管理上面的代码
    我们先写一个UserDao接口和实现类,把操作数据库代码放到这里面,当然这里面也是用到JdbcTemplate的,作为属性注入。

    public class UserDaoImpl implements UserDao {
        private JdbcTemplate jdbcTemplate;
        //我们只要在applicationContext里面配置了UserDaoImpl的bean和这个属性,JdbcTemplate就会自动注入了
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        public void saveUser(User user) {
            jdbcTemplate.update("update t_user set username=?,password=? where id=?",user.getUsername(),user.getPassword(),user.getId());
        }
    }

    然后,我们直接写配置了,差不多把上面写的代码都在这里面配置了一遍,从数据源到Jdbc模板,然后到实现类的bean。

    <?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="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="com.MysqL.jdbc.Driver"></property>
            <property name="url" value="jdbc:MysqL://localhost:3306/spring01"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <!-- 创建jdbcTemplate,需要数据源,所以在上面创建数据源 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 我们最终要使用的,需要一个jdbcTemplate的ref,所以在上面创建一个jdbcTemplate -->
        <bean id="userDao" class="com.hello.dao.impl.UserDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>
    </beans>

    最终使用:

    @Test
        public void test02(){
            String xmlPath="com/hello/domain/applicationContext.xml";
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(xmlPath);
            UserDao userDao=(UserDao)classPathXmlApplicationContext.getBean("userDao");
    
            User u=new User();
            u.setId(1);
            u.setUsername("Andy");
            u.setPassword("9999");
    
            userDao.saveUser(u);
        }

    ——我们再来做一遍C3P0的配置,其他不变,主要在配置上面做一些改变,因为有些属性名称和DBCP不一样。所以只改变数据源配置这一块即可。

    <!-- C3P0配置数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.MysqL.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:MysqL://localhost:3306/spring01"></property>
            <property name="user" value="root"></property>
            <property name="password" value="root"></property>
        </bean>

    ——我们上面xml配置文件里面创建的JdbcTemplate都是自动创建的,也就是说我们给了一个数据源,然后系统就帮我们自动创建了,那么我们其实是可以省略手动配置JdbcTemplate这个步骤的,相当于我们直接给我们的目标类bean一个数据源,它会帮我们自动创建JdbcTemplate。

    这里就需要添加一些规范,比如我们的数据类需要继承自JdbcDaoSupport,这个父类里面会帮我们自动创建JdbcTemplate。

    我们在UserDaoImpl 实现类里面如下写,继承了JdbcDaoSupport ,这样我们就不能设置setJdbcTemplate了,因为父类里面有了,而且是final修饰的不允许覆写。

    public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
    
        public void saveUser(User user) {
            this.getJdbcTemplate().update("update t_user set username=?,user.getId());
        }
    }

    配置文件也变得简单了,配置了一下数据源,然后直接用数据源给目标类的bean当做属性注入即可(这个属性甚至不需要写在我们的目标实现类里面,因为在父类JdbcDaoSupport里面已经写了):

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.MysqL.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:MysqL://localhost:3306/spring01"></property>
            <property name="user" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
    
        <bean id="userDao" class="com.hello.dao.impl.UserDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
        </bean>

    ——最后,我们还可以使用properties文件来储存数据源的配置数据,然后进一步简化applicationContext里面的东西。

    先写properties文件

    然后,修改配置文件,当然得先添加context的命名空间,之前说过。

    <!-- 需要先加载属性文件 -->
        <!-- classpath:前缀表示 src下,配置好之后通过 ${key}来获得值 -->
        <context:property-placeholder location="classpath:dataSourceInfo.properties" />
    
        <!-- 利用属性文件来进行C3P0配置 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}"></property>
            <property name="jdbcUrl" value="${jdbc.url}"></property>
            <property name="user" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.pwd}"></property>
        </bean>

    代码JavaEE JdbcTemplate的简单示例

    其他的bean的书写都不变,这样也可以成功使用JdbcTemplate模板来操作数据库

    猜你在找的XML相关文章