基于注解spring AOP实现

####spring容器的三大特点:控制反转,切面编程(Aspect of Program)与事务管理
####这次写了个spring切面编程的demo,spring对切面编程的支持主要在对象的方法调用层次.用最简单的手段实现最多的功能,这是spring的设计哲学.因此spring支持最完善的也是对于方法的拦截.
####spring进行切面编程的主要手段是生成代理.在代理对象执行目标对象所拦截方法前后或者周围提供各种功能.
####切面编程比较多的应用在日志收集和事物管理和权限检查等等场景.将一些横切逻辑单独成可以管理的模块进行统一管理,可以灵活的定义在一类动作执行前后做一些相关的事情.
####spring生成代理的方式,主要使用jdk动态代理和CGLIB动态代理,如果需要代理的对象实现了某个接口,则采用采用jdk提供的默认代理机制,生成的对象也实现该接口并并拥有一个目标对象的引用,这里类似于生成了静态代理类模式的结构.
####如果没有实现某个接口,spring就采用cglib的方式生成代理,它的原理也不难理解,采用为目标类生成子类的方式,然后再重写目标类的目标方法,然后在对应的类周围添加逻辑,这样子类就完成了类似于代理的功能,但是这种代理的缺陷是子类无法重写父类的final函数.
####代理可以分为静态代理和动态代理,aop大都是采用动态代理的机制,在Java中属于类加载机制中的类初始化阶段,对应的引用替换成代理对象,也可以叫做Java里动态绑定机制,这样虽然相比静态代理开销多了一些,但是增加的灵活性是更有价值的.
接下来,我们用spring注解方式验证jdk与cglib两种方法:

最近写反射发现一个问题,使用apache common的MethodUtils方法获取不到对应方法的注解。
Method[] methods = MethodUtils.getMethodsWithAnnotation(bean.getClass(), A.class);
发现这个是由于Spring的bean被代理成CGlib的对象,自然拿不到被代理类的注解,因此最好使用AnnotationUtils.这里面封装了很多逻辑。

// 定义目标对象实现的接口
interface Server {
	void doBusiness(String params);
}
// 目标对象,需要被代理的对象
@Component
class TomcatServer implements Server {

	public void doBusiness(String params) {
		System.out.println("call set name ,value : " + params);
	}
	
}

我们定义切点拦截器

@Component
@Aspect
class MyInterceptor {
	// 通知方法设置
	@Pointcut("execution(* micro.test.spring.TomcatServer.*(..))")
	public void pointCut(){}
	
	// 在切点方法调用之前
	@Before("pointCut() && args(name)")
	public void doBeforeMethod(String name) {
		System.out.println("args name is " + name);
		System.out.println("before aspect do something");
	}
	// 方法之后
	@After("pointCut() && args(name)")
	public void doAfterMethod(String name) {
		System.out.println("args name is " + name);
		System.out.println("after aspect do something");
	}
	
	@AfterReturning("pointCut() && args(name)")
	public void doAfterReturn(String name) {
		System.out.println("params is " + name);
		System.out.println("do something after return");
	}
	
	@AfterThrowing("pointCut() && args(name)")
	public void doAfterThrow(String name) {
		System.out.println("params is " + name);
		System.out.println("do something after throw ...");
	}
	
	@Around("pointCut()")
	public void doSomethingAroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("do something around method execute");
		System.out.println("do something before proxy method call");
		proceedingJoinPoint.proceed();
		System.out.println("do something after proxy method call");
	}
}

最开始我测试一直报异常error at ::0 formal unbound in pointcut.后来才发现是注解参数没写对,通知函数如果有参数必须在注解中添加 args内容,否则会织入抛出异常.
####好了,我们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" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/util
		http://www.springframework.org/schema/util/spring-util-3.0.xsd  
       http://www.springframework.org/schema/aop   
       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


	<!-- 开启aspect代理 -->
	<aop:aspectj-autoproxy />
	<!-- 开启注解 -->
	<context:annotation-config />
	<!-- 扫描包 -->
	<context:component-scan base-package="micro.test.spring" />

</beans>

####测试

		// 加载spring容器
		ApplicationContext context = new ClassPathXmlApplicationContext("*.xml");
		Server tomcatServer = (Server)context.getBean("tomcatServer");
		tomcatServer.doBusiness("test params");
		System.out.println(tomcatServer.getClass());

测试结果:

do something around method execute
do something before proxy method call
args name is test params
before aspect do something
call set name ,value : test params
do something after proxy method call
args name is test params
after aspect do something
params is test params
do something after return
class micro.test.spring.$Proxy12

最后一行打印出来的代理表明获取的是jdk生成的代理对象.

###如果目标对象没有实现接口,改写如下:

@Component
class TomcatServer {

	public void doBusiness(String params) {
		System.out.println("call set name ,value : " + params);
	}

}

测试代码:

// 加载spring容器
		ApplicationContext context = new ClassPathXmlApplicationContext("*.xml");
		TomcatServer tomcatServer = (TomcatServer) context.getBean("tomcatServer");
		tomcatServer.doBusiness("test params");
		System.out.println(tomcatServer.getClass());

打印出来的结果:

do something around method execute
do something before proxy method call
args name is test params
before aspect do something
call set name ,value : test params
do something after proxy method call
args name is test params
after aspect do something
params is test params
do something after return
class micro.test.spring.TomcatServer$$EnhancerBySpringCGLIB$$c6cb7663

最后一行可以看到spring采用了cglib的方式生成代理对象.

最后补充下maven配置:

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.version>4.1.7.RELEASE</spring.version>
		<slf4j.version>1.7.2</slf4j.version>
		<log4j.version>1.2.16</log4j.version>
		<junit.version>4.11</junit.version>
		<jsonlib.version>2.4</jsonlib.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<!-- spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.5.3</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.9</version>
		</dependency>

		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20160810</version>
		</dependency>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值