spring学习(三) spring AOP

更多spring相关的文章可查看
spring学习(一)
spring学习(二) 自动装配的歧义

四、spring AOP
spring AOP(Aspect Oriented Programming)即面向切面的编程,在我们平常的oop(Object Oriented Programming,面向对象编程)中,难免会有一些照顾不到的地方,举个例子,我们的日志功能。在大多数的业务逻辑中,日志是必不可少的,基于oop,我们需要在实现每个对象的核心功能时,加上启用日志的代码。这些代码与我们本身需要实现的业务逻辑并无关联,而且会在多处复用到,因此我们会希望将这些与业务无关,却被多个模块共同调用的逻辑提取并封装起来,这样系统中的重复代码数量会大大减少,功能的逻辑也会更加清晰,易于维护。

1、常用术语

  1. Advice:通知,切面所要完成的工作。spring切面有5种类型的通知:前置通知(Before),后置通知(After),环绕通知(Around),返回通知(After-returning),异常通知(After-throwing)。
  2. Join point:连接点,应用通知的点,是能够插入切面的一个点,spring只支持方法级别的连接点
  3. Poincut:切点,定义了切面在哪里插入,直观点讲一般体现为一个切入点表达式
  4. Aspect:切面,通知和切点的结合,一般是一个类
  5. Introduction:引入,允许我们向现有的类添加新方法或属性
  6. Weaving:织入,把切面应用到目标对象并创建新的对象代理的过程。

2、使用注解创建简单的切面
在xml文件中启用AspectJ自动代理

<!--启用AspectJ自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

注意proxy-target-class="true",当该属性为true时,是基于类的代理,采用cglib,当为false时,则是基于接口的jdk动态代理,默认值是false
我们可以通过一个简单的类来看明白.

package com.shiqy.controller;

import com.shiqy.config.MyAspectConfig;
import com.shiqy.dao.UserDao;
import com.shiqy.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@Component
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mybatis.xml")
//@ContextConfiguration(classes = MyAspectConfig.class)
public class MyLogAspectTest {
    @Autowired
    private UserService userService;

    @Autowired
    private UserDao userDao;
    @Test
    public void test() {
        System.out.println(userDao.getClass());
        System.out.println(userService.getClass());
    }
}

proxy-target-class="false"时,结果为

class com.sun.proxy.$Proxy25
class com.sun.proxy.$Proxy27

当为true时,结果为

class com.sun.proxy.$Proxy25
class com.shiqy.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$cc3824e3

一个简单的切面

package com.shiqy.service.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyLogAspect {
    @Before("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
    public void beforeMethod(){
        System.out.println("记录日志!!!");
    }

    @After("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
    public void afterMethod(){
        System.out.println("记录成功!!!");
    }
}

@AspectJ注解表明该类是一个切面,@Before@After注解代表通知的类型为前置通知和后置通知,execution()用于匹配是连接点的执行方法,execution(* com.shiqy.service.impl.UserServiceImpl.*(..))代表UserServiceImpl类的所有方法,该切面的作用是当执行指定实现类中的任意方法前后都会发送通知.
在上述代码中,@Before@After所注解的切点表达式冗余了,我们可以使用@PointCut注解定义可重用的切点

package com.shiqy.service.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyLogAspect {
    @Pointcut("execution(* com.shiqy.service.impl.UserServiceImpl.*(..))")
    public void myMethod(){ }

    @Before("myMethod()")
    public void beforeMethod(){
        System.out.println("记录日志!!!");
    }

    @After("myMethod()")
    public void afterMethod(){
        System.out.println("记录成功!!!");
    }
}

接下来写测试类

package com.shiqy.controller;

import com.shiqy.config.MyAspectConfig;
import com.shiqy.dao.UserDao;
import com.shiqy.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@Component
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-mybatis.xml")
//@ContextConfiguration(classes = MyAspectConfig.class)
public class MyLogAspectTest {
    @Autowired
    private UserService userService;
    @Test
    public void test(){
        long id =1;
        System.out.println(userService.selectUser(id).getUsername());
    }
}

spring-mybatis.xml中,我们需要配置
<context:component-scan base-package="com.shiqy.service"/>自动扫描得到我们的切面,运行结果为

20:52:26.701 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-commons.properties' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-log.properties' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found. Skipping.
20:52:26.702 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/c3p0.properties' could not be found. Skipping.
20:52:26.801 [main] INFO com.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
20:52:26.850 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 registered.
20:52:26.874 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 unregistered, in order to be reregistered after update.
20:52:26.874 [main] DEBUG com.mchange.v2.c3p0.management.DynamicPooledDataSourceManagerMBean - MBean: com.mchange.v2.c3p0:type=PooledDataSource,identityToken=z8kflt9u1r0kgrq1ycbj82|7fd50002,name=z8kflt9u1r0kgrq1ycbj82|7fd50002 registered.
记录日志!!!
20:52:27.380 [main] INFO com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 2, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 10000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> z8kflt9u1r0kgrq1ycbj82|7fd50002, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> z8kflt9u1r0kgrq1ycbj82|7fd50002, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/springmvc?useUnicode=true&characterEncoding=utf8&useSSL=true, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 30, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 10, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-commons.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/mchange-log.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier '/c3p0.properties' could not be found. Skipping.
20:52:27.392 [main] DEBUG com.mchange.v2.cfg.MConfig - The configuration file for resource identifier 'hocon:/reference,/application,/c3p0,/' could not be found. Skipping.
20:52:27.393 [main] WARN com.mchange.v2.resourcepool.BasicResourcePool - Bad pool size config, start 3 < min 10. Using 10 as start.
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - com.mchange.v2.resourcepool.BasicResourcePool@2b0f373b config: [start -> 10; min -> 10; max -> 30; inc -> 3; num_acq_attempts -> 2; acq_attempt_delay -> 1000; check_idle_resources_delay -> 0; max_resource_age -> 0; max_idle_time -> 0; excess_max_idle_time -> 0; destroy_unreturned_resc_time -> 0; expiration_enforcement_delay -> 0; break_on_acquisition_failure -> false; debug_store_checkout_exceptions -> false; force_synchronous_checkins -> false]
20:52:27.394 [main] DEBUG com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager - Created new pool for auth, username (masked): 'ro******'.
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - acquire test -- pool size: 0; target_pool_size: 10; desired target? 1
20:52:27.394 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool - awaitAvailable(): [unknown]
记录成功!!!

猜你喜欢

转载自blog.csdn.net/qq_38359443/article/details/79843395