Spring学习笔记(三)-AOP技术(模拟实现利用java中的Proxy和CGlib)

1.利用JDK自带的Proxy和CGlib代理类模拟实现AOP功能

1.实现的业务是:

1).拦截所有的业务方法

2).判断用户是否有权限,有权限就允许它执行业务方法,没有权限就不允许它执行业务方法(是否有权限根据user是否为null来判断)

上面的这个就是AOP中所说的“横切性关注点”。

使用JDK中提供的Proxy(代理类)类实现上面一段话所讲的拦截。

编写代理类,用的是JDK中提供的Proxy,Proxy是面向接口的,如果想要创建代理的实例没有接口,那么不能用Proxy来创建代理。

2.java代码的实现

1).service接口:PersonService.java

2).serviceImpl:PersonServiceImpl.java

3).编写代理类,模拟AOP:JdkProxyFactory.java

package com.gaoyuan.aop;


import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;


import com.gaoyuan.service.impl.PersonServiceImpl;


/**

 * 编写代理类,用的是JDK中提供的Proxy,Proxy是面向接口的,如果想要创建代理的实例没有接口,那么不能用Proxy来创建代理。

 * @author G_yuan

 *

 */

public class JdkProxyFactory implements InvocationHandler {

private Object targetObject;

/**

 * 创建代理对象

 * @param targetObject

 * @return

 */

public Object createProxyIntance(Object targetObject){

this.targetObject = targetObject;

//创建一个代理类

Object object = Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),

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

return object;

}

/**

 * 创建的代理对象实现了InvocationHandler,所以有个回调对象,意思就是谁调用的代理对象,那么回调对象就是谁,

 * 要通过反射对这个回调对象中的方法进行一定业务的处理。

 */

@Override

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

throws Throwable {

PersonServiceImpl bean = (PersonServiceImpl) this.targetObject;

Object result = null;

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

result = method.invoke(targetObject, args); //此处是通过反射,动态的调用传入对象(targetObject)的方法,result的值为,对象方法的值返回是什么,result的值就是什么

}

return result;

}

}

4).测试代码

如果想要创建代理的类没有接口的话,那么用CGlib产生代理对象。(目标对象没有接口)

编码实现:

Service和serviceImpl的代码上面一样,唯一不一样的就是编写的代理类

CGlibProxyFactory.java

package com.gaoyuan.aop;


import java.lang.reflect.Method;


import com.gaoyuan.service.impl.PersonServiceImpl;


import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

/**

 * 如果想要创建代理的类没有接口的话,那么用CGlib产生代理对象。(目标对象没有接口)

 * @author G_yuan

 *

 */

public class CGlibProxyFactory implements MethodInterceptor {

private Object targetObject;

public Object createProxyIntance(Object targetObject){

this.targetObject = targetObject;

Enhancer enhancer = new Enhancer();//使用Enhancer类来创建代理类。

//Enhancer类实现代理的方式是:Enhancer继承了目标类(this.targetObject.getClass()),然后覆盖了目标类中的

//所有非final的方法,然后在覆盖的方法中添加了自己的一些代码。

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

enhancer.setCallback(this);

return enhancer.create();

}

/**

 * Object object:代理对象本身

 * Method method:被拦截的方法,在应用中调用那个方法,那就拦截那个方法,具体指看下面的注解

 * Object[] arg2:方法的参数

   MethodProxy methodProxy:方法代理对象

 */

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

MethodProxy methodProxy) throws Throwable {

PersonServiceImpl bean = (PersonServiceImpl) this.targetObject;

Object result = null;

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

result = methodProxy.invoke(targetObject, args); //此处是通过反射,动态的调用传入对象(targetObject)的方法,result的值为,对象方法的值返回是什么,result的值就是什么

}

MethodProxy methodProxy1 = methodProxy;//methodProxy net.sf.cglib.proxy.MethodProxy@7e41986c

Method me = method; //me = public java.lang.String com.gaoyuan.service.impl.PersonServiceImpl.getPersonName(java.lang.Integer)

return result;

}

}

3.AOP中通知的概念

横切性关注点:我们要对那些方法进行拦截,拦截之后我们要做些什么,这些思考的步骤都可以定义为横切性关注点。

