框架学习之Spring 第三节 采用Spring实现AOP功能

本文内容纲要:框架学习之Spring 第三节 采用Spring实现AOP功能

1.AOP中的概念

Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.

joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)

Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.

Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知

Target(目标对象):代理的目标对象

Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.

Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

代理方式:

静态代理

动态代理

2.使用JDK的proxy技术实现AOP功能

JDK 动态代理 (采用的是JDK 反射技术)

public class JDKProxy implements InvocationHandler {

private Object targetObject;//代理的目标对象

public Object createProxyInstance(Object targetObject){

this.targetObject = targetObject;

/*

* 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器

* 第二个参数设置代理类实现的接口

* 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法

*/

return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),

this.targetObject.getClass().getInterfaces(), this);

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

return method.invoke(this.targetObject, args);//把方法调用委派给目标对象

}

}

当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。

3.使用CGLIB实现AOP功能

public class CGLIBProxy implements MethodInterceptor {

private Object targetObject;//代理的目标对象

public Object createProxyInstance(Object targetObject){

this.targetObject = targetObject;

Enhancer enhancer = new Enhancer();//该类用于生成代理对象

enhancer.setSuperclass(this.targetObject.getClass());//设置父类

enhancer.setCallback(this);//设置回调用对象为本身

return enhancer.create();

}

public Object intercept(Object proxy, Method method, Object[] args,

MethodProxy methodProxy) throws Throwable {

return methodProxy.invoke(this.targetObject, args);

}

}

CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。

注:使用CGLIB之前要导入相应的jar包,在下载的spring文件包的cglib目录下

测试:

新建一个接口:

package cn.itcast.service;

public interface PersonService {

public void save(String name);

public void update(String name, Integer personid);

public String getPersonName(Integer personid);

}

新建一个接口的 实现类:

代码的目的是,在下面这个业务逻辑类中的方法执行之前都要被容器拦截,拦截了之后进行权限的判断,如果有权限才可以执行类中的方法

本例中的有权限是指 user!=null

package cn.itcast.service.impl;

import cn.itcast.service.PersonService;

public class PersonServiceBean implements PersonService{

private String user = null;

public String getUser() {

return user;

}

public PersonServiceBean(){}

public PersonServiceBean(String user){

this.user = user;

}

public String getPersonName(Integer personid) {

System.out.println("我是getPersonName()方法");

return "xxx";

}

public void save(String name) {

System.out.println("我是save()方法");

}

public void update(String name, Integer personid) {

System.out.println("我是update()方法");

}

}

使用JDK完成动态代理:

package cn.itcast.aop;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import cn.itcast.service.impl.PersonServiceBean;

public class JDKProxyFactory implements InvocationHandler{

private Object targetObject;

public Object createProxyIntance(Object targetObject){

this.targetObject = targetObject;

return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),

this.targetObject.getClass().getInterfaces(), this);

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {//环绕通知

PersonServiceBean bean = (PersonServiceBean) this.targetObject;

Object result = null;

if(bean.getUser()!=null){

//..... advice()-->前置通知

try {

result = method.invoke(targetObject, args);

// afteradvice() -->后置通知

} catch (RuntimeException e) {

//exceptionadvice()--> 例外通知

}finally{

//finallyadvice(); -->最终通知

}

}

return result;

}

}

使用CGLIB实现AOP功能:

package cn.itcast.aop;

import java.lang.reflect.Method;

import cn.itcast.service.impl.PersonServiceBean;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class CGlibProxyFactory implements MethodInterceptor{

private Object targetObject;

public Object createProxyIntance(Object targetObject){

this.targetObject = targetObject;

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(this.targetObject.getClass());//非final

enhancer.setCallback(this);

return enhancer.create();

}

public Object intercept(Object proxy, Method method, Object[] args,

MethodProxy methodProxy) throws Throwable {

PersonServiceBean bean = (PersonServiceBean) this.targetObject;

Object result = null;

if(bean.getUser()!=null){

result = methodProxy.invoke(targetObject, args);

}

return result;

}

}

新建一个单元测试:

package junit.test;

import org.junit.BeforeClass;

import org.junit.Test;

import cn.itcast.aop.CGlibProxyFactory;

import cn.itcast.aop.JDKProxyFactory;

import cn.itcast.service.PersonService;

import cn.itcast.service.impl.PersonServiceBean;

public class AOPTest {

@BeforeClass

public static void setUpBeforeClass() throws Exception {

}

@Test public void proxyTest(){

JDKProxyFactory factory = new JDKProxyFactory();

PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("xxx"));

service.save("888");

}

@Test public void proxyTest2(){

CGlibProxyFactory factory = new CGlibProxyFactory();

PersonServiceBean service = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("xxx"));

service.save("999");

}

}

两个测试结果都是:

我是save()方法

4.使用Spring框架实现AOP功能

要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:

<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-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

</beans>

注意上面的 aop 命名空间 和 最后一行的两个schema文件路径

Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:

(1)基于注解方式声明切面。

(2)基于XML配置方式声明切面。

<1> 基于注解方式声明切面。

首先启动对@AspectJ注解的支持(下划线部分):

<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-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<aop:aspectj-autoproxy/>

<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>

<bean id="log" class="cn.itcast.service.LogPrint"/>

</beans>

启用@AspectJ支持后,在application context中定义的任意带有一个@Aspect切面(拥有@Aspect注解)的bean都将被

