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类