先用jdk,cglib模拟下:
使用JDK动态代理
//当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
package cn.zyj15.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.zyj15.service.impl.PersonServiceBean; /** * * @author Administrator *实现InvocationHandler接口的即为代理,记实现invoke方法 */ public class JDKProxyFactory implements InvocationHandler { private Object targetObject; public Object createProxyIntance(Object targetObject){ this.targetObject = targetObject; //从newProxyInstance方法看出用jdk实现代理必须实现接口 /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法 */ 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) this.targetObject; Object result = null; if(bean.getUser()!=null){ //..... advice()-->前置通知 try { result = method.invoke(targetObject, args);//把方法调用委派给目标对象 // afteradvice() -->后置通知 } catch (RuntimeException e) { //exceptionadvice()--> 例外通知 }finally{ //finallyadvice(); -->最终通知 } }else { System.out.println("没有权限!"); } return result; } }
package junit.test15; import static org.junit.Assert.*; import org.junit.Test; import cn.zyj15.aop.JDKProxyFactory; import cn.zyj15.service.PersonService; import cn.zyj15.service.impl.PersonServiceBean; public class AOPTest { @Test public void jdktest() { JDKProxyFactory factory = new JDKProxyFactory(); //必须用接口 PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean(null)); service.save("888"); } }
使用CGLIB生成代理
//CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
package cn.zyj16.aop; import java.lang.reflect.Method; import cn.zyj16.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());//设置代理类的父类。CGLIB可以生成目标类的子类,并重写父类非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.test16; import static org.junit.Assert.*; import org.junit.Test; import cn.zyj16.service.PersonService; import cn.zyj16.service.impl.PersonServiceBean; import cn.zyj16.aop.CGlibProxyFactory; public class AOPTest { @Test public void cglibTest2(){ CGlibProxyFactory factory = new CGlibProxyFactory(); //此事创建的代理对象是PersonServiceBean的子类! PersonServiceBean service = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("xxx")); service.save("999"); } }
AOP中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
使用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>
Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:
基于XML配置方式声明切面。
基于注解方式声明切面。
首先启动对@AspectJ注解的支持(蓝色部分):
<?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/>
<bean id="myInterceptor" class="cn.zyj17.service.MyInterceptor"/>
<bean id="personService" class="cn.zyj17.service.impl.PersonServiceBean"></bean>
</beans>
package cn.zyj17.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; /** * 切面 * */ @Aspect public class MyInterceptor { @Pointcut("execution (* cn.zyj17.service.impl.PersonServiceBean.*(..))") private void anyMethod() {}//声明一个切入点 @Before("anyMethod() && args(name)") public void doAccessCheck(String name) { System.out.println("前置通知:"+ name); } @AfterReturning(pointcut="anyMethod()",returning="result") public void doAfterReturning(String result) { System.out.println("后置通知:"+ result); } @After("anyMethod()") public void doAfter() { System.out.println("最终通知"); } @AfterThrowing(pointcut="anyMethod()",throwing="e") public void doAfterThrowing(Exception e) { System.out.println("例外通知:"+ e); } @Around("anyMethod()") public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { //if(){//判断用户是否在权限 System.out.println("进入方法"); Object result = pjp.proceed(); System.out.println("退出方法"); //} return result; } }
package junit.test17; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.zyj17.service.PersonService; public class SpringAOPTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void interceptorTest(){ ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); PersonService personService = (PersonService)cxt.getBean("personService");//接口 personService.save("xx"); } }
基于基于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/> <bean id="personService" class="cn.zyj19.service.impl.PersonServiceBean"></bean> <bean id="aspetbean" class="cn.zyj19.service.MyInterceptor"/> <aop:config> <aop:aspect id="asp" ref="aspetbean"> <aop:pointcut id="mycut" expression=" execution(* cn.zyj19.service..*.*(..))"/> <aop:before pointcut-ref="mycut" method="doAccessCheck"/> <aop:after-returning pointcut-ref="mycut" method="doAfterReturning"/> <aop:after-throwing pointcut-ref="mycut" method="doAfterThrowing"/> <aop:after pointcut-ref="mycut" method="doAfter"/> <aop:around pointcut-ref="mycut" method="doBasicProfiling"/> </aop:aspect> </aop:config> </beans>
package cn.zyj19.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; /** * 切面 * */ public class MyInterceptor { public void doAccessCheck() { System.out.println("前置通知"); } public void doAfterReturning() { System.out.println("后置通知"); } public void doAfter() { System.out.println("最终通知"); } public void doAfterThrowing() { System.out.println("例外通知"); } public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入方法"); Object result = pjp.proceed(); System.out.println("退出方法"); return result; } }
package junit.test19; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.zyj19.service.PersonService; public class SpringAOPTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void interceptorTest(){ ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); PersonService personService = (PersonService)cxt.getBean("personService"); personService.save("xx"); } }