面向切面编程的两种实现

1、面向切面的定义自行百度

2、面向切面编程的应用场景自己想象,大概就是日志之类的地方

3、上面两句话基本是废话


实现方式一,在XML中声明切面

1、编写一个原始类

package com.hsb.beans;

import org.springframework.stereotype.Repository;

@Repository
public class Perform {
	public void show(){
		System.out.println("message from Perform.show()");
	}
}
本例中show()方法就是切点。在一个大型项目中,许多个切点构成了切面,这些切面实际上是为了某些共同的东西而成为一个面的。
2、编写一个切面类

package com.hsb.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class Audience {
	public void beforeShow(){
		System.out.println("message from beforeShow");
	}
	public void afterShow(){
		System.out.println("message from afterShow");
	}
	public void around(ProceedingJoinPoint joinpoint){
		System.out.println("message from Start around");
		long start = System.currentTimeMillis();
		try {
			joinpoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println("message from End around total : "+(end-start)+" ms");
	}
}

注意代码中的joinpoint.proceed(),这句话就是实际调用切点方法,也就是本例中的show()。如果切面类中定义了around通知,通知一定要加上这句话,否则要切点方法不会被调用!此处应该是一个坑,一定要记住,免得后面找bug到处都找不到。


3、配置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" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                        http://www.springframework.org/schema/aop   
                        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
                        http://www.springframework.org/schema/tx   
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
                        http://www.springframework.org/schema/context   
                        http://www.springframework.org/schema/context/spring-context-3.0.xsd"
	default-lazy-init="true">
	<context:component-scan base-package="com.hsb.beans" />
	<bean id="audience" class="com.hsb.aop.Audience" />
	<aop:config>
		<aop:aspect ref="audience">
			<aop:pointcut expression="execution(* com.hsb.beans.Perform.show(..))"
				id="performance" />
			<aop:before method="beforeShow" pointcut-ref="performance" />
			<aop:after method="afterShow" pointcut-ref="performance" />
			<aop:around method="around" pointcut-ref="performance" />
		</aop:aspect>
	</aop:config>
</beans>
注意,上面的<bean id="audience" class="com.hsb.aop.Audience" />将切面类生命为一个bean,而<aop:config></aop:config>声明中引用了这个bean.简单解释一下<aop:config>声明中各句的意思。配置声明的时候,一定注意使用英文字符进行配置,不然会报很多稀奇古怪的错误,大致就是说切面无法使用或者其他什么的。

<aop:pointcut expression="execution(* com.hsb.beans.Perform.show(..))"
				id="performance" />
声明了一个切点(pointcut),后面声明的通知会引用。

通知一共有五种分别是<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing />、<aop:around />。根据其字面意思大概就能懂其应用场景,而around上面已经说过了,标识出切点位置,在切点前后做某些工作。

4、测试

package com.hsb.dao;

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.hsb.beans.Perform;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:**/applicationContext.xml","classpath*:**/springmvc-servlet.xml"})
public class PerformTests {
	@Autowired
	private Perform perform;

	@Test
	public void testShow() {
		perform.show();
	}

}
上面的代码使用自动注入生成一个Perform实例,在测试方法中调用切点方法show().

5、Console打印结果

九月 05, 2016 9:54:03 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from file [D:\PractiseForWork\WorkSpace\HelloMaven\target\classes\applicationContext.xml]
九月 05, 2016 9:54:04 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@6e3c1e69: startup date [Mon Sep 05 21:54:04 CST 2016]; root of context hierarchy
message from beforeShow
message from Start around
message from Perform.show()
message from End around total : 38 ms
message from afterShow
从Console的打印结果中,我们可以猜测出五种通知的具体用法


6、Maven工程一定要添加依赖

                <dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.12</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.12</version>
		</dependency>
如果不添加上述依赖,极有可能会测试报错。



实现方式二,注解切面

1、编写一个原始类

package com.hsb.beans;

import org.springframework.stereotype.Repository;

@Repository
public class Perform {
	public void show(){
		System.out.println("message from Perform.show()");
	}
}
2、编写一个切面类

package com.hsb.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
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 Audience {
	@Pointcut("execution(* com.hsb.beans.Perform.show(..))")
	public void performance() {
	}

	@Before("performance()")
	public void beforeShow() {
		System.out.println("message from beforeShow");
	}

	@After("performance()")
	public void afterShow() {
		System.out.println("message from afterShow");
	}

	@Around("performance()")
	public void around(ProceedingJoinPoint joinpoint) {
		System.out.println("message from Start around");
		long start = System.currentTimeMillis();
		try {
			joinpoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();
		System.out.println("message from End around total : " + (end - start)
				+ " ms");
	}
}
@Aspect将此类声明为切面类,@Component将此类声明为bean放到spring容器中,@Pointcut将下面的performance声明为一个切点,并将Perform中的show()方法进行了关联。execution(* com.hsb.beans.Perform.show(..))的意思是,执行此方法时,忽略返回值,参数类型和个数忽略。还可以更加简写,用于匹配合适的方法,譬如对Perform类的全部方法进行匹配就是com.hsb.beans.Perform.*。

3、配置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" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                        http://www.springframework.org/schema/aop   
                        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
                        http://www.springframework.org/schema/tx   
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
                        http://www.springframework.org/schema/context   
                        http://www.springframework.org/schema/context/spring-context-3.0.xsd"
	default-lazy-init="true">
	<context:component-scan base-package="com.hsb.*" />
	<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
可以看见此处就没有使用<aop:config />声明,而是使用<aop:aspectj-autoproxy proxy-target-class="true" />。这句话的意思是自动在spring上下文中创建一个AnnotationAwareAspectJAutoProxyCreator类,它会自动代理一些bean,这些bean的方法需要与使用@Aspect注解的bean中所定义的切点相匹配,而这些切点又是使用@Pointcut注解定义出来的。proxy-target-class="true"的意思是使用基于类的代理使用 cglib库,如果为false则使用jdk自带的基于接口的代理

4、测试

package com.hsb.dao;

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.hsb.beans.Perform;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:**/applicationContext.xml","classpath*:**/springmvc-servlet.xml"})
public class PerformTests {
	@Autowired
	private Perform perform;

	@Test
	public void testShow() {
		perform.show();
	}

}

5、Console打印结果

九月 05, 2016 10:24:46 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from file [D:\PractiseForWork\WorkSpace\HelloMaven\target\classes\applicationContext.xml]
九月 05, 2016 10:24:47 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.GenericApplicationContext@6e3c1e69: startup date [Mon Sep 05 22:24:47 CST 2016]; root of context hierarchy
message from Start around
message from beforeShow
message from Perform.show()
message from End around total : 39 ms
message from afterShow

如上所示,完全是一样的效果。可以看出使用注解减少了很多工作,不容易出错,各个组件间的耦合度降低了。试想,如果一个大型项目中有很多切面,切点,如果全部去xml中配置,将会是一项极为艰苦的工作,但是如果使用注解就可以做很少的工作完成这一切。



后记一、使用注解时一定要使用@component或者@Repository、@Controller、@Service中一个去声明,将切面类放入到spring容器中,不然就去xml中显式写一个bean,不然的话就会报错,无法实现切面功能。


  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值