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类

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Java设计模式——代理模式 代理模式主要分为接口,委托类,代理类 接口:规定具体方法委托类:实现接口,完成具体的...
    vczyh阅读 651评论 0 0
  • https://blog.csdn.net/luanlouis/article/details/24589193 ...
    小陈阿飞阅读 848评论 1 1
  • 文/乘风律次方 影片为多年前的影片,同样,文章也是多年前的文章。但是影片的内容,文章的思想并不会因为时间的过逝而过...
    6e0781d5a6dd阅读 510评论 0 1
  • 高挂的灯笼轰响的鼓 猩红绸缎缠绕通天的漆柱 糜烂的血肉里 迂腐揉进骨子眼儿 华美的嫁裳精致的妆 洞房里的火烛轻轻摇...
    卫卫卫杞啊阅读 244评论 0 0
  • https://www.cnblogs.com/chris-oil/p/9142795.html
    fb941c99409d阅读 100评论 0 0