需求:
1、拦截所有业务方法
2、判断用户是否有权限,有权限就让他执行业务方法,没有权限就不允许执行。(是否有权限是根据user是否为null作为判断依据)
思考:
我们该如何实现?
思路1:
我们在每个业务方法上面加上判断语句。
否决掉了。代码过多,不灵活如果我需要更改需求就要挂掉。
解决方式:
使用动态代理实现。
Proxy动态代理。要求:被代理的对象需要有父接口。
准备代码:
package cn.itcast.service; public interface PersonService { public abstract String getPersonName(Integer personId); public abstract void save(String name); public abstract void update(String name,Integer personId); } package cn.itcast.service.impl; import cn.itcast.service.PersonService; public class PersonServiceBean implements PersonService{ private String user=null; public PersonServiceBean(){} public String getUser() { return user; } 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()方法"); } }
Proxy代理:
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 createProxyInstance(Object targetObject){ this.targetObject=targetObject; return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//环绕通知 PersonServiceBean bean=(PersonServiceBean) targetObject; Object result=null; if(bean!=null && bean.getUser()!=null){ try{ //...advice()-->前置通知 result=method.invoke(targetObject, args); //...afteradvice()-->后置通知 }catch(RuntimeException e){ //exceptionadvice()-->例外通知 }finally{ //finallyadvice()-->最终通知 } } return result; } }
如果操作的目标对象没有父接口,应该怎么做呢?
使用cglib.jar生成代理对象
原理:
生成的代理对象继承自原对象,重写了原对象的除了final外方法
导入jar:
cglib-nodep-2.1_3.jar
主要代码:
package cn.itcast.aop; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import cn.itcast.service.impl.PersonServiceBean; public class CGlibProxyFactory implements MethodInterceptor{ private Object targetObject; public Object createProxyInstance(Object targetObject){ this.targetObject=targetObject; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass());//继承了目标类,对目标类里面所有非final方法进行和覆盖,在覆盖的方法内添加自身代码 enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { PersonServiceBean bean=(PersonServiceBean) targetObject; Object result=null; if(bean!=null && bean.getUser()!=null){ result=method.invoke(targetObject, args); } return result; } }
spring的AOP操作的内部原理就是使用的上面两种动态代理方式。
现在讲讲AOP的一些概念:
横切性关注点:
前面我们思考的拦截对象拦截方式等叫做横切性关注点。
对什么进行拦截,拦截后应该做什么,这些思考的步骤,我们可以定义为横切性关注点。
aspect(切面):
指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类时对物体特征的抽象,而切面是横切性关注点的抽象。
joinpoint(连接点):
所谓连接点,就是指那些被拦截到的方法。在上面的例子中,练接点是绝大部分方法(非native),在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或者类构造器
pointcut(切入点):
所谓切入点,就是指的我们需要拦截的连接点的定义,在上面的例子中指的是业务方法
advice(通知):拦截到连接点之后需要做的事
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//环绕通知 PersonServiceBean bean=(PersonServiceBean) targetObject; Object result=null; if(bean!=null && bean.getUser()!=null){ try{ //...advice()-->前置通知 result=method.invoke(targetObject, args); //...afteradvice()-->后置通知 }catch(RuntimeException e){ //exceptionadvice()-->例外通知 }finally{ //finallyadvice()-->最终通知 } } return result; }
target(目标对象):
代理的目标对象
weave(织入):
将aspects应用到target对象并导致proxy对象创建的过程称为织入
introduction(引入):
在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或者Field