切面:思考完横切性关注点之后,就要进行编码进行实现,那么对应上面写的代理类,JdkProxyFactory或者CGlibProxyFactory类,即可认为是切面。

连接点:对应的就是,在tset类,PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceImpl("888"));

//service.save("333");

service.getPersonName(1);

创建的service对象调用那个方法,那个方法就是连接点。

切入点:比如上面要实现的业务是,对所有的业务方法进行拦截,这就是一个切入点。

通知:

Target(目标对象):JdkProxyFactory中Proxy.newProxyInstance创建的对象

织入:serviceImpl本身没有对方法进行拦截的判断,但是通过编写了切面(代理类),然后它具备了这项功能,这个过程的实现就是织入。

引入:动态的在代理对象上添加一些字段或者是方法。

4.spring中AOP注解开发

上面是通过没有使用任何框架来实现的AOP,spring中集成上面的两种方法实现AOP,如果想要创建代理的类没有接口时,spring就会自动使用CGlib来实现AOP,如果有接口时就使用JDK中的Proxy。

当进行切面开发时如果用JDK是1.7的版本时,项目中引入的aspectjrt-1.7.4.jar和aspectjweaver-1.7.4.jar也应该是1.7版本的。

切面代码实现

1).PersonService.java

2).PersonServiceImpl.java

package com.gaoyuan.service.impl;


import org.springframework.stereotype.Service;


import com.gaoyuan.service.PersonService;

@Service("personService")//记的写

public class PersonServiceImpl implements PersonService  {

private String user = null;

public String getUser() {

return user;

}

public PersonServiceImpl() {

}

public PersonServiceImpl(String user) {

this.user = user;

}

@Override

public String getPersonName(Integer id){

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

return "xxxx";

}

@Override

public void save(String name){

throw new RuntimeException();

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

}

@Override

public void update(String name , Integer id ){

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

}

}

3).切面类:MyInterceptor.java


package com.gaoyuan.service;


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;

import org.springframework.stereotype.Component;


@Aspect

@Component

public class MyInterceptor {

/**

* 对"execution (* com.gaoyuan.service..*.*(..))"的解释

(!void com.gaoyuan.service..*.*(..) 拦截返回值不是void的方法

(* com.gaoyuan.service..*.*(java.lang.String,..) 拦截方法 

   中的参数第一个是String类型,后面随意参数的方法。

 * execution:表示执行

 * 第一个“ * ”:表示返回值类型,*代表所有的返回值类型,如果特殊需要写具体的类型时,写类型的全名意思就是加包名。

 * com.gaoyuan.service..:表示包名,com.gaoyuan.service包下面的以及它的子包后面的两个".."意思包含子包,可以直接写包的全名

 * 如果直接写包的全名的话,那就是只是拦截此包下类的方法。

 * 第二个“ * ”:表示:前面包的所有类,也是可以写全名的。写全名代码只拦截此包下此类的方法 。

 * 第二个*和第三个*之间的“.”是分割这两个星号的。

 * 第三个“*”:代表方法名 “*”代表所有的方法。

 * 第三个“*”后的(..):代表参数。

 */

@Pointcut("execution (* com.gaoyuan.service..*.*(..))")

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

//如果想获取调用方法的参数时,配置args(name) 括号中的name要和doAccessCheck(String name)中参数的名字一致。

@Before("anyMethod() && args(name)")

public void doAccessCheck(String name){

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

}

//如果想获取调用方法后的返回值,配置returning="result" 括号中的result要和doAfterReturning(String result)中参数的名字一致。

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

public void doAfterReturning(String result){

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

}

@After("anyMethod()")

public void doAfter(){

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

}

//如果想获取方法中抛出的异常用throwing="e"

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

public void doAfterThrowing(Exception e){

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

}

@Around("anyMethod()")

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{

//如果定义了环绕通知后,必须调用proceed()方法,如果不调用的话,后面调用业务层的方法是不会执行的。所以,

//此处特别适合做权限判断

//if(){Object result = pjp.proceed();}

System.out.println("进入方法");

Object result = pjp.proceed();

System.out.println("退出方法 ");

return result;

}

}

4.测试类:

5.基于XML配置方式声明切面

对应的java类

你可能感兴趣的:(Spring学习笔记(三)-AOP技术(模拟实现利用java中的Proxy和CGlib))