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;//最终
}
}