zoukankan      html  css  js  c++  java
  • Spring-AOP

    学而时习之,不亦说乎!

                                 --《论语》

    aopalliance(Aspect-Oriented Programming Alliance)为AOP制定了一些规范:

    可以看到,全是接口,spring已经将这些接口整合到了aop的包里面,因此,我们在使用的时候不需要引入这个包。

     AOP联盟为通知Advice定义了org.aopalliance.aop.Advice,这个接口是AOP最重要的接口之一。

    Spring按照通知Advice在目标类方法连接点的位置,可以分为五类:

    1)前置通知:org.springframework.aop.MethodBeforeAdvice(在目标方法执行前实施增强)

    2)后置通知:org.springframework.aop.AfterReturningAdvice(在目标方法执行后实施增强)

    3)环绕通知:org.aopalliance.intercept.MethodInterceptor(在目标方法执行前后实施增强)

    4)异常通知:org.springframework.aop.ThrowsAdvice(在方法抛出异常后实施增强)

    5)引介通知:org.springframework.aop.IntroductionInterceptor(在目标类中添加一些新的方法和属性

    这里的五个通知类型都实现了Advice接口,最重要的通知:环绕通知。因为环绕通知需要手动执行,所以,可以说是可以代替其他几个通知的。

    		try {
    			前置通知
    			方法执行
    			后置通知
    		} catch (Exception e) {
    			异常通知
    		}
    

    使用Spring创建单个代理对象:

    1)项目整体结构如下:

    2)创建maven项目,pom.xml如下:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.zby</groupId>
    	<artifactId>aop</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<dependencies>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aop</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-test</artifactId>
    			<version>4.3.8.RELEASE</version>
    		</dependency>
    		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    		<dependency>
    			<groupId>org.aspectj</groupId>
    			<artifactId>aspectjweaver</artifactId>
    			<version>1.8.10</version>
    		</dependency>
    	</dependencies>
    </project>

    其中的aspectjweaver只有在创建多个代理对象才用到。

    3)创建UserService接口:

    package com.zby.service;
    
    public interface UserService {
    
    	void saveUser(String username, String password);
    }
    

    4)创建UserService接口实现类UserServiceImpl:

    package com.zby.service.impl;
    
    import org.springframework.stereotype.Service;
    
    import com.zby.service.UserService;
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
    	public void saveUser(String username, String password) {
    		System.out.println("save user[username=" + username + ",password=" + password + "]");
    	}
    
    }
    

    5)创建实现指定接口MyInterceptor的切面类MethodInterceptor:

    package com.zby.interceptor;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyInterceptor implements MethodInterceptor {
    
    	@Override
    	public Object invoke(MethodInvocation invocation) throws Throwable {
    		try {
    			System.out.println("I get datasource here and start transaction");
    			invocation.proceed();
    			System.out.println("I get datasource here and end transaction");
    		} catch (Exception e) {
    			System.out.println("a exception is catched.");
    		}
    		return null;
    	}
    
    }
    

    6)创建spring的配置文件ApplicationContext.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"
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<!-- 目标类 -->
    	<bean id="userService" class="com.zby.service.impl.UserServiceImpl"></bean>
    
    	<!-- 切面类 -->
    	<bean id="myInterceptor" class="com.zby.interceptor.MyInterceptor"></bean>
    
    	<!-- 代理类 -->
    	<!-- 
    		* 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean 
    		* ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象 
    		interfaces : 确定接口们 通过<array>可以设置多个值 只有一个值时,value="" 
    		target : 确定目标类 
    		interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value="" 
    		optimize :强制使用cglib <property name="optimize" value="true"></property> 
    		底层机制 如果目标类有接口,采用jdk动态代理 
    		如果没有接口,采用cglib 字节码增强 
    		如果声明 optimize = true ,无论是否有接口,都采用cglib -->
     	<bean id="proxyUserService" class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="interfaces" value="com.zby.service.UserService "></property>
    		<property name="target" ref="userService"></property>
    		<property name="interceptorNames" value="myInterceptor"></property>
    		<property name="optimize" value="true"></property>
    	</bean>
    	
    </beans>
    

    7)创建测试类:

    package com.zby.test;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.zby.service.UserService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
    public class ProxyTest {
    
    	@Resource(name = "userService")
    	private UserService	userService;
    
    	@Resource(name = "proxyUserService")
    	private UserService	proxyUserService;
    
    
    
    	@Test
    	public void testProxy() {
    		System.out.println("before Proxy......");
    		userService.saveUser("zby", "1234567890");
    		System.out.println("After Proxy......");
    		proxyUserService.saveUser("zby", "1234567890");
    	}
    }
    

    8)控制台打印结果:

    六月 05, 2017 8:42:38 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
    信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    六月 05, 2017 8:42:38 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
    六月 05, 2017 8:42:38 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
    六月 05, 2017 8:42:38 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    六月 05, 2017 8:42:38 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
    信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@498d1538, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@4d6c3541, org.springframework.test.context.support.DirtiesContextTestExecutionListener@7b1c661c]
    六月 05, 2017 8:42:38 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [applicationContext.xml]
    六月 05, 2017 8:42:38 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.GenericApplicationContext@2f5ca208: startup date [Mon Jun 05 20:42:38 CST 2017]; root of context hierarchy
    before Proxy......
    save user[username=zby,password=1234567890]
    After Proxy......
    I get datasource here and start transaction
    save user[username=zby,password=1234567890]
    I get datasource here and end transaction
    六月 05, 2017 8:42:39 下午 org.springframework.context.support.GenericApplicationContext doClose
    信息: Closing org.springframework.context.support.GenericApplicationContext@2f5ca208: startup date [Mon Jun 05 20:42:38 CST 2017]; root of context hierarchy
    

    使用Spring创建多个代理对象:

    1)本例在上面例子中改造,首先修改applicationContext为:

    <?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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<!-- 目标类 -->
    	<bean id="userService" class="com.zby.service.impl.UserServiceImpl"></bean>
    
    	<!-- 切面类 -->
    	<bean id="myInterceptor" class="com.zby.interceptor.MyInterceptor"></bean>
    		<!-- 
    		1 .导入aop命名空间
    		2 .使用 <aop:config>进行配置
    				proxy-target-class="true" 声明时使用cglib代理
    			<aop:pointcut> 切入点 ,从目标对象获得具体方法
    			<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
    				advice-ref 通知引用
    				pointcut-ref 切入点引用
    		3 .切入点表达式
    			execution(* com.zby.service.*.*(..))
    			选择方法         返回值任意   包             类名任意   方法名任意   参数任意 -->
    	<aop:config proxy-target-class="true">
    		<aop:pointcut expression="execution(* com.zby.service.*.*(..))" id="myPointCut"/>
    		<aop:advisor advice-ref="myInterceptor" pointcut-ref="myPointCut"/>
    	</aop:config>
    	
    </beans>
    

    2)编写测试方法:

    package com.zby.test;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.zby.service.UserService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
    public class ProxyTest {
    
    	@Autowired
    	private UserService userService;
    
    
    
    	@Test
    	public void testProxy() {
    		System.out.println("After Proxy......");
    		userService.saveUser("zby", "1234567890");
    	}
    }
    

    3)控制台打印结果:

    六月 05, 2017 8:32:02 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames
    信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
    六月 05, 2017 8:32:02 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttribute]
    六月 05, 2017 8:32:02 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    六月 05, 2017 8:32:02 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
    六月 05, 2017 8:32:02 下午 org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners
    信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@43b1510a, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2e3f8a3e, org.springframework.test.context.support.DirtiesContextTestExecutionListener@4d8d042a]
    六月 05, 2017 8:32:03 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [applicationContext.xml]
    六月 05, 2017 8:32:03 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.GenericApplicationContext@6cecf7fa: startup date [Mon Jun 05 20:32:03 CST 2017]; root of context hierarchy
    After Proxy......
    I get datasource here and start transaction
    save user[username=zby,password=1234567890]
    I get datasource here and end transaction
    

      总结:这两种方法看起来一样,但是又有点不一样。前者适合给单个bean代理,后者适合给多个bean代理。后者看起来配置比较灵活,但是切入点表达式需要理解。有一个需要注意的是,当我们的类没有实现接口的时候,会使用CGLIB代理,但是在这儿我们没有引入CGLIB的包!因为spring-core整合了CGLIB的包:

    最重要的是,这两种,我基本都只看到别的框架在用,我们自己用,还有更简单的spring整合aspectJ的方式。

  • 相关阅读:
    Android仿人人客户端(v5.7.1)——个人主页(三)
    hdu2554-N对数的排列问题
    POJ1363:Rails
    golang printf
    HDU1200:To and Fro
    [C# 基础知识系列]专题六:泛型基础篇——为什么引入泛型
    poj 2480 (欧拉函数应用)
    Re:从0开始的微服务架构--(二)快速快速体验微服务架构?--转
    爬虫推荐的工具
    python2 与 python3 语法区别--转
  • 原文地址:https://www.cnblogs.com/zby9527/p/6946952.html
Copyright © 2011-2022 走看看