Spring自动识别并用于配置Spring AOP。

注解方式的示例:

@Aspect

public class LogPrint {

@Pointcut("execution(* cn.itcast.service..*.*(..))")

private void anyMethod() {}//声明一个切入点

@Before("anyMethod() && args(userName)")//定义前置通知

public void doAccessCheck(String userName) {

}

@AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知

public void doReturnCheck(String revalue) {

}

@AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知

public void doExceptionAction(Exception ex) {

}

@After("anyMethod()")//定义最终通知

public void doReleaseAction() {

}

@Around("anyMethod()")//环绕通知

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

return pjp.proceed();

}

}

<2>基于XML配置方式声明切面。

各种不同类型的通知

public class LogPrint {

public void doAccessCheck() {}定义前置通知

public void doReturnCheck() {}定义后置通知

public void doExceptionAction() {}定义例外通知

public void doReleaseAction() {}定义最终通知

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

return pjp.proceed();环绕通知

}

}

XML配置:

<bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>

<bean id="log" class="cn.itcast.service.LogPrint"/>

<aop:config>

<aop:aspect id="myaop" ref="log">

<aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>

<aop:before pointcut-ref="mycut" method="doAccessCheck"/>

<aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/>

<aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>

<aop:after pointcut-ref="mycut" method=“doReleaseAction"/>

<aop:around pointcut-ref="mycut" method="doBasicProfiling"/>

</aop:aspect>

</aop:config>

测试:

重新建立一个接口:

package com.yinger.service;

public interface PersonService2 {

public void save();

public void exTest() throws Exception;

public String test(String name);

}

新建一个类,实现上面的接口:

package com.yinger.service.impl;

import com.yinger.service.PersonService2;

public class PersonServiceBean4 implements PersonService2{

//默认的构造器

public PersonServiceBean4(){

System.out.println("instance me");

}

//exTest 方法

public void exTest() throws Exception {

throw new Exception("发生了异常");

}

//test 测试方法

public String test(String name){

System.out.println("test");

return name;

}

//save 方法

public void save(){

System.out.println("save");

}

}

新建一个拦截器:

package com.yinger.service.intercepter;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.AfterThrowing;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

@Aspect

public class PersonServiceIntercepter {

@Pointcut("execution(* com.yinger.service.impl.PersonServiceBean4.*(..))")

private void anyMethod(){}

@Before("anyMethod()")

public void doAccessCheck(){

System.out.println("前置通知");

}

@AfterReturning(pointcut="anyMethod()",returning="reValue")

public void doReturnCheck(String reValue){

System.out.println("后置通知:"+reValue);

}

@AfterThrowing(pointcut="anyMethod()",throwing="ex")

public void doExceptionAction(Exception ex) {

System.out.println("例外通知:"+ex);

}

@After("anyMethod()")

public void doReleaseAction(){

System.out.println("最终通知");

}

@Around("anyMethod()")

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

System.out.println("环绕通知");

return pjp.proceed();

}

}

beans.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: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-2.5.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<aop:aspectj-autoproxy/>

<!-- 注意记得要将自定义的拦截器交给Spring容器管理 -->

<bean id="myIntercepter" class="com.yinger.service.intercepter.PersonServiceIntercepter"></bean>

<bean id="personService4" class="com.yinger.service.impl.PersonServiceBean4"></bean>

</beans>

添加测试方法:

@Test  //用于测试AOP功能的方法

public void testAOP() throws Exception {

AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

System.out.println("--------");

PersonService2 ps2 = (PersonService2)ctx.getBean("personService4");

ps2.test("name");

// ps2.exTest();

// ps2.save();

ctx.close();

}

首先测试 test 方法,结果:

log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).

log4j:WARN Please initialize the log4j system properly.

instance me

--------

前置通知

环绕通知

test

后置通知:name

最终通知

其次测试 save 方法:

log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).

log4j:WARN Please initialize the log4j system properly.

instance me

--------

前置通知

环绕通知

save

后置通知:null

最终通知

最后测试 exTest 方法:

log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).

log4j:WARN Please initialize the log4j system properly.

instance me

--------

前置通知

环绕通知

例外通知:java.lang.Exception: 发生了异常

最终通知

从上面的例子中可以看出,使用通知可以得到方法返回的值,当发生了异常时可以知道异常的类型和信息

并且通过给通知方法限制方法参数的类型,可以限制为指定的方法参数的方法

如果是异常的话,就只有抛出的异常和例外通知中的异常一样时,异常通知方法才会执行

参考文档中的内容:

1011

基于xml配置的例子略过。。。(和上面的示例代码差不多)

5.aspectj的切入点的语法细节分析

expression="execution(* cn.itcast.service..*.*(..))"

第一个*表示方法的返回值可以是任何值

接着 cn.itcast.service..* 表示 cn.itcast.service 包以及它的子包(两个点,如果是一个点就不包括子包)中的任何类 ,第二个*表示任何类

接着 .* 表示类中的任何方法,第三个*表示任何方法

最后的 (..)表示方法的参数可以是任意的,可以没有,也可以有一个或者多个

本文内容总结:框架学习之Spring 第三节 采用Spring实现AOP功能

原文链接:https://www.cnblogs.com/yinger/archive/2011/08/23/2150627.html

以上是 框架学习之Spring 第三节 采用Spring实现AOP功能 的全部内容, 来源链接: utcz.com/z/296664.html

回到顶部