AOP概念
不讲废话,面向切面就比如说有很多个业务逻辑代码,如果你要修改代码,在代码实现前后增加一条逻辑,比如要判断后才执行代码,你总不能一条条去改各个类的代码。
所以切面就是说执行一个方法,这个方法变为一个切入点来配置,你可以定义这个切入点,你想在执行这个方法之前增加逻辑或者在它之后增加逻辑,都可自行配置。
原理(Proxy&CGlib)
Proxy实现(JDK的AOP,不是框架里的)
定义一个代理工厂拦截了业务bean,先判断。
package com.yiki.service; public interface PersonSercvice { public void save(String name); public void update(String name,Integer pid); public String getPname(Integer pid); }
package com.yiki.bean; import com.yiki.service.PersonSercvice; public class Person implements PersonSercvice{ private String user = null; public Person() { } public Person(String user) { this.user=user; } public String getUser() { return user; } @Override public void save(String name) { System.out.println("save'"); } @Override public void update(String name, Integer pid) { System.out.println("update"); } @Override public String getPname(Integer pid) { // TODO Auto-generated method stub return "getname"; } }
package com.yiki.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.aopalliance.intercept.Invocation; import com.yiki.bean.Person; public class JDKProxyFactory implements InvocationHandler { private Object target; public Object createProxyInstance(Object target) { this.target = target; Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this); return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Person bean = (Person) this.target; Object result = null; if (bean.getUser() != null) { result = method.invoke(target, args); } return result; } }
package com.yiki.test; import org.junit.Test; import com.yiki.aop.JDKProxyFactory; import com.yiki.bean.Person; import com.yiki.service.PersonSercvice; public class aoptest { @Test public void testProxy(){ JDKProxyFactory proxy = new JDKProxyFactory(); PersonSercvice service = (PersonSercvice) proxy.createProxyInstance(new Person("yiki")); service.save("888"); //PersonSercvice service = (PersonSercvice) proxy.createProxyInstance(new Person());没有输出 } }
Spring cglib实现
package com.yiki.aop; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import com.yiki.bean.People; import com.yiki.bean.Person; public class cglibFactory implements MethodInterceptor { private Object target; public Object createProxyInstance(Object target) { this.target=target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy methodproxy) throws Throwable { People bean = (People) this.target; Object result = null; if (bean.getUser() != null) { result = methodproxy.invoke(target, args); } return result; } }
package com.yiki.bean; public class People {//不需要实现接口 private String user; public People() { } public People(String user) { this.user = user; } public String getUser() { return user; } public void save(String name) { System.out.println("save'"); } }
@Test public void testcglib(){ cglibFactory cglib = new cglibFactory(); People people = (People) cglib.createProxyInstance(new People("yiki"));//为空则不输出 people.save("000"); }
框架实现
Spring AOP注解实现(真正的Spring框架实现)
这里只涉及最普通的注解,具体切面的匹配(就是@PointCut(匹配规则))以及一些需要传入参数(&&args)的情况要另算……(太多了不知道怎么写- -。)
首先这里要先把配置文件自动扫描给加上。
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <context:annotation-config /> <!-- 自动扫描所有的bean --> <context:component-scan base-package="com.yiki.bean" /> <!-- 声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面 --> <aop:aspectj-autoproxy /> </beans>
这里我已经不会在配置文件写bean了,而是用@Compenent
package com.yiki.bean; import org.springframework.stereotype.Component; @Component public class Student { public void student(){ System.out.println("student"); } }
切面类
package com.yiki.bean; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; 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 aspect { @Pointcut("execution (* com.yiki.bean.Student.*(..))")//匹配Student下的所有方法 private void anyMethod() { }// 空方法,是一个切入点 @Before("anyMethod()") public void before() { System.out.println("before前置通知"); } @AfterReturning("anyMethod()") public void after() { System.out.println("afterReturning后置通知"); } @After("anyMethod()") public void doAfter(){ System.out.println("after最终通知"); } @Around("anyMethod()")//适合权限控制,万能~~~~~ public Object around(ProceedingJoinPoint pjp) throws Throwable{ Object result = pjp.proceed(); try { //=AfterReturning } catch (Exception e) { // =before }finally { //后置通知 } System.out.println("around环绕通知"); return result;//最终 } }
测试
package com.yiki.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.yiki.bean.Student; public class tests { public static void main(String[] args) { String xmlPath = "ApplicationContext.xml"; ApplicationContext cfg = new ClassPathXmlApplicationContext(xmlPath); Student stu = (Student) cfg.getBean("student"); stu.student(); } }
基于xml配置文件的AOP(只支持单例模式)
遵循以下原则:
<bean id="student" class="com.yiki.bean.Student"></bean> <bean id="aspect" class="com.yiki.bean.aspect"></bean> <!-- 把student作为一个切面声明,这个切面的名字是aspectAOP 【expression】里写需要匹配的方法,*后面有空格 --> <aop:config> <aop:aspect id="aspectAOP" ref="aspect"> <aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))" /> </aop:aspect> </aop:config>
对于切入点PointCut:execution(这里自己查官方吧,很多匹配规则)
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <!-- bean definition & AOP specific configuration --> <bean id="student" class="com.yiki.bean.Student"></bean> <bean id="aspect" class="com.yiki.bean.aspect"></bean> <!-- 把aspect作为一个切面声明,这个切面的名字是aspectAOP 切入点是student里的所有方法,==》【expression】里写需要匹配的方法,*后面有空格 --> <aop:config> <aop:aspect id="aspectAOP" ref="aspect"> <aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))" /> <!-- advice通知配置 --> <aop:before method="before" pointcut-ref="doOne"/> <aop:after-returning method="after" pointcut-ref="doOne"/> <aop:around method="around" pointcut-ref="doOne"/> </aop:aspect> </aop:config> </beans>
package com.yiki.bean; import org.aspectj.lang.ProceedingJoinPoint; public class aspect { public void show(){ System.out.println("aspect"); } public void before() { System.out.println("before前置通知"); } public void after() { System.out.println("afterReturning后置通知"); } public void doAfter(){ System.out.println("after最终通知"); } public Object around(ProceedingJoinPoint pjp) throws Throwable{ Object result = pjp.proceed(); try { //=AfterReturning System.out.println("之后Runing环绕通知"); } catch (Exception e) { // =before System.out.println("之前环绕通知"); }finally { //后置通知 System.out.println("之后环绕通知"); } System.out.println("around环绕通知"); return result;//最终 